UNPKG

@making-sense/antlr-editor

Version:
401 lines 15.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GrammarStatement = void 0; const log_1 = require("../utils/log"); const multiplyMode_1 = require("./multiplyMode"); const ruleTokenizer_1 = require("./ruleTokenizer"); const statementType_1 = require("./statementType"); const syntaxLink_1 = require("./syntaxLink"); const tokenType_1 = require("./tokenType"); class GrammarStatement { constructor(type, tokens, name) { this.type = statementType_1.StatementType.Unknown; this.alternatives = false; this.unresolved = false; this.multiplied = multiplyMode_1.MultiplyMode.None; this.greedy = false; this.statements = []; this.tokens = []; this.syntax = []; this.type = type; this.tokens = tokens; if (name) this._name = name; } processRule() { if (this.tokens.length === 0 || this.statements.length !== 0 || this.token) return; const tokens = this.tokens; // ruleBlock and ruleAltList Antlr4 rule if (tokens.length === 1) { this.processToken(tokens[0]); return; } const statements = ruleTokenizer_1.RuleTokenizer.alternatives(tokens); if (statements.length === 1) { this.processRuleAlternative(statements[0]); } else if (statements.length > 1) { this.alternatives = true; statements.forEach(statement => this.addRuleAlternative(statement)); } } addRuleAlternative(tokens) { if (tokens.length === 0) return; const statement = new GrammarStatement(statementType_1.StatementType.Block, tokens); this.statements.push(statement); statement.processRuleAlternative(tokens); } processRuleAlternative(tokens) { if (tokens.length === 0) return; // labeledAlt Antlr4 rule if (tokens.length === 1) { this.processToken(tokens[0]); return; } const hashIndex = tokens.findIndex(token => token.type === tokenType_1.TokenType.Hash); if (hashIndex > -1) { this._label = tokens .slice(hashIndex + 1) .map(token => token.name) .join(""); tokens = tokens.slice(0, hashIndex); } this.processElements(tokens); } processElements(tokens) { if (tokens.length === 0) return; // alternative Antlr4 rule if (tokens.length === 1) { this.processToken(tokens[0]); return; } let i = 0; const length = tokens.length; while (i < tokens.length) { const token = tokens[i]; switch (token.type) { case tokenType_1.TokenType.Identifier: { // labeledElement Antlr4 rule if (i + 2 < length) { const assign = tokens[++i]; const element = tokens[++i]; if (assign.isAssign()) { if (element.type === tokenType_1.TokenType.Lparen && element.sibling) { element.label(token.name); const index = tokens.indexOf(element.sibling); if (index > i) { this.addBlock(tokens.slice(i, index + 1)); i = index; } break; } else if (element.isAtom()) { element.label(token.name); this.addAtom(element); break; } } } log_1.Log.error("Missing or mismatched token(s) after identifier", "GrammarStatement"); break; } case tokenType_1.TokenType.Lparen: { // ebnf Antlr4 rule if (token.sibling) { const index = tokens.indexOf(token.sibling); if (index > i) { this.addBlock(tokens.slice(i, index + 1)); i = index; } } else { log_1.Log.error("Missing right parenthesis token", "GrammarStatement"); } break; } default: if (token.isAtom()) { // atom Antlr4 rule this.addAtom(token); } else { log_1.Log.warn("Unexpected token " + token.name + " of type " + token.type, "GrammarStatement"); } } i++; } } addAtom(token) { const statement = new GrammarStatement(statementType_1.StatementType.Unknown, [token]); this.statements.push(statement); statement.processToken(token); } processToken(token) { this.token = token; this._name = token.name; this._value = token.value; this.multiply(token); switch (token.type) { case tokenType_1.TokenType.Rule: this.type = statementType_1.StatementType.Rule; if (!this._label) this._label = token.identifier; this.unresolved = true; break; case tokenType_1.TokenType.Keyword: this.type = statementType_1.StatementType.Keyword; this._label = token.identifier; this.unresolved = true; break; case tokenType_1.TokenType.Operator: this.type = statementType_1.StatementType.Operator; this._label = token.identifier; this.unresolved = true; break; case tokenType_1.TokenType.Operand: this.type = statementType_1.StatementType.Operand; this._label = token.identifier; this.unresolved = true; break; default: log_1.Log.warn("Unexpected token " + token.name + " of type " + token.type, "GrammarStatement"); } } addBlock(tokens) { if (tokens.length === 0) return; const statement = new GrammarStatement(statementType_1.StatementType.Block, tokens); this.statements.push(statement); statement.processBlock(tokens); } processBlock(tokens) { const left = tokens[0]; const right = tokens[tokens.length - 1]; if (left.type === tokenType_1.TokenType.Lparen && right.type === tokenType_1.TokenType.Rparen && left.sibling === right) { this.multiply(right); this._label = left.identifier; tokens = ruleTokenizer_1.RuleTokenizer.unnest(tokens); // altList Antldir4 rule const statements = ruleTokenizer_1.RuleTokenizer.alternatives(tokens); if (statements.length === 1) { this.processElements(statements[0]); } else if (statements.length > 1) { this.alternatives = true; statements.forEach(statement => this.addAlternative(statement)); } } else { log_1.Log.error("Missing parenthesis tokens in block", "GrammarStatement"); } } addAlternative(tokens) { if (tokens.length === 0) return; const statement = new GrammarStatement(statementType_1.StatementType.Block, tokens); this.statements.push(statement); statement.processElements(tokens); } resolveStatements(rules, operators, visited) { if (this.name) { if (visited.has(this.name)) return; visited.set(this.name, this); } this.statements.forEach((statement, index, statements) => { if (statement.unresolved && statement.name) { if (statement.isToken([tokenType_1.TokenType.Rule])) { const rule = rules.get(statement.name); if (rule) { if (rule !== statement) { if (!rule.label && !!statement.label) rule.label = statement.label; statements[index] = rule; } rule.resolveStatements(rules, operators, visited); } else { log_1.Log.warn("Unknown rule in graph " + statement.name, "GrammarStatement"); } } else if (statement.isToken([tokenType_1.TokenType.Keyword, tokenType_1.TokenType.Operator, tokenType_1.TokenType.Operand])) { const operator = operators.get(statement.name); if (operator) { if (operator !== statement) statements[index] = operator; } else { statement.unresolved = false; operators.set(statement.name, statement); } } } else { statement.resolveStatements(rules, operators, visited); } }); } resolveSyntax(keywords) { switch (this.type) { case statementType_1.StatementType.Rule: { let link; if (this.alternatives) { this.statements.forEach(statement => { link = statement.constructLink(false); if (link.hasKeyword()) this.syntax.push(link); }); } else { const syntax = new syntaxLink_1.SyntaxLink(); this.statements.forEach(statement => { link = statement.constructLink(syntax.hasKeyword()); if (syntax.hasKeyword() || link.hasKeyword()) { syntax.add(link); } }); if (syntax.hasKeyword()) this.syntax.push(syntax); } break; } case statementType_1.StatementType.Keyword: { const syntax = new syntaxLink_1.SyntaxLink(); if (!!this.value) syntax.keyword = this.value; else log_1.Log.warn("Keyword without value " + this.name, "GrammarStatement"); this.syntax.push(syntax); break; } case statementType_1.StatementType.Operator: { break; } case statementType_1.StatementType.Operand: { break; } default: log_1.Log.warn("Unknown statement type " + this.type, "GrammarStatement"); } if (this.alternatives) { const added = []; const removed = []; this.syntax.forEach(link => { if (link.alternatives) { removed.push(link); link.chain.forEach(sublink => { sublink.multiplied = (0, multiplyMode_1.mergeMultiplyMode)(sublink.multiplied, link.multiplied); added.push(sublink); }); } }); this.syntax = this.syntax.filter(link => !removed.includes(link)); this.syntax.push(...added); } this.syntax.forEach(link => { link.collapse(); // Due to bug in tokenizing process, remove erroneous optional flags link.unOption(); link.collectSyntax(keywords); }); } constructLink(keyword) { let syntax = new syntaxLink_1.SyntaxLink(); syntax.multiplied = this.multiplied; switch (this.type) { case statementType_1.StatementType.Rule: { if (this.statements.length === 1) syntax = this.statements[0].constructLink(false); else if (!!this.name) syntax.rule = this.name; break; } case statementType_1.StatementType.Block: { if (this.statements.length === 1) { syntax = this.statements[0].constructLink(keyword); break; } let link; if (this.alternatives) { syntax.alternatives = true; this.statements.forEach(statement => { link = statement.constructLink(keyword); if (keyword || link.hasKeyword()) { syntax.add(link); } }); } else { this.statements.forEach(statement => { link = statement.constructLink(keyword || syntax.hasKeyword()); if (keyword || syntax.hasKeyword() || link.hasKeyword()) { syntax.add(link); } }); } break; } case statementType_1.StatementType.Keyword: { if (!!this.value) syntax.keyword = this.value; else log_1.Log.warn("Keyword without value " + this.name, "GrammarStatement"); break; } case statementType_1.StatementType.Operator: { if (!!this.value) syntax.operator = this.value; else log_1.Log.warn("Operator without value " + this.name, "GrammarStatement"); break; } case statementType_1.StatementType.Operand: { if (!!this.name) syntax.operand = this.name; else log_1.Log.warn("Operand without name", "GrammarStatement"); break; } default: log_1.Log.warn("Unknown statement type " + this.type, "GrammarStatement"); } syntax.collapse(); return syntax; } isOptional() { return this.multiplied === multiplyMode_1.MultiplyMode.Optional || this.multiplied === multiplyMode_1.MultiplyMode.Zeromore; } is(type) { return this.type === type; } get name() { return this._name; } get label() { return this._label; } set label(label) { this._label = label; } get value() { return this._value; } multiply(token) { this.greedy = this.greedy || token.greedy; if (this.multiplied === token.multiplied || token.multiplied === multiplyMode_1.MultiplyMode.None) return; this.multiplied = this.multiplied === multiplyMode_1.MultiplyMode.None ? token.multiplied : multiplyMode_1.MultiplyMode.Zeromore; } isToken(types) { return !!this.token && types.includes(this.token.type); } } exports.GrammarStatement = GrammarStatement; //# sourceMappingURL=grammarStatement.js.map