UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

294 lines (243 loc) 8.36 kB
'use strict'; var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Example tokenizer template. */ var EXAMPLE_TOKENIZER_TEMPLATE = _fs2.default.readFileSync(__dirname + '/templates/tokenizer.template.example', 'utf-8'); /** * The trait is used by parser generators (LL/LR) for Example language. * * Here we can override any needed method from the `LRParserGeneratorDefault`, * or even from the very base `BaseParserGenerator` to generate code in a * needed target language format. * * E.g., overriding `generateParseTable()` we can transform the parsing table, * which is received from the `generateParseTableData()`, from the JS format * to the format of the target language. */ /** * The MIT License (MIT) * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com> */ var ExampleParserGeneratorTrait = { /** * Generates parser class name. */ generateParserClassName: function generateParserClassName(className) { this.writeData('PARSER_CLASS_NAME', className); }, /** * Generates parsing table in Example language Map format. * * An array of records, where index is a state number, and a value is a * map from an encoded symbol (number) to a parsing action. * * The parsing action can be "Shift/s", "Reduce/r", a state * transition number, or "Accept/acc". * * Example: * * [ * // 0 * { * 0: "1", * 3: "s8", * 4: "s2", * }, * // 1 * { * 1: "s3", * 2: "s4", * 6: "acc", * }, * ... * ] */ generateParseTable: function generateParseTable() { this.writeData('TABLE', this._toExampleLanguageMap(this.generateParseTableData())); }, /** * Generates tokens table in Example language Map format. */ generateTokensTable: function generateTokensTable() { this.writeData('TOKENS', this._toExampleLanguageMap(this._tokens)); }, /** * Production handlers are implemented as methods on the `yyparse` class. */ buildSemanticAction: function buildSemanticAction(production) { var action = this.getSemanticActionCode(production); if (!action) { return null; } // Here can do any transformation on the semantic action. action += ';'; var args = this.getSemanticActionParams(production).join(','); // Save the action, they are injected later. // "_handler1", "_handler2", etc. this._productionHandlers.push({ args: args, action: action }); return '_handler' + this._productionHandlers.length; }, /** * Default format in the [ ] array notation. * * The `generateRawProductionsData` from the base class returns * an encoded grammar productions table. * * Format of an array: * * [ <Non-Terminal Index>, <RHS.Length>, <handler> ] * * Non-terminal indices are from 0 to Last Non-terminal index. * LR-algorithm uses length of RHS to pop symbols from the stack; this * length is stored as the second element of a record. The last element is * an optional name of the semantic action handler. The first record is always * a special marker [-1, -1] entry representing an augmented production. * * Example: * * [ * [-1, 1], * [0, 3, "_handler1"], * [0, 2, "_handler2"], * ... * ] */ generateProductionsData: function generateProductionsData() { return this.generateRawProductionsData().map(function (data) { return JSON.stringify(data); }); }, /** * Generates built-in tokenizer instance. */ generateBuiltInTokenizer: function generateBuiltInTokenizer() { this.writeData('TOKENIZER', EXAMPLE_TOKENIZER_TEMPLATE); }, /** * Generates rules for tokenizer. * * Array of arrays: * * [ * [/^\s+/, "_lexRule1"], * [/^\d+/, "_lexRule1"], * ... * ] * */ generateLexRules: function generateLexRules() { var _this = this; var lexRules = this._grammar.getLexGrammar().getRules().map(function (lexRule) { // Here you may transform the handler code as needed. E.g. if your // language doesn't support module-level global variables, such as // `yytext`, and `yyleng` which we use as real globals here, you may // choose to transform them to some scoped access: you may store // `yytext` as a class property (e.g. on yyparse class), and replace // `yytext` to `yyparse.yytext` in the code. var action = lexRule.getRawHandler() + ';'; _this._lexHandlers.push({ args: '', action: action }); var flags = []; if (lexRule.isCaseInsensitive()) { flags.push('i'); } // Example: ["^\s+", "_lexRule1"], return '[/' + lexRule.getRawMatcher() + '/' + flags.join('') + ', ' + ('"_lexRule' + _this._lexHandlers.length + '"]'); }); this.writeData('LEX_RULES', '[ ' + lexRules.join(',\n') + ' ]'); }, /** * Generates lexical rules grouped by start conditions. A map from state * name to the list of lex rule indices from the `LEX_RULES`. * * Example: * * { * "INITIAL": [0,5,6,7], * "comment": [1,2,3,4,6], * ... * }; */ generateLexRulesByStartConditions: function generateLexRulesByStartConditions() { var lexGrammar = this._grammar.getLexGrammar(); var lexRulesByConditions = lexGrammar.getRulesByStartConditions(); var result = {}; for (var condition in lexRulesByConditions) { result[condition] = lexRulesByConditions[condition].map(function (lexRule) { return lexGrammar.getRuleIndex(lexRule); }); } this.writeData('LEX_RULES_BY_START_CONDITIONS', '' + this._toExampleLanguageMap(result)); }, /** * An imaginary converter from JS format of an object, to the Example * language map object. E.g. here you may convert from JS's {foo: 10, bar: 20} * into PHP's array('foo' => 10, 'bar' => 20), etc. */ _toExampleLanguageMap: function _toExampleLanguageMap(object) { // See other plugin examples to see actual transformation. // Here just use the simplest JSON.stringify. return JSON.stringify(object); }, /** * Example language specific lex rules handler declarations. */ generateLexHandlers: function generateLexHandlers() { var handlers = this._generateHandlers(this._lexHandlers, '_lexRule', '' /* return type, you can use e.g. 'string' */ ); this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n')); }, /** * Example language specific handler declarations. */ generateProductionHandlers: function generateProductionHandlers() { var handlers = this._generateHandlers(this._productionHandlers, '_handler', '' /* return type */ ); this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n')); }, /** * Productions array in the Example language format. * * An array of arrays, see `generateProductionsData` for details. */ generateProductions: function generateProductions() { this.writeData('PRODUCTIONS', '[ ' + this.generateProductionsData().join(',\n') + ' ]'); }, /** * Injects the code passed in the module include directive. */ generateModuleInclude: function generateModuleInclude() { var moduleInclude = this._grammar.getModuleInclude(); if (!moduleInclude) { // Example: add some default module include if needed. moduleInclude = '\n let foo = \'Example module include\';\n '; } this.writeData('MODULE_INCLUDE', moduleInclude); }, /** * Generates function declarations for handlers. * * Example: * * _handler1(_1, _2) { * __ = _1 + _2; * } * * Or: * * _lexRule1() { * return "NUMBER"; * } */ _generateHandlers: function _generateHandlers(handlers, name) { var returnType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; return handlers.map(function (_ref, index) { var args = _ref.args, action = _ref.action; return returnType + ' ' + name + (index + 1) + ('(' + args + ') {\n' + action + '\n}'); }); } }; module.exports = ExampleParserGeneratorTrait;