UNPKG

@roku-road/bright

Version:

Blazing fast parser for BrightScript that gives you ESTree like AST

398 lines (397 loc) 18.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const lodash_1 = require("lodash"); const BaseVisitor_1 = require("./BaseVisitor"); class ASTVisitor extends BaseVisitor_1.BaseVisitor { constructor() { super(); this.validateVisitor(); } Program(ctx, props = { tokens: [] }) { return this.mapArguments(ctx, ({ Declaration = [] }) => { const body = this.asArray(Declaration); const head = lodash_1.first(body); const tail = lodash_1.last(body); return this.asNode(Object.assign({}, this.Location(head, tail), { body, comments: [], sourceType: "module", tokens: props ? props.tokens : [], type: "Program" }), ctx); }); } EndOfStatement(ctx) { return this.mapArguments(ctx, ({ TERMINATOR, Comment }) => { return Comment ? Comment : TERMINATOR; }); } LibraryStatement(ctx) { return this.mapArguments(ctx, ({ LIBRARY, path }) => { return this.asNode(Object.assign({}, this.Location(LIBRARY, path), { path, type: "LibraryStatement" }), ctx); }); } FunctionDeclaration(ctx) { return this.mapArguments(ctx, ({ FUNCTION, END_FUNCTION, id, ReturnType, params, body, trailingComments, }) => { trailingComments = this.hasComment(trailingComments); return this.asNode(Object.assign({ type: "FunctionDeclaration", id, ReturnType, params, trailingComments, body }, this.Location(FUNCTION, END_FUNCTION)), ctx); }); } BlockStatement(ctx) { return this.mapArguments(ctx, ({ body }) => { const bodyArray = this.asArray(body); const head = lodash_1.first(bodyArray); const tail = lodash_1.last(bodyArray); return this.asNode(Object.assign({}, this.Location(head, tail), { body: lodash_1.filter(bodyArray, (node) => node && node.type !== "NEWLINE"), type: "BlockStatement" }), ctx); }); } Statement(ctx) { return this.mapArguments(ctx, ({ trailingComments, Empty, Statement }) => { trailingComments = this.hasComment(trailingComments); const node = this.asNode(Object.assign({ trailingComments }, (Statement || Empty)), ctx); return node; }); } ExpressionStatement(ctx) { return this.singleNode(ctx); } EmptyStatement(ctx) { return this.mapArguments(ctx, ({ NEWLINE, trailingComments }) => { if (trailingComments) { return trailingComments; } return Object.assign({ type: "EmptyStatement" }, this.Location(NEWLINE, NEWLINE)); }); } ArrayExpression(ctx) { return this.mapArguments(ctx, ({ OPEN_BRACKET, CLOSE_BRACKET, elements = [], trailingComments }) => { const elementsAsArray = this.asArray(elements); const trailingCommentsAsArray = this.asArray(trailingComments); return this.asNode(Object.assign({}, this.Location(OPEN_BRACKET, CLOSE_BRACKET), { elements: this.mergeTrailing(elementsAsArray, trailingCommentsAsArray), type: "ArrayExpression" }), ctx); }); } ObjectExpression(ctx) { return this.mapArguments(ctx, ({ OPEN_CURLY_BRACE, CLOSE_CURLY_BRACE, properties = [], trailingComments = [], }) => { const propsAsArray = this.asArray(properties); const trailingCommentsAsArray = this.asArray(trailingComments); return this.asNode(Object.assign({}, this.Location(OPEN_CURLY_BRACE, CLOSE_CURLY_BRACE), { properties: this.mergeTrailing(propsAsArray, trailingCommentsAsArray), trailingComments, type: "ObjectExpression" }), ctx); }); } GoToStatement(ctx) { return this.mapArguments(ctx, ({ GOTO, Identifier }) => { return this.asNode(Object.assign({ type: "GoToStatement", id: Identifier }, this.Location(GOTO, Identifier)), ctx); }); } LabeledStatement(ctx) { return this.mapArguments(ctx, ({ COLON, label, body }) => { return this.asNode(Object.assign({ type: "LabeledStatement", label, body }, this.Location(label, COLON)), ctx); }); } Property(ctx) { return this.mapArguments(ctx, ({ key, value }) => { return this.asNode(Object.assign({ type: "Property", key, value }, this.Location(key, value)), ctx); }); } ArrayElement(ctx) { return this.mapArguments(ctx, ({ value, trailingComments = [] }) => { trailingComments = this.hasComment(trailingComments); return this.asNode(Object.assign({ type: "ArrayElement", value, trailingComments }, this.Location(value, value)), ctx); }); } PropertyName(ctx) { return this.singleNode(ctx); } DimStatement(ctx) { return this.mapArguments(ctx, ({ DIM, Identifier, ArrayExpression }) => { return this.asNode(Object.assign({ type: "DimStatement", id: Identifier, ArrayExpression }, this.Location(DIM, ArrayExpression)), ctx); }); } ExitStatement(ctx) { return this.asNode(Object.assign({ type: "ExitStatement" }, this.singleNode(ctx)), ctx); } IfStatement(ctx) { return this.mapArguments(ctx, ({ IF, END_IF, test, alternate, consequent }) => { const bodyArray = this.asArray(consequent); const tail = END_IF ? END_IF : lodash_1.last(bodyArray); return this.asNode(Object.assign({ type: "IfStatement", test, alternate, consequent }, this.Location(IF, tail)), ctx); }); } ElseIfStatement(ctx) { return this.mapArguments(ctx, ({ ELSE_IF, body = [], test }) => { const bodyArray = this.asArray(body); const tail = bodyArray.length ? lodash_1.last(bodyArray) : ELSE_IF; return this.asNode(Object.assign({ type: "ElseIfStatement", test, body }, this.Location(ELSE_IF, tail)), ctx); }); } ElseStatement(ctx) { return this.mapArguments(ctx, ({ ELSE, body = [] }) => { const bodyArray = this.asArray(body); const tail = bodyArray.length ? lodash_1.last(bodyArray) : ELSE; return this.asNode(Object.assign({ type: "ElseStatement", body }, this.Location(ELSE, tail)), ctx); }); } ForStatement(ctx) { return this.mapArguments(ctx, ({ FOR, END_FOR, init, test, counter, update, body, trailingComments, }) => { const tail = lodash_1.first(lodash_1.filter([END_FOR, lodash_1.last(this.asArray(body))])); trailingComments = this.hasComment(trailingComments); return this.asNode(Object.assign({ type: "ForStatement", init, test, update, counter, body, trailingComments }, this.Location(FOR, tail)), ctx); }); } ForEachStatement(ctx) { return this.mapArguments(ctx, ({ FOR, END_FOR, countExpression, body, trailingComments, counter }) => { const tail = lodash_1.first(lodash_1.filter([END_FOR, lodash_1.last(this.asArray(body))])); trailingComments = this.hasComment(trailingComments); return this.asNode(Object.assign({ type: "ForEachStatement", countExpression, trailingComments, counter, body }, this.Location(FOR, tail)), ctx); }); } NextStatement(ctx) { return this.mapArguments(ctx, ({ NEXT }) => { return this.asNode(Object.assign({}, this.Location(NEXT, NEXT), { type: "NextStatement" }), ctx); }); } PrintStatement(ctx) { return this.mapArguments(ctx, ({ PRINT, value }) => { const valueAsArray = this.asArray(value); const tail = valueAsArray.length ? lodash_1.last(valueAsArray) : PRINT; return this.asNode(Object.assign({}, this.Location(PRINT, tail), { type: "PrintStatement", value }), ctx); }); } ReturnStatement(ctx) { return this.mapArguments(ctx, ({ RETURN, argument }) => { return this.asNode(Object.assign({}, this.Location(RETURN, argument ? argument : RETURN), { argument, type: "ReturnStatement" }), ctx); }); } StopStatement(ctx) { return this.mapArguments(ctx, ({ STOP }) => { return this.asNode(Object.assign({}, this.Location(STOP, STOP), { type: "StopStatement" }), ctx); }); } WhileStatement(ctx) { return this.mapArguments(ctx, ({ WHILE, END_WHILE, test, body }) => { return this.asNode(Object.assign({ type: "WhileStatement", test, body }, this.Location(WHILE, END_WHILE)), ctx); }); } RokuTryStatement(ctx) { return this.mapArguments(ctx, ({ TRY, END_TRY, body, trailingComments, exception, onError }) => { return this.asNode(Object.assign({ type: "RokuTryStatement", body, trailingComments, exception, onError }, this.Location(TRY, END_TRY)), ctx); }); } FunctionExpression(ctx) { return this.mapArguments(ctx, ({ FUNCTION, END_FUNCTION, body = [], params = [], ReturnType }) => { return this.asNode(Object.assign({ type: "FunctionExpression", body, params, ReturnType }, this.Location(FUNCTION, END_FUNCTION)), ctx); }); } SubExpression(ctx) { return this.mapArguments(ctx, ({ SUB, END_SUB, body, params }) => { return this.asNode(Object.assign({ type: "SubExpression", body, params }, this.Location(SUB, END_SUB)), ctx); }); } SubDeclaration(ctx) { return this.mapArguments(ctx, ({ SUB, END_SUB, id, params, ReturnType, body }) => { return this.asNode(Object.assign({ type: "SubDeclaration", id, params, body, ReturnType }, this.Location(SUB, END_SUB)), ctx); }); } AssignmentExpression(ctx) { return this.singleNode(ctx); } AdditionExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ ADDICTIVE_OPERATOR, left, right }) => this.flatListExpression("AdditionExpression", ADDICTIVE_OPERATOR, left, right))); } MultiplicationExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ MULTI_OPERATOR, left, right }) => this.flatListExpression("MultiplicationExpression", MULTI_OPERATOR, left, right))); } ShiftExpression(ctx) { return this.singleNode(ctx); } RelationExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ RELATIONAL_OPERATOR, left, right }) => this.flatListExpression("RelationExpression", RELATIONAL_OPERATOR, left, right))); } EqualityExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ EQUALITY_OPERATOR, left, right }) => { const head = lodash_1.first(this.asArray(left)); const tail = lodash_1.last(this.asArray(right)); return this.asNode(Object.assign({}, this.Location(head, tail), { left, operator: EQUALITY_OPERATOR, right, type: "AssignmentExpression" }), ctx); })); } LogicExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ LOGIC_OPERATOR, left, right }) => this.flatListExpression("LogicExpression", LOGIC_OPERATOR, left, right))); } UnaryExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ UNARY, right }) => { return this.asNode(Object.assign({ type: "UnaryExpression", operator: UNARY, argument: right }, this.Location(UNARY, right)), ctx); })); } Arguments(ctx) { return this.mapArguments(ctx, ({ OPEN_PAREN, CLOSE_PAREN, param = [] }) => { return this.asNode(Object.assign({ type: "Arguments", param }, this.Location(OPEN_PAREN, CLOSE_PAREN)), ctx); }); } PostfixExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ POSTFIX, left }) => { return this.asNode(Object.assign({ type: "PostfixExpression", operator: POSTFIX, argument: left }, this.Location(left, POSTFIX)), ctx); })); } CallMemberExpression({ id, args }) { return Object.assign({}, this.Location(id, args), { args, callee: id, type: "CallExpression" }); } CallExpression(ctx) { return this.mapArguments(ctx, ({ id, args }) => { return this.asNode(Object.assign({}, this.Location(id, args), { args, callee: id, type: "CallExpression" }), ctx); }); } ObjectMemberExpression({ id, properties = [] }) { properties = this.asArray(properties); return Object.assign({}, this.Location(id, lodash_1.last(properties)), { computed: false, object: id, properties, type: "MemberExpression" }); } MemberExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ id, properties = [], args = "" }) => { if (args) { return this.asNode(this.CallMemberExpression({ id, args }), ctx); } else { return this.asNode(this.ObjectMemberExpression({ id, properties }), ctx); } })); } MemberChunkExpression(ctx) { return (this.singleArgument(ctx) || this.mapArguments(ctx, ({ property, args }) => { if (args) { return this.CallMemberExpression({ id: property, args }); } else { return this.ObjectMemberExpression({ id: property, properties: [] }); } })); } DotMemberExpression(ctx) { return (this.singleArgument(ctx) || this.asNode(this.mapArguments(ctx, ({ operator, right }) => (Object.assign({}, this.Location(operator, right), { operator, right, type: "DotMemberExpression" }))), ctx)); } PrimaryExpression(ctx) { return this.singleNode(ctx); } ParenthesisExpression(ctx) { return this.mapArguments(ctx, ({ OPEN_PAREN, CLOSE_PAREN, innerExpression }) => { return this.asNode(Object.assign({}, this.Location(OPEN_PAREN, CLOSE_PAREN), { expression: innerExpression, type: "ParenthesisExpression" }), ctx); }); } Literal(ctx) { return this.mapArguments(ctx, ({ LITERAL }) => { const { loc, range } = LITERAL; return this.asNode({ type: "Literal", range, raw: loc.source, value: loc.source, loc }, ctx); }); } ReservedWord(ctx) { return this.singleNode(ctx); } ConditionalCompilationStatement(ctx) { return this.singleNode(ctx); } ConditionalConst(ctx) { return this.mapArguments(ctx, ({ CONDITIONAL_CONST, assignment }) => { return this.asNode(Object.assign({ type: "ConditionalConst", assignment }, this.Location(CONDITIONAL_CONST, assignment)), ctx); }); } ConditionalError(ctx) { return this.mapArguments(ctx, ({ CONDITIONAL_ERROR }) => { return this.asNode(Object.assign({ type: "ConditionalError", error: CONDITIONAL_ERROR }, this.Location(CONDITIONAL_ERROR, CONDITIONAL_ERROR)), ctx); }); } ConditionalIfStatement(ctx) { return this.mapArguments(ctx, ({ CONDITIONAL_IF, CONDITIONAL_END_IF, body, test, alternate }) => { return this.asNode(Object.assign({ alternate, body, test, type: "ConditionalIfStatement" }, this.Location(CONDITIONAL_IF, CONDITIONAL_END_IF)), ctx); }); } ConditionalElseIfStatement(ctx) { return this.mapArguments(ctx, ({ CONDITIONAL_ELSE_IF, test, body, trailingComments = [] }) => { const tail = lodash_1.last(this.asArray(body)); trailingComments = this.hasComment(trailingComments); return this.asNode(Object.assign({ body, test, trailingComments, type: "ConditionalElseIfStatement" }, this.Location(CONDITIONAL_ELSE_IF, tail)), ctx); }); } ConditionalElseStatement(ctx) { return this.mapArguments(ctx, ({ CONDITIONAL_ELSE, body }) => { const tail = lodash_1.last(this.asArray(body)); return this.asNode(Object.assign({ body, type: "ConditionalElseStatement" }, this.Location(CONDITIONAL_ELSE, tail)), ctx); }); } UnTypedIdentifier(ctx) { return this.mapArguments(ctx, ({ IDENTIFIER }) => { const { loc, range } = IDENTIFIER; return this.asNode({ type: "UnTypedIdentifier", name: loc.source, loc, range }, ctx); }); } ParameterList(ctx) { return this.mapArguments(ctx, ({ Parameter, OPEN_PAREN, CLOSE_PAREN }) => { return this.asNode(Object.assign({}, this.Location(OPEN_PAREN, CLOSE_PAREN), { args: this.asArray(Parameter), type: "ParameterList" }), ctx); }); } Parameter(ctx) { return this.mapArguments(ctx, ({ Identifier, TypeAnnotation, value }) => { const tail = lodash_1.last(lodash_1.filter([Identifier, TypeAnnotation, value])); return this.asNode(Object.assign({ type: "Parameter", name: Identifier, TypeAnnotation, value }, this.Location(Identifier, tail)), ctx); }); } Identifier(ctx) { return this.mapArguments(ctx, ({ asType = "", id }) => { return this.asNode(Object.assign({ asType }, id, { type: "Identifier" }), ctx); }); } TypeAnnotation(ctx) { const { loc, range } = this.singleNode(ctx); return this.asNode({ loc, range, value: loc.source, type: "TypeAnnotation" }, ctx); } Comment(ctx) { const COMMENT = this.singleNode(ctx); let value = null; if (COMMENT.type === "COMMENT_QUOTE") { value = COMMENT.loc.source.substr(1).trim(); } else { value = COMMENT.loc.source.substr(3).trim(); } return this.asNode(Object.assign({ type: "Comment", value }, this.Location(COMMENT, COMMENT)), ctx); } hasComment(trailingComments) { if (trailingComments && trailingComments.type === "Comment") { return trailingComments; } return ""; } } exports.ASTVisitor = ASTVisitor;