@making-sense/antlr-editor
Version:
ANTLR Typescript editor
401 lines • 15.8 kB
JavaScript
"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