|
|
import { chevrotain } from '../../../lib.js'; |
|
|
import { MacroLexer } from './MacroLexer.js'; |
|
|
|
|
|
const { CstParser } = chevrotain; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let instance; |
|
|
export { instance as MacroParser }; |
|
|
|
|
|
class MacroParser extends CstParser { |
|
|
static #instance; |
|
|
static get instance() { return MacroParser.#instance ?? (MacroParser.#instance = new MacroParser()); } |
|
|
|
|
|
|
|
|
constructor() { |
|
|
super(MacroLexer.def, { |
|
|
traceInitPerf: false, |
|
|
nodeLocationTracking: 'full', |
|
|
recoveryEnabled: true, |
|
|
}); |
|
|
const Tokens = MacroLexer.tokens; |
|
|
|
|
|
const $ = this; |
|
|
|
|
|
|
|
|
$.document = $.RULE('document', () => { |
|
|
$.MANY(() => { |
|
|
$.OR([ |
|
|
{ ALT: () => $.CONSUME(Tokens.Plaintext, { LABEL: 'plaintext' }) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.PlaintextOpenBrace, { LABEL: 'plaintext' }) }, |
|
|
{ ALT: () => $.SUBRULE($.macro) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Macro.Start, { LABEL: 'plaintext' }) }, |
|
|
]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
$.macro = $.RULE('macro', () => { |
|
|
$.CONSUME(Tokens.Macro.Start); |
|
|
$.OR([ |
|
|
{ ALT: () => $.CONSUME(Tokens.Macro.DoubleSlash, { LABEL: 'Macro.identifier' }) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Macro.Identifier, { LABEL: 'Macro.identifier' }) }, |
|
|
]); |
|
|
$.OPTION(() => $.SUBRULE($.arguments)); |
|
|
$.CONSUME(Tokens.Macro.End); |
|
|
}); |
|
|
|
|
|
|
|
|
$.arguments = $.RULE('arguments', () => { |
|
|
$.OR([ |
|
|
{ |
|
|
ALT: () => { |
|
|
$.CONSUME(Tokens.Args.DoubleColon, { LABEL: 'separator' }); |
|
|
$.AT_LEAST_ONE_SEP({ |
|
|
SEP: Tokens.Args.DoubleColon, |
|
|
DEF: () => $.SUBRULE($.argument, { LABEL: 'argument' }), |
|
|
}); |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
ALT: () => { |
|
|
$.OPTION(() => { |
|
|
$.CONSUME(Tokens.Args.Colon, { LABEL: 'separator' }); |
|
|
}); |
|
|
$.SUBRULE($.argumentAllowingColons, { LABEL: 'argument' }); |
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
IGNORE_AMBIGUITIES: true, |
|
|
}, |
|
|
]); |
|
|
}); |
|
|
|
|
|
|
|
|
const validArgumentTokens = [ |
|
|
{ ALT: () => $.SUBRULE($.macro) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Identifier) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Unknown) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Args.Colon) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Args.Equals) }, |
|
|
{ ALT: () => $.CONSUME(Tokens.Args.Quote) }, |
|
|
]; |
|
|
|
|
|
$.argument = $.RULE('argument', () => { |
|
|
$.MANY(() => { |
|
|
$.OR([...validArgumentTokens]); |
|
|
}); |
|
|
}); |
|
|
$.argumentAllowingColons = $.RULE('argumentAllowingColons', () => { |
|
|
$.AT_LEAST_ONE(() => { |
|
|
$.OR([ |
|
|
...validArgumentTokens, |
|
|
{ ALT: () => $.CONSUME(Tokens.Args.DoubleColon) }, |
|
|
]); |
|
|
}); |
|
|
}); |
|
|
|
|
|
this.performSelfAnalysis(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parseDocument(input) { |
|
|
if (!input) { |
|
|
return { cst: null, errors: [{ message: 'Input is empty' }], lexingErrors: [], parserErrors: [] }; |
|
|
} |
|
|
|
|
|
const lexingResult = MacroLexer.tokenize(input); |
|
|
|
|
|
this.input = lexingResult.tokens; |
|
|
const cst = this.document(); |
|
|
|
|
|
const errors = [ |
|
|
...lexingResult.errors, |
|
|
...this.errors, |
|
|
]; |
|
|
|
|
|
return { cst, errors, lexingErrors: lexingResult.errors, parserErrors: this.errors }; |
|
|
} |
|
|
|
|
|
test(input) { |
|
|
const lexingResult = MacroLexer.tokenize(input); |
|
|
|
|
|
this.input = lexingResult.tokens; |
|
|
const cst = this.macro(); |
|
|
|
|
|
|
|
|
|
|
|
const errors = this.errors.map(x => ({ message: x.message, ...x, stack: x.stack })); |
|
|
|
|
|
return { cst, errors: errors }; |
|
|
} |
|
|
} |
|
|
|
|
|
instance = MacroParser.instance; |
|
|
|