greyscript-core
Version:
Core lexer/parser for GreyScript
212 lines (211 loc) • 9.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const miniscript_core_1 = require("miniscript-core");
const greybel_core_1 = require("greybel-core");
const position_1 = require("miniscript-core/dist/types/position");
const range_1 = require("miniscript-core/dist/types/range");
const ast_1 = require("./parser/ast");
const lexer_1 = __importDefault(require("./lexer"));
const keywords_1 = require("./types/keywords");
class Parser extends greybel_core_1.Parser {
constructor(content, options = {}) {
options.lexer =
options.lexer ||
new lexer_1.default(content, {
unsafe: options.unsafe,
tabWidth: options.tabWidth
});
options.astProvider = options.astProvider || new ast_1.ASTProvider();
super(content, options);
const me = this;
me.nativeImports = [];
}
parseStatement() {
const me = this;
if (me.isType(miniscript_core_1.TokenType.Keyword)) {
const value = me.token.value;
switch (value) {
case keywords_1.GreyScriptKeyword.ImportCode:
me.next();
const item = me.parseNativeImportCodeStatement();
me.lineRegistry.addItemToLines(item);
me.backpatches.peek().body.push(item);
return;
default:
break;
}
}
return super.parseStatement();
}
parseFunctionDeclaration(base, asLval = false, statementStart = false) {
const me = this;
if (!greybel_core_1.Selectors.Function(me.token))
return me.parseOr(asLval, statementStart);
me.next();
const functionStartToken = me.previousToken;
const functionStatement = me.astProvider.functionStatement({
start: functionStartToken.start,
end: null,
range: [functionStartToken.range[0], null],
scope: me.currentScope,
parent: me.outerScopes[me.outerScopes.length - 1],
assignment: me.currentAssignment
});
const parameters = [];
me.pushScope(functionStatement);
if (!greybel_core_1.SelectorGroups.BlockEndOfLine(me.token)) {
me.requireToken(greybel_core_1.Selectors.LParenthesis, functionStartToken.start);
while (!greybel_core_1.SelectorGroups.FunctionDeclarationArgEnd(me.token)) {
const parameter = me.parseIdentifier(miniscript_core_1.ASTIdentifierKind.Argument);
const parameterStartToken = parameter;
if (me.consume(greybel_core_1.Selectors.Assign)) {
const defaultValue = me.parseExpr(null);
const assign = me.astProvider.assignmentStatement({
variable: parameter,
init: defaultValue,
start: parameterStartToken.start,
end: me.previousToken.end,
range: [parameterStartToken.range[0], me.previousToken.range[1]],
scope: me.currentScope
});
me.currentScope.definitions.push(assign);
parameters.push(assign);
}
else {
const assign = me.astProvider.assignmentStatement({
variable: parameter,
init: me.astProvider.unknown({
start: parameterStartToken.start,
end: me.previousToken.end,
range: [parameterStartToken.range[0], me.previousToken.range[1]],
scope: me.currentScope
}),
start: parameterStartToken.start,
end: me.previousToken.end,
range: [parameterStartToken.range[0], me.previousToken.range[1]],
scope: me.currentScope
});
me.currentScope.definitions.push(assign);
parameters.push(parameter);
}
if (greybel_core_1.Selectors.RParenthesis(me.token))
break;
me.requireToken(greybel_core_1.Selectors.ArgumentSeperator, functionStartToken.start);
if (greybel_core_1.Selectors.RParenthesis(me.token)) {
me.raise('expected argument instead received right parenthesis', new range_1.Range(me.previousToken.end, me.previousToken.end));
break;
}
}
me.requireToken(greybel_core_1.Selectors.RParenthesis, functionStartToken.start);
}
functionStatement.parameters = parameters;
const pendingBlock = new miniscript_core_1.PendingFunction(functionStatement, base, me.lineRegistry);
me.backpatches.push(pendingBlock);
return functionStatement;
}
parseNativeImportCodeStatement() {
var _a, _b, _c, _d;
const me = this;
const startToken = me.previousToken;
if (!me.consume(greybel_core_1.Selectors.LParenthesis)) {
me.raise(`expected import_code to have opening parenthesis`, new range_1.Range(startToken.start, new position_1.Position((_a = me.token.lastLine) !== null && _a !== void 0 ? _a : me.token.line, me.token.end.character)));
return me.parseInvalidCode();
}
let directory;
if (me.isType(miniscript_core_1.TokenType.StringLiteral)) {
directory = me.token.value;
me.next();
}
else {
me.raise(`expected import_code argument to be string literal`, new range_1.Range(startToken.start, new position_1.Position((_b = me.token.lastLine) !== null && _b !== void 0 ? _b : me.token.line, me.token.end.character)));
return me.parseInvalidCode();
}
if (me.consume(greybel_core_1.Selectors.ImportCodeSeperator)) {
if (!me.isType(miniscript_core_1.TokenType.StringLiteral)) {
me.raise(`expected import_code argument to be string literal`, new range_1.Range(startToken.start, new position_1.Position((_c = me.token.lastLine) !== null && _c !== void 0 ? _c : me.token.line, me.token.end.character)));
return me.parseInvalidCode();
}
directory = me.token.value;
console.warn(`Warning: Second import_code argument is deprecated. Use the first argument for the file system path instead.`);
me.next();
}
if (!me.consume(greybel_core_1.Selectors.RParenthesis)) {
me.raise(`expected import_code to have closing parenthesis`, new range_1.Range(startToken.start, new position_1.Position((_d = me.token.lastLine) !== null && _d !== void 0 ? _d : me.token.line, me.token.end.character)));
return me.parseInvalidCode();
}
const endToken = me.previousToken;
let currentDirectory = directory;
let shouldEmit = true;
let shouldEval = true;
if (me.isType(miniscript_core_1.TokenType.Comment)) {
const options = ast_1.ASTImportCodeExpression.parseMetaOptions(me.token.value);
shouldEmit = options.emit;
shouldEval = options.eval;
if (options.directory) {
currentDirectory = options.directory;
}
}
const base = me.astProvider.importCodeExpression({
originalDirectory: directory,
directory: currentDirectory,
emit: shouldEmit,
eval: shouldEval,
start: startToken.start,
end: endToken.end,
range: [startToken.range[0], endToken.range[1]],
scope: me.currentScope
});
me.nativeImports.push(base);
return base;
}
parseChunk() {
const me = this;
me.next();
const startToken = me.token;
const chunk = me.astProvider.chunk({
start: startToken.start,
end: null,
range: [startToken.range[0], null]
});
const pending = new miniscript_core_1.PendingChunk(chunk, me.lineRegistry);
me.backpatches.setDefault(pending);
me.pushScope(chunk);
while (!greybel_core_1.Selectors.EndOfFile(me.token)) {
me.skipNewlines();
if (greybel_core_1.Selectors.EndOfFile(me.token))
break;
me.lexer.recordSnapshot();
me.statementErrors = [];
me.parseStatement();
if (me.statementErrors.length > 0) {
me.tryToRecover();
}
}
let last = me.backpatches.pop();
while (!(0, miniscript_core_1.isPendingChunk)(last)) {
const exception = me.raise(`found open block ${last.block.type}`, new range_1.Range(last.block.start, last.block.start));
last.complete(me.previousToken);
me.errors.push(exception);
if (!me.unsafe) {
throw exception;
}
last = me.backpatches.pop();
}
me.finishRemaingScopes();
me.popScope();
pending.complete(me.token);
chunk.literals = me.literals;
chunk.comments = me.comments;
chunk.scopes = me.scopes;
chunk.lines = me.lineRegistry.lines;
chunk.nativeImports = me.nativeImports;
chunk.imports = me.imports;
chunk.includes = me.includes;
chunk.injects = me.injects;
return chunk;
}
}
exports.default = Parser;