psl-parser
Version:
A parser for the Profile Scripting Language
706 lines • 26.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tokenizer_1 = require("./tokenizer");
var SyntaxKind;
(function (SyntaxKind) {
SyntaxKind[SyntaxKind["ASSIGNMENT"] = 0] = "ASSIGNMENT";
SyntaxKind[SyntaxKind["BINARY_OPERATOR"] = 1] = "BINARY_OPERATOR";
SyntaxKind[SyntaxKind["CATCH_STATEMENT"] = 2] = "CATCH_STATEMENT";
SyntaxKind[SyntaxKind["DO_STATEMENT"] = 3] = "DO_STATEMENT";
SyntaxKind[SyntaxKind["FOR_STATEMENT"] = 4] = "FOR_STATEMENT";
SyntaxKind[SyntaxKind["IDENTIFIER"] = 5] = "IDENTIFIER";
SyntaxKind[SyntaxKind["IF_STATEMENT"] = 6] = "IF_STATEMENT";
SyntaxKind[SyntaxKind["NUMERIC_LITERAL"] = 7] = "NUMERIC_LITERAL";
SyntaxKind[SyntaxKind["POST_CONDITION"] = 8] = "POST_CONDITION";
SyntaxKind[SyntaxKind["QUIT_STATEMENT"] = 9] = "QUIT_STATEMENT";
SyntaxKind[SyntaxKind["RETURN_STATEMENT"] = 10] = "RETURN_STATEMENT";
SyntaxKind[SyntaxKind["SET_STATEMENT"] = 11] = "SET_STATEMENT";
SyntaxKind[SyntaxKind["MULTIPLE_VARIABLE_SET"] = 12] = "MULTIPLE_VARIABLE_SET";
SyntaxKind[SyntaxKind["STRING_LITERAL"] = 13] = "STRING_LITERAL";
SyntaxKind[SyntaxKind["WHILE_STATEMENT"] = 14] = "WHILE_STATEMENT";
SyntaxKind[SyntaxKind["TYPE_STATEMENT"] = 15] = "TYPE_STATEMENT";
SyntaxKind[SyntaxKind["VARIABLE_DECLARATION"] = 16] = "VARIABLE_DECLARATION";
SyntaxKind[SyntaxKind["TYPE_IDENTIFIER"] = 17] = "TYPE_IDENTIFIER";
})(SyntaxKind = exports.SyntaxKind || (exports.SyntaxKind = {}));
var OPERATOR_VALUE;
(function (OPERATOR_VALUE) {
OPERATOR_VALUE["AND_LITERAL"] = "and";
OPERATOR_VALUE["APOSTROPHE"] = "'";
OPERATOR_VALUE["AT"] = "@";
OPERATOR_VALUE["BACK_SLASH"] = "\\";
OPERATOR_VALUE["CARROT"] = "^";
OPERATOR_VALUE["COLON"] = ":";
OPERATOR_VALUE["DOLLAR"] = "$";
OPERATOR_VALUE["DOT"] = ".";
OPERATOR_VALUE["EQUAL"] = "=";
OPERATOR_VALUE["EXCLAMATION"] = "!";
OPERATOR_VALUE["GREATER_THAN"] = ">";
OPERATOR_VALUE["HASH"] = "#";
OPERATOR_VALUE["LEFT_BRACKET"] = "[";
OPERATOR_VALUE["LESS_THAN"] = "<";
OPERATOR_VALUE["MINUS"] = "-";
OPERATOR_VALUE["NOT_LITERAL"] = "not";
OPERATOR_VALUE["OR_LITERAL"] = "or";
OPERATOR_VALUE["PLUS"] = "+";
OPERATOR_VALUE["QUESTION_MARK"] = "?";
OPERATOR_VALUE["RIGHT_BRACKET"] = "]";
OPERATOR_VALUE["SLASH"] = "/";
OPERATOR_VALUE["STAR"] = "*";
OPERATOR_VALUE["UNDERSCORE"] = "_";
OPERATOR_VALUE["RET"] = "ret";
})(OPERATOR_VALUE || (OPERATOR_VALUE = {}));
var STORAGE_MODIFIERS;
(function (STORAGE_MODIFIERS) {
STORAGE_MODIFIERS["STATIC"] = "static";
STORAGE_MODIFIERS["NEW"] = "new";
STORAGE_MODIFIERS["LITERAL"] = "literal";
})(STORAGE_MODIFIERS || (STORAGE_MODIFIERS = {}));
var ACCESS_MODIFIERS;
(function (ACCESS_MODIFIERS) {
ACCESS_MODIFIERS["PUBLIC"] = "public";
ACCESS_MODIFIERS["PRIVATE"] = "private";
})(ACCESS_MODIFIERS || (ACCESS_MODIFIERS = {}));
var STATEMENT_KEYWORD;
(function (STATEMENT_KEYWORD) {
STATEMENT_KEYWORD["DO"] = "do";
STATEMENT_KEYWORD["SET"] = "set";
STATEMENT_KEYWORD["IF"] = "if";
STATEMENT_KEYWORD["CATCH"] = "catch";
STATEMENT_KEYWORD["FOR"] = "for";
STATEMENT_KEYWORD["QUIT"] = "quit";
STATEMENT_KEYWORD["RETURN"] = "return";
STATEMENT_KEYWORD["WHILE"] = "while";
STATEMENT_KEYWORD["TYPE"] = "type";
})(STATEMENT_KEYWORD || (STATEMENT_KEYWORD = {}));
const UNARY_OPERATORS = [
{ value: OPERATOR_VALUE.APOSTROPHE },
{ value: OPERATOR_VALUE.AT },
{ value: OPERATOR_VALUE.CARROT },
{ value: OPERATOR_VALUE.DOLLAR, appendable: true },
{ value: OPERATOR_VALUE.DOT },
{ value: OPERATOR_VALUE.MINUS },
{ value: OPERATOR_VALUE.NOT_LITERAL },
{ value: OPERATOR_VALUE.PLUS },
{ value: OPERATOR_VALUE.RIGHT_BRACKET },
{ value: OPERATOR_VALUE.RET },
];
const BINARY_OPERATORS = [
{ value: OPERATOR_VALUE.AND_LITERAL },
{ value: OPERATOR_VALUE.APOSTROPHE, appendable: true },
{ value: OPERATOR_VALUE.AT },
{ value: OPERATOR_VALUE.BACK_SLASH },
{ value: OPERATOR_VALUE.CARROT },
{ value: OPERATOR_VALUE.DOT },
{ value: OPERATOR_VALUE.EQUAL },
{ value: OPERATOR_VALUE.EXCLAMATION },
{ value: OPERATOR_VALUE.GREATER_THAN, appendable: true },
{ value: OPERATOR_VALUE.HASH },
{ value: OPERATOR_VALUE.LEFT_BRACKET },
{ value: OPERATOR_VALUE.LESS_THAN, appendable: true },
{ value: OPERATOR_VALUE.MINUS },
{ value: OPERATOR_VALUE.NOT_LITERAL, appendable: true },
{ value: OPERATOR_VALUE.OR_LITERAL },
{ value: OPERATOR_VALUE.PLUS },
{ value: OPERATOR_VALUE.QUESTION_MARK },
{ value: OPERATOR_VALUE.SLASH },
{ value: OPERATOR_VALUE.STAR },
{ value: OPERATOR_VALUE.UNDERSCORE },
];
class StatementParser {
constructor(arg) {
this.tokens = [];
if (typeof arg === 'string') {
this.tokenizer = tokenizer_1.getTokens(arg);
}
else if (Array.isArray(arg)) {
this.tokenizer = arg[Symbol.iterator]();
}
else {
this.tokenizer = arg;
}
this.next(); // should I?
}
parseLine() {
if (!this.activeToken)
return [];
const statements = [];
do {
if (this.activeToken.isNewLine())
break;
if (this.activeToken.isAlphanumeric()) {
const statement = this.parseStatement();
if (!statement) {
this.next();
continue;
}
statements.push(statement);
}
else if (this.activeToken.isWhiteSpace())
this.next(true);
else
break;
} while (this.activeToken);
return statements;
}
parseStatement() {
if (!this.activeToken)
return;
if (!this.activeToken.isAlphanumeric())
return;
const action = this.activeToken;
let loadFunction;
let kind;
const loadSingleExpression = () => {
if (!this.next(true))
return { action, kind, expressions: [] };
const expression = loadFunction();
const expressions = expression ? [expression] : [];
return { kind, action, expressions };
};
const loadCommaSeparatedExpressions = () => {
if (!this.next(true))
return { action, kind, expressions: [] };
const expressions = this.loadCommaSeparated(loadFunction);
return { kind, action, expressions };
};
switch (action.value) {
case STATEMENT_KEYWORD.DO:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.DO_STATEMENT;
return loadCommaSeparatedExpressions();
case STATEMENT_KEYWORD.SET:
loadFunction = () => this.parseSetExpression();
kind = SyntaxKind.SET_STATEMENT;
return loadCommaSeparatedExpressions();
case STATEMENT_KEYWORD.IF:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.IF_STATEMENT;
return loadCommaSeparatedExpressions();
case STATEMENT_KEYWORD.CATCH:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.CATCH_STATEMENT;
return loadCommaSeparatedExpressions();
case STATEMENT_KEYWORD.FOR:
return this.parseForStatement();
case STATEMENT_KEYWORD.QUIT:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.QUIT_STATEMENT;
return loadCommaSeparatedExpressions();
case STATEMENT_KEYWORD.RETURN:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.RETURN_STATEMENT;
return loadSingleExpression();
case STATEMENT_KEYWORD.WHILE:
loadFunction = () => this.parseExpression();
kind = SyntaxKind.WHILE_STATEMENT;
return loadSingleExpression();
case STATEMENT_KEYWORD.TYPE:
return this.parseTypeStatement();
default:
return;
}
}
parseTypeStatement() {
const action = this.activeToken;
this.next(true);
let staticToken;
let newToken;
let publicToken;
let literalToken;
const getKeyWordToken = (token) => {
switch (token.value) {
case ACCESS_MODIFIERS.PUBLIC:
publicToken = token;
return true;
case STORAGE_MODIFIERS.LITERAL:
literalToken = token;
return true;
case STORAGE_MODIFIERS.NEW:
newToken = token;
return true;
case STORAGE_MODIFIERS.STATIC:
staticToken = token;
return true;
default:
return false;
}
};
const parseTypeAssignments = () => {
if (!this.activeToken || !this.activeToken.isAlphanumeric()) {
if (literalToken || newToken || publicToken || staticToken) {
const emptyDeclaration = {
id: undefined,
kind: SyntaxKind.VARIABLE_DECLARATION,
literalToken: undefined,
newToken,
publicToken,
staticToken,
type: undefined,
};
return [emptyDeclaration];
}
return [];
}
const type = { id: this.activeToken, kind: SyntaxKind.TYPE_IDENTIFIER };
if (!this.next(true) || staticToken) {
const declaration = {
id: undefined,
kind: SyntaxKind.VARIABLE_DECLARATION,
literalToken,
newToken,
publicToken,
staticToken,
type,
};
return [declaration];
}
const assignments = this.loadCommaSeparated(() => {
return this.parseAssignment(() => {
const variable = this.parseValue(); // why not parseIdentifier
return {
args: variable.args,
id: variable.id,
kind: SyntaxKind.VARIABLE_DECLARATION,
literalToken,
newToken,
publicToken,
staticToken,
type,
};
});
});
assignments.forEach(expression => {
forEachChild(expression, node => {
if (!node)
return;
if (node.kind === SyntaxKind.VARIABLE_DECLARATION) {
const declaration = node;
if (declaration.args) {
declaration.args = declaration.args.map((arg) => {
if (!arg)
return;
arg.kind = SyntaxKind.TYPE_IDENTIFIER;
return arg;
});
}
}
return true;
});
});
return assignments;
};
while (this.activeToken && getKeyWordToken(this.activeToken)) {
if (!this.next(true))
break;
}
const expressions = parseTypeAssignments();
return {
action,
expressions,
kind: SyntaxKind.TYPE_STATEMENT,
};
}
parseAssignment(getLeft) {
const left = getLeft();
let rootNode = left;
if (this.activeToken && this.activeToken.isEqualSign()) {
const equalSign = this.activeToken;
this.next(true);
const expression = this.parseExpression();
rootNode = { operator: [equalSign], kind: SyntaxKind.ASSIGNMENT };
rootNode.left = left;
rootNode.right = expression;
}
return rootNode;
}
parseForStatement() {
if (!this.activeToken)
return;
const action = this.activeToken;
const forStatement = { action, kind: SyntaxKind.FOR_STATEMENT, expressions: [] };
if (!this.next())
return forStatement; // consume for
if (!this.next())
return forStatement; // consume first space
const spaceOrExpression = this.activeToken;
if (spaceOrExpression.isSpace()) {
this.next();
return forStatement; // argumentless for
}
const expression = this.parseExpression();
if (expression)
forStatement.expressions.push(expression);
return forStatement;
}
parseSetExpression() {
if (!this.activeToken)
return;
const postCondition = this.parsePostCondition();
const assignment = this.parseAssignment(() => {
const setVariables = this.parseSetVariables();
if (this.activeToken && this.activeToken.isWhiteSpace())
this.next(true);
if (postCondition && !setVariables) {
postCondition.expression = setVariables;
return postCondition;
}
return setVariables;
});
if (assignment && postCondition) {
postCondition.expression = assignment;
return postCondition;
}
return assignment;
}
parsePostCondition() {
if (!this.activeToken)
return;
if (this.activeToken.isColon()) {
const colon = this.activeToken;
const postCondition = { kind: SyntaxKind.POST_CONDITION, colon };
this.next(true);
const condition = this.parseExpression();
if (!condition)
return postCondition;
postCondition.condition = condition;
return postCondition;
}
}
parseSetVariables() {
if (!this.activeToken)
return;
if (this.activeToken.isOpenParen()) {
this.next(true);
const variables = this.loadCommaSeparated(() => this.parseExpression());
if (this.activeToken && this.activeToken.isCloseParen())
this.next(true);
return { variables, kind: SyntaxKind.MULTIPLE_VARIABLE_SET };
}
else {
return this.parseExpression(true);
}
}
parseExpression(ignoreEquals, includeRet) {
const postCondition = this.parsePostCondition();
let rootNode = this.parseValue(undefined, includeRet);
if (!rootNode) {
if (postCondition)
return postCondition;
else
return;
}
if (this.activeToken && this.activeToken.isEqualSign() && ignoreEquals) {
return rootNode;
}
rootNode = this.parseOperatorSeparatedValues(rootNode, ignoreEquals);
if (!rootNode)
return;
rootNode = this.parseColonSeparatedValues(rootNode);
if (postCondition) {
postCondition.expression = rootNode;
rootNode = postCondition;
}
return rootNode;
}
parseColonSeparatedValues(rootNode) {
while (this.activeToken && this.activeToken.isColon()) {
const colonToken = this.activeToken;
this.next(true);
const colon = {
kind: SyntaxKind.BINARY_OPERATOR,
left: rootNode,
operator: [colonToken],
right: this.parseValue(),
};
rootNode = colon;
}
return rootNode;
}
parseOperatorSeparatedValues(rootNode, ignoreEquals) {
while (this.activeToken && getBinaryOperator(this.activeToken.value)) {
if (this.activeToken.isEqualSign() && ignoreEquals)
break;
const operator = this.parseBinaryOperator();
if (!operator)
return;
operator.left = rootNode;
operator.right = this.parseValue();
rootNode = operator;
}
return rootNode;
}
parseBinaryOperator() {
if (!this.activeToken)
return;
const binaryOperator = {
kind: SyntaxKind.BINARY_OPERATOR,
operator: [this.activeToken],
};
if (!this.next(true))
return binaryOperator;
let operator;
do {
operator = getBinaryOperator(this.activeToken.value);
if (!operator)
break;
if (operator) {
const not = operator.value === OPERATOR_VALUE.NOT_LITERAL
|| operator.value === OPERATOR_VALUE.APOSTROPHE;
if (not && binaryOperator.operator.length)
break;
}
binaryOperator.operator.push(this.activeToken);
this.next(true);
} while (operator && operator.appendable);
return binaryOperator;
}
parseUnaryOperator(includeRet) {
if (!this.activeToken)
return [];
const leftOperator = [];
let unaryOperator = getUnaryOperator(this.activeToken.value, includeRet);
if (unaryOperator) {
leftOperator.push(this.activeToken);
this.next(true);
while (unaryOperator && unaryOperator.appendable) {
unaryOperator = getUnaryOperator(this.activeToken.value);
if (unaryOperator) {
leftOperator.push(this.activeToken);
this.next(true);
}
}
}
return leftOperator;
}
parseValue(tree, includeRet) {
let value;
if (!this.activeToken || this.activeToken.isWhiteSpace()) {
if (tree)
return tree;
else
return;
}
const unaryOperator = this.parseUnaryOperator(includeRet);
if (!this.activeToken) {
return {
id: new tokenizer_1.Token(-1 /* Undefined */, '', { character: 0, line: 0 }),
kind: SyntaxKind.IDENTIFIER,
unaryOperator,
};
}
if (this.activeToken.type === 1 /* Alphanumeric */) {
value = this.parseIdentifier();
if (!value)
return;
if (unaryOperator.length)
value.unaryOperator = unaryOperator;
}
else if (this.activeToken.type === 9 /* DoubleQuotes */) {
value = this.parseStringLiteral();
if (!value)
return;
if (unaryOperator.length)
value.unaryOperator = unaryOperator;
}
else if (this.activeToken.type === 2 /* Numeric */) {
value = { id: this.activeToken, kind: SyntaxKind.NUMERIC_LITERAL };
if (unaryOperator.length)
value.unaryOperator = unaryOperator;
this.next(true);
}
else if (this.activeToken.type === 40 /* OpenParen */) {
this.next(true);
value = this.parseExpression();
this.next(true);
}
if (tree && value) {
tree.right = value;
value = tree;
}
if (this.activeToken && (this.activeToken.type === 46 /* Period */ || this.activeToken.type === 94 /* Caret */)) {
const operator = {
kind: SyntaxKind.BINARY_OPERATOR,
left: value,
operator: [this.activeToken],
};
this.next();
return this.parseValue(operator);
}
if (value && this.activeToken && this.activeToken.isWhiteSpace())
this.next(true);
return value;
}
parseIdentifier() {
if (!this.activeToken)
return;
const id = this.activeToken;
if (this.next() && this.activeToken.isOpenParen()) {
const openParen = this.activeToken;
if (this.next(true)) {
const args = this.parseArgs();
if (this.activeToken.isCloseParen()) {
const closeParen = this.activeToken;
this.next();
return { id, kind: SyntaxKind.IDENTIFIER, args, openParen, closeParen };
}
}
}
return { id, kind: SyntaxKind.IDENTIFIER };
}
parseStringLiteral() {
if (!this.activeToken)
return;
const openQuote = this.activeToken;
this.next();
const id = this.activeToken;
if (!id || !this.next())
return;
if (this.activeToken.isDoubleQuotes()) {
const closeQuote = this.activeToken;
this.next(true);
return { id, kind: SyntaxKind.STRING_LITERAL, openQuote, closeQuote };
}
}
parseArgs() {
return this.loadCommaSeparated(() => this.parseExpression(false, true));
}
loadCommaSeparated(getArg) {
const args = [];
do {
if (!this.activeToken)
break;
if (this.activeToken.isWhiteSpace()) {
if (!this.next(true))
break;
}
if (this.activeToken.isComma()) {
args.push(undefined); // desired behavior?
continue;
}
const arg = getArg();
if (arg && !Array.isArray(arg))
args.push(arg);
else if (arg)
args.push(...arg);
else if (!arg && args.length > 0)
args.push(undefined);
else
break;
if (!this.activeToken)
break;
} while (this.activeToken.isComma() && this.next(true));
return args;
}
next(skipSpaceOrTab) {
if (this.activeToken)
this.previousToken = this.activeToken;
do {
this.activeToken = this.tokenizer.next().value;
if (this.activeToken)
this.tokens.push(this.activeToken);
} while (skipSpaceOrTab && this.activeToken && (this.activeToken.isSpace() || this.activeToken.isTab()));
return this.activeToken !== undefined;
}
}
exports.StatementParser = StatementParser;
function getBinaryOperator(tokenValue) {
return BINARY_OPERATORS.find(o => o.value === tokenValue);
}
function getUnaryOperator(tokenValue, includeRet) {
const operator = UNARY_OPERATORS.find(o => o.value === tokenValue);
if (!operator)
return;
if (operator.value === OPERATOR_VALUE.RET && !includeRet)
return;
return operator;
}
function forEachChild(node, f) {
let goDeeper = false;
if (!node)
return;
switch (node.kind) {
case SyntaxKind.DO_STATEMENT:
case SyntaxKind.IF_STATEMENT:
case SyntaxKind.QUIT_STATEMENT:
case SyntaxKind.RETURN_STATEMENT:
case SyntaxKind.SET_STATEMENT:
case SyntaxKind.WHILE_STATEMENT:
case SyntaxKind.FOR_STATEMENT:
case SyntaxKind.CATCH_STATEMENT:
case SyntaxKind.TYPE_STATEMENT:
goDeeper = f(node);
if (!goDeeper)
return;
const statement = node;
statement.expressions.forEach(expression => {
if (!expression)
return;
forEachChild(expression, f);
});
break;
case SyntaxKind.ASSIGNMENT:
case SyntaxKind.BINARY_OPERATOR:
goDeeper = f(node);
if (!goDeeper)
return;
const assignment = node;
const left = assignment.left;
if (left && left.kind === SyntaxKind.MULTIPLE_VARIABLE_SET) {
left.variables.forEach(n => {
forEachChild(n, f);
});
}
else if (left) {
forEachChild(left, f);
}
const right = assignment.right;
if (right) {
forEachChild(right, f);
}
break;
case SyntaxKind.POST_CONDITION:
goDeeper = f(node);
if (!goDeeper)
return;
const postCondition = node;
if (postCondition.condition)
forEachChild(postCondition.condition, f);
if (postCondition.expression) {
const expression = postCondition.expression;
if (Array.isArray(expression)) {
expression.forEach(n => {
forEachChild(n, f);
});
}
else if (expression) {
forEachChild(expression, f);
}
}
break;
case SyntaxKind.IDENTIFIER:
case SyntaxKind.TYPE_IDENTIFIER:
goDeeper = f(node);
if (!goDeeper)
return;
const identifier = node;
if (identifier.args)
identifier.args.forEach(arg => forEachChild(arg, f));
break;
case SyntaxKind.VARIABLE_DECLARATION:
goDeeper = f(node);
if (!goDeeper)
return;
const declaration = node;
if (declaration.args)
declaration.args.forEach(arg => forEachChild(arg, f));
f(declaration.type);
case SyntaxKind.NUMERIC_LITERAL:
case SyntaxKind.STRING_LITERAL:
f(node);
break;
default:
break;
}
}
exports.forEachChild = forEachChild;
//# sourceMappingURL=statementParser.js.map