1+ /**
2+ * The MIT License (MIT)
3+ * Copyright (c) 2015-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
4+ */
5+
6+ import fs from 'fs' ;
7+
8+ const LUA_TOKENIZER_TEMPLATE = fs . readFileSync (
9+ `${ __dirname } /templates/tokenizer.template.lua` ,
10+ 'utf-8'
11+ ) ;
12+
13+ const LuaParserGeneratorTrait = {
14+
15+ /**
16+ * Generates parser class name.
17+ */
18+ generateParserClassName ( className ) {
19+ this . writeData ( 'PARSER_CLASS_NAME' , className ) ;
20+ } ,
21+
22+ generateParseTable ( ) {
23+ this . writeData (
24+ 'TABLE' ,
25+ this . _toLuaMap ( this . generateParseTableData ( ) ) ,
26+ ) ;
27+ } ,
28+
29+ /**
30+ * Generates tokens table in Lua Map format.
31+ */
32+ generateTokensTable ( ) {
33+ this . writeData (
34+ 'TOKENS' ,
35+ this . _toLuaMap ( this . _tokens ) ,
36+ ) ;
37+ } ,
38+
39+ buildSemanticAction ( production ) {
40+ let action = this . getSemanticActionCode ( production ) ;
41+
42+ if ( ! action ) {
43+ return null ;
44+ }
45+
46+ action += ';' ;
47+
48+ const args = this
49+ . getSemanticActionParams ( production )
50+ . join ( ',' ) ;
51+
52+ this . _productionHandlers . push ( { args, action} ) ;
53+ return `_handler${ this . _productionHandlers . length } ` ;
54+ } ,
55+
56+ generateProductionsData ( ) {
57+ return this . generateRawProductionsData ( )
58+ . map ( data => {
59+ return `{ ${ data . map ( ( item , index ) => {
60+ // quote
61+ if ( index >= 2 ) {
62+ return `"${ item } "` ;
63+ }
64+ return item ;
65+ } ) . join ( ',' ) } }`;
66+ } ) ;
67+ } ,
68+
69+ generateBuiltInTokenizer ( ) {
70+ this . writeData ( 'TOKENIZER' , LUA_TOKENIZER_TEMPLATE ) ;
71+ } ,
72+
73+ generateLexRules ( ) {
74+ let lexRules = this . _grammar . getLexGrammar ( ) . getRules ( ) . map ( lexRule => {
75+
76+ const action = lexRule . getRawHandler ( ) + ';' ;
77+
78+ this . _lexHandlers . push ( { args : '' , action} ) ;
79+
80+ const flags = [ ] ;
81+
82+ if ( lexRule . isCaseInsensitive ( ) ) {
83+ flags . push ( 'i' ) ;
84+ }
85+
86+ // Example: ["^\s+", "_lexRule1"],
87+ return `{[[${ lexRule . getRawMatcher ( ) } ${ flags . join ( '' ) } ]], ` +
88+ `"_lexRule${ this . _lexHandlers . length } "}` ;
89+ } ) ;
90+
91+ this . writeData ( 'LEX_RULES' , `{ ${ lexRules . join ( ',\n' ) } }` ) ;
92+ } ,
93+
94+ generateLexRulesByStartConditions ( ) {
95+ const lexGrammar = this . _grammar . getLexGrammar ( ) ;
96+ const lexRulesByConditions = lexGrammar . getRulesByStartConditions ( ) ;
97+ const result = { } ;
98+
99+ for ( const condition in lexRulesByConditions ) {
100+ result [ condition ] = lexRulesByConditions [ condition ] . map ( lexRule =>
101+ lexGrammar . getRuleIndex ( lexRule )
102+ ) ;
103+ }
104+
105+ this . writeData (
106+ 'LEX_RULES_BY_START_CONDITIONS' ,
107+ `${ this . _toLuaMap ( result ) } ` ,
108+ ) ;
109+ } ,
110+
111+ /**
112+ * Converts JS object to Lua's table representation.
113+ * E.g. converts {foo: 10, bar: 20} into {foo = 10, bar = 20}
114+ */
115+ _toLuaMap ( value ) {
116+ function _toLuaMapInner ( value ) {
117+ if ( value === null ) return "nil" ;
118+ if ( typeof value === "number" || typeof value === "boolean" ) return value . toString ( ) ;
119+ if ( typeof value === "string" ) return `"${ value . replace ( / " / g, '\\"' ) } "` ;
120+
121+ if ( Array . isArray ( value ) ) {
122+ const items = value . map ( _toLuaMapInner ) . join ( ", " ) ;
123+ return `{${ items } }` ;
124+ }
125+
126+ if ( typeof value === "object" ) {
127+ const entries = Object . entries ( value ) . map ( ( [ k , v ] ) => {
128+ const key = / ^ [ a - z A - Z _ ] [ a - z A - Z 0 - 9 _ ] * $ / . test ( k ) ? k : `["${ k } "]` ;
129+ return `${ key } = ${ _toLuaMapInner ( v ) } ` ;
130+ } ) . join ( ", " ) ;
131+ return `{${ entries } }` ;
132+ }
133+
134+ return "nil" ; // fallback
135+ }
136+
137+ return _toLuaMapInner ( value ) ;
138+ } ,
139+
140+ /**
141+ * Lua lex rules handler declarations.
142+ */
143+ generateLexHandlers ( ) {
144+ const handlers = this . _generateHandlers (
145+ this . _lexHandlers ,
146+ 'Tokenizer:' ,
147+ '_lexRule' ,
148+ '' /* return type, you can use e.g. 'string' */
149+ ) ;
150+ this . writeData ( 'LEX_RULE_HANDLERS' , handlers . join ( '\n\n' ) ) ;
151+ } ,
152+
153+ /**
154+ * Lua parser handler declarations.
155+ */
156+ generateProductionHandlers ( ) {
157+ const handlers = this . _generateHandlers (
158+ this . _productionHandlers ,
159+ 'parser:' ,
160+ '_handler' ,
161+ '' , /* return type */
162+ ) ;
163+ this . writeData ( 'PRODUCTION_HANDLERS' , handlers . join ( '\n' ) ) ;
164+ } ,
165+
166+ /**
167+ * Productions array in the Lua format.
168+ *
169+ * An array of arrays, see `generateProductionsData` for details.
170+ */
171+ generateProductions ( ) {
172+ this . writeData (
173+ 'PRODUCTIONS' ,
174+ `{ ${ this . generateProductionsData ( ) . join ( ',\n' ) } }`
175+ ) ;
176+ } ,
177+
178+ /**
179+ * Injects the code passed in the module include directive.
180+ */
181+ generateModuleInclude ( ) {
182+ let moduleInclude = this . _grammar . getModuleInclude ( ) ;
183+
184+ if ( ! moduleInclude ) {
185+ // Example: add some default module include if needed.
186+ moduleInclude = `
187+ let foo = 'Example module include';
188+ ` ;
189+ }
190+
191+ this . writeData ( 'MODULE_INCLUDE' , moduleInclude ) ;
192+ } ,
193+
194+ _generateHandlers ( handlers , class_prefix , name , returnType = '' ) {
195+ return handlers . map ( ( { args, action} , index ) => {
196+ return `\nfunction ${ class_prefix } ${ name } ${ index + 1 } ` +
197+ `(${ args } )\n\t\t${ action } \nend`
198+ } ) ;
199+ } ,
200+ } ;
201+
202+ module . exports = LuaParserGeneratorTrait ;
0 commit comments