UNPKG

syntax-cli-prog

Version:

Syntactic analysis toolkit, language agnostic parsers generator.

308 lines (244 loc) 8.61 kB
'use strict'; var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * C# tokenizer template. */ var CSHARP_TOKENIZER_TEMPLATE = _fs2.default.readFileSync(__dirname + '/templates/tokenizer.template.cs', 'utf-8'); /** * The trait is used by parser generators (LL/LR) for C#. */ /** * The MIT License (MIT) * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com> */ var CSharpParserGeneratorTrait = { /** * Generates parser class name. */ generateParserClassName: function generateParserClassName(className) { this.writeData('PARSER_CLASS_NAME', className); }, /** * Generates parsing table in C# Dictionary format. */ generateParseTable: function generateParseTable() { this.writeData('TABLE', this._buildTable(this.generateParseTableData())); }, /** * Converts JS object into C# Dictionary. * * In C# we represent a table as an array, where index is a state number, * and a value is a record of LR entries (shift/reduce/etc). * * Example: * { * new Dictionary<int, string>() * { * {0, "1"}, * {3, "s8"}, * {4, "s2"}, * }, * } */ _buildTable: function _buildTable(table) { var _this = this; var entries = []; Object.keys(table).forEach(function (state) { entries.push('new Dictionary<int, string>() ' + _this._toCSharpDictionary(table[state], 'number', 'string')); }); return '{ ' + entries.join(',\n\n') + ' }'; }, /** * Generates tokens table in C# Dictionary format. */ generateTokensTable: function generateTokensTable() { this.writeData('TOKENS', this._toCSharpDictionary(this._tokens, 'string', 'number')); }, /** * Production handlers are implemented as methods on the `yyparse` class. */ buildSemanticAction: function buildSemanticAction(production) { var action = this.getSemanticActionCode(production); if (!action) { return null; } action = this._actionFromHandler(action); var args = this.getSemanticActionParams(production) // Append type information for C#. .map(function (arg) { return 'dynamic ' + arg; }).join(','); // Save the action, they are injected later. this._productionHandlers.push({ args: args, action: action }); return '"_handler' + this._productionHandlers.length + '"'; }, /** * Default format in the [ ] array notation. */ generateProductionsData: function generateProductionsData() { return this.generateRawProductionsData().map(function (data) { return 'new object[] { ' + data + ' }'; }); }, /** * Generates built-in tokenizer instance. */ generateBuiltInTokenizer: function generateBuiltInTokenizer() { this.writeData('TOKENIZER', CSHARP_TOKENIZER_TEMPLATE); }, /** * Creates an action from raw handler. */ _actionFromHandler: function _actionFromHandler(handler) { var action = (this._scopeVars(handler) || '').trim(); if (!action) { return 'return null;'; } if (!/;\s*$/.test(action)) { action += ';'; } return action; }, /** * Generates rules for tokenizer. */ generateLexRules: function generateLexRules() { var _this2 = this; var lexRules = this._grammar.getLexGrammar().getRules().map(function (lexRule) { var action = _this2._actionFromHandler(lexRule.getRawHandler()); _this2._lexHandlers.push({ args: '', action: action }); var flags = []; if (lexRule.isCaseInsensitive()) { flags.push('i'); } if (flags.length > 0) { flags = '(?' + flags.join('') + ')'; } else { flags = ''; } // Example: new string[] {@"^\s+", "_lexRule1"}, return 'new string[] { @"' + flags + lexRule.getRawMatcher() + '", ' + ('"_lexRule' + _this2._lexHandlers.length + '" }'); }); this.writeData('LEX_RULES', '{ ' + lexRules.join(',\n') + ' }'); }, 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._toCSharpDictionary(result, 'string')); }, /** * Replaces global vars like `yytext`, `$$`, etc. to be * referred from `yyparse`. */ _scopeVars: function _scopeVars(code) { return code.replace(/yytext/g, 'yyparse.yytext').replace(/yyleng/g, 'yyparse.yyleng').replace(/yyloc/g, 'YyLoc.yyloc'); }, /** * Type-converts a key of a C# dictionary, e.g. string or number, etc. */ _dictKey: function _dictKey(key, keyType) { switch (keyType) { case 'string': return '"' + key + '"'; case 'number': return Number(key); default: throw new Error('_dictKey: Incorrect type ' + keyType); } }, /** * Type-converts a value of a C# dictionary, e.g. string or number, etc. */ _dictValue: function _dictValue(value, valueType) { if (Array.isArray(value)) { // Support only int arrays here for simplicity. return 'new int[] { ' + value.join(', ') + ' }'; } switch (valueType) { case 'string': return '"' + value + '"'; case 'number': return Number(value); default: throw new Error('_dictValue: Incorrect value ' + valueType); } }, /** * Converts JS object to C#'s Dictionary representation. * * {x: 10, y: 20} -> {{"x", 10}, {"y", 20}} * * The `keyTransform`, and `valueTransform` are used to put * the data in needed types, e.g. strings, numbers, arrays, etc. */ _toCSharpDictionary: function _toCSharpDictionary(object, keyType, valueType) { var result = []; for (var k in object) { var value = object[k]; var key = k.replace(/"/g, '\\"'); result.push('{ ' + this._dictKey(key, keyType) + ', ' + (this._dictValue(value, valueType) + ' }')); } return '{ ' + result.join(', ') + ' }'; }, /** * C#-specific lex rules handler declarations. */ generateLexHandlers: function generateLexHandlers() { var handlers = this._generateHandlers(this._lexHandlers, '_lexRule', 'object'); this.writeData('LEX_RULE_HANDLERS', handlers.join('\n\n')); }, /** * C#-specific handler declarations. */ generateProductionHandlers: function generateProductionHandlers() { var handlers = this._generateHandlers(this._productionHandlers, '_handler', 'void'); this.writeData('PRODUCTION_HANDLERS', handlers.join('\n\n')); }, /** * Productions array in C# format. */ generateProductions: function generateProductions() { this.writeData('PRODUCTIONS', '{ ' + this.generateProductionsData().join(',\n') + ' }'); }, /** * Injects the code passed in the module include directive. * Default is class Init { public void run() { ... }} * which is used to setup parsing hooks, etc. */ generateModuleInclude: function generateModuleInclude() { var moduleInclude = this._grammar.getModuleInclude(); var defaultModuleInclude = '\n namespace SyntaxParser\n {\n public class Init\n {\n public static void run()\n {\n // Put init code here.\n // E.g. yyparse.onParseBegin = (string code) => { ... };\n }\n }\n }\n '; if (!moduleInclude) { moduleInclude = defaultModuleInclude; } else if (!/class Init\s+{/.test(moduleInclude)) { moduleInclude += defaultModuleInclude; } this.writeData('MODULE_INCLUDE', moduleInclude); }, /** * Generates C#'s void function declarations for handlers. * * Example: * * public void _handler1(dynamic _1, dynamic _2) * { * __ = _1 + _2; * } */ _generateHandlers: function _generateHandlers(handlers, name, returnType) { return handlers.map(function (_ref, index) { var args = _ref.args, action = _ref.action; return 'public ' + returnType + ' ' + name + (index + 1) + ('(' + args + ') {\n' + action + '\n}'); }); } }; module.exports = CSharpParserGeneratorTrait;