PageRenderTime 12ms CodeModel.GetById 2ms app.highlight 4ms RepoModel.GetById 1ms app.codeStats 0ms

/vm/tinyrb/grammar.leg

http://github.com/feyeleanor/RubyGoLightly
Unknown | 334 lines | 282 code | 52 blank | 0 comment | 0 complexity | 4dcd747ac6b099c7cba2309e4786b4fa MD5 | raw file
  1%{
  2#include <stdlib.h>
  3#include "tr.h"
  4#include "internal.h"
  5
  6/*#define YY_DEBUG 1*/
  7
  8#define YYSTYPE   OBJ
  9#define YYMALLOC  TR_MALLOC
 10#define YYREALLOC TR_REALLOC
 11#define yyvm      compiler->vm
 12
 13static char *charbuf;
 14static char *sbuf;
 15static size_t nbuf;
 16static TrCompiler *compiler;
 17
 18#define YY_INPUT(buf, result, max_size) { \
 19  int yyc; \
 20  if (charbuf && *charbuf != '\0') \
 21    yyc= *charbuf++; \
 22  else \
 23    yyc= EOF; \
 24  result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \
 25}
 26
 27/* TODO grow buffer */
 28#define STRING_MAX   4096
 29#define STRING_START sbuf = TR_ALLOC_N(char, STRING_MAX); nbuf = 0
 30#define STRING_PUSH(P,L) \
 31  assert(nbuf + (L) < 4096); \
 32  TR_MEMCPY_N(sbuf + nbuf, (P), char, (L)); \
 33  nbuf += (L)
 34
 35%}
 36
 37Root      = s:Stmts EOF                     { compiler->node = NODE(ROOT, s) }
 38
 39Stmts     = SEP*
 40            - head:Stmt Comment?            { head = NODES(head) }
 41            ( SEP - tail:Stmt Comment?      { PUSH_NODE(head, tail) }
 42            | SEP - Comment
 43            )* SEP?                         { $$ = head }
 44          | SEP+                            { $$ = NODES_N(0) }
 45
 46OptStmts  = Stmts
 47          | - SEP?                          { $$ = NODES_N(0) }
 48
 49Stmt      = While
 50          | Until
 51          | If
 52          | Unless
 53          | Def
 54          | Class
 55          | Module
 56          | Expr
 57
 58Expr      = Assign
 59          | AsgnCall
 60          | UnaryOp
 61          | BinOp
 62          | SpecCall
 63          | Call
 64          | Range
 65          | Yield
 66          | Return
 67          | Break
 68          | Value
 69
 70Comment   = - '#' (!EOL .)*
 71
 72Call      =                                 { block = rcv = 0 }
 73            ( rcv:Value '.'
 74            )? ( rmsg:Message '.'           { rcv = NODE2(SEND, rcv, rmsg) }
 75               )* msg:Message
 76                  - block:Block?            { $$ = NODE3(SEND, rcv, msg, block) }
 77
 78# TODO refactor head part w/ Call maybe eh?
 79AsgnCall   =                                { rcv = 0 }
 80            ( rcv:Value '.'
 81            )? ( rmsg:Message '.'           { rcv = NODE2(SEND, rcv, rmsg) }
 82               )* msg:ID - asg:ASSIGN
 83                  - val:Stmt                { VM = yyvm; $$ = NODE2(SEND, rcv, NODE2(MSG, SYMCAT(msg, asg), NODES(NODE(ARG, val)))) }
 84
 85Receiver  = (                               { rcv = 0 }
 86              rcv:Call
 87            | rcv:Value
 88            )                               { $$ = rcv }
 89
 90SpecCall  = rcv:Receiver '[' args:Args ']'  
 91            - ASSIGN - val:Stmt             { PUSH_NODE(args, NODE(ARG, val)); $$ = NODE2(SEND, rcv, NODE2(MSG, TrSymbol_new(yyvm, "[]="), args)) }
 92          | rcv:Receiver '[' args:Args ']'  { $$ = NODE2(SEND, rcv, NODE2(MSG, TrSymbol_new(yyvm, "[]"), args)) }
 93
 94BinOp     = ( rcv:SpecCall | rcv:Receiver )
 95            -
 96            (
 97              '&&' - arg:Expr               { $$ = NODE2(AND, rcv, arg) }
 98            | '||' - arg:Expr               { $$ = NODE2(OR, rcv, arg) }
 99            | '+' - arg:Expr                { $$ = NODE2(ADD, rcv, arg) }
100            | '-' - arg:Expr                { $$ = NODE2(SUB, rcv, arg) }
101            | '<' - arg:Expr                { $$ = NODE2(LT, rcv, arg) }
102            | op:BINOP - arg:Expr           { $$ = NODE2(SEND, rcv, NODE2(MSG, op, NODES(NODE(ARG, arg)))) }
103            ) 
104
105UnaryOp   = '-' rcv:Expr                    { $$ = NODE(NEG, rcv) }
106          | '!' rcv:Expr                    { $$ = NODE(NOT, rcv) }
107
108Message   = name:ID                         { args = 0 }
109              ( '(' args:Args? ')'
110              | SPACE args:Args
111              )?                            { $$ = NODE2(MSG, name, args) }
112
113Args      = - head:Expr -                   { head = NODES(NODE(ARG, head)) }
114            ( ',' - tail:Expr -             { PUSH_NODE(head, NODE(ARG, tail)) }
115            )* ( ',' - '*' splat:Expr -     { PUSH_NODE(head, NODE2(ARG, splat, 1)) }
116               )?                           { $$ = head }
117          | - '*' splat:Expr -              { $$ = NODES(NODE2(ARG, splat, 1)) }
118
119Block     = 'do' SEP
120              - body:OptStmts -
121            'end'                           { $$ = NODE(BLOCK, body) }
122          | 'do' - '|' params:Params '|' SEP
123              - body:OptStmts -
124            'end'                           { $$ = NODE2(BLOCK, body, params) }
125          # FIXME this might hang the parser and is very slow.
126          # Clash with Hash for sure.
127          #| '{' - body:OptStmts - '}'       { $$ = NODE(BLOCK, body) }
128          #| '{' - '|' params:Params '|'
129          #  - body:OptStmts - '}'           { $$ = NODE2(BLOCK, body, params) }
130
131Assign    = name:ID - ASSIGN - val:Stmt     { $$ = NODE2(ASSIGN, name, val) }
132          | name:CONST - ASSIGN - val:Stmt  { $$ = NODE2(SETCONST, name, val) }
133          | name:IVAR - ASSIGN - val:Stmt   { $$ = NODE2(SETIVAR, name, val) }
134          | name:CVAR - ASSIGN - val:Stmt   { $$ = NODE2(SETCVAR, name, val) }
135          | name:GLOBAL - ASSIGN - val:Stmt { $$ = NODE2(SETGLOBAL, name, val) }
136
137While     = 'while' SPACE cond:Expr SEP
138              body:Stmts -
139            'end'                           { $$ = NODE2(WHILE, cond, body) }
140
141Until     = 'until' SPACE cond:Expr SEP
142              body:Stmts -
143            'end'                           { $$ = NODE2(UNTIL, cond, body) }
144
145If        = 'if' SPACE cond:Expr SEP        { else_body = 0 }
146              body:Stmts -
147            else_body:Else?
148            'end'                           { $$ = NODE3(IF, cond, body, else_body) }
149          | body:Expr - 'if' - cond:Expr    { $$ = NODE2(IF, cond, NODES(body)) }
150
151Unless    = 'unless' SPACE cond:Expr SEP    { else_body = 0 }
152              body:Stmts -
153            else_body:Else?
154            'end'                           { $$ = NODE3(UNLESS, cond, body, else_body) }
155          | body:Expr -
156              'unless' - cond:Expr          { $$ = NODE2(UNLESS, cond, NODES(body)) }
157
158Else      = 'else' SEP - body:Stmts -       { $$ = body }
159
160Method    = rcv:ID '.' name:METHOD          { $$ = NODE2(METHOD, NODE2(SEND, 0, NODE(MSG, rcv)), name) }
161          | rcv:Value '.' name:METHOD       { $$ = NODE2(METHOD, rcv, name) }
162          | name:METHOD                     { $$ = NODE2(METHOD, 0, name) }
163
164Def       = 'def' SPACE method:Method       { params = 0 }
165            (- '(' params:Params? ')')? SEP
166              body:OptStmts -
167            'end'                           { $$ = NODE3(DEF, method, params ? params : NODES_N(0), body) }
168
169Params    = head:Param                      { head = NODES(head) }
170            ( ',' tail:Param                { PUSH_NODE(head, tail) }
171            )*                              { $$ = head }
172
173Param     = - name:ID - '=' - def:Expr      { $$ = NODE3(PARAM, name, 0, def) }
174          | - name:ID -                     { $$ = NODE(PARAM, name) }
175          | - '*' name:ID -                 { $$ = NODE2(PARAM, name, 1) }
176
177Class     = 'class' SPACE name:CONST        { super = 0 }
178            (- '<' - super:CONST)? SEP
179              body:OptStmts -
180            'end'                           { $$ = NODE3(CLASS, name, super, body) }
181
182Module    = 'module' SPACE name:CONST SEP
183              body:OptStmts -
184            'end'                           { $$ = NODE3(MODULE, name, 0, body) }
185
186Range     = s:Receiver - '..' - e:Expr      { $$ = NODE3(RANGE, s, e, 0) }
187          | s:Receiver - '...' - e:Expr     { $$ = NODE3(RANGE, s, e, 1) }
188
189Yield     = 'yield' SPACE args:AryItems     { $$ = NODE(YIELD, args) }
190          | 'yield' '(' args:AryItems ')'   { $$ = NODE(YIELD, args) }
191          | 'yield'                         { $$ = NODE(YIELD, NODES_N(0)) }
192
193Return    = 'return' SPACE arg:Expr - !','  { $$ = NODE(RETURN, arg) }
194          | 'return' '(' arg:Expr ')' - !','{ $$ = NODE(RETURN, arg) }
195          | 'return' SPACE args:AryItems    { $$ = NODE(RETURN, NODE(ARRAY, args)) }
196          | 'return' '(' args:AryItems ')'  { $$ = NODE(RETURN, NODE(ARRAY, args)) }
197          | 'return'                        { $$ = NODE(RETURN, 0) }
198
199Break     = 'break'                         { $$ = NODE(BREAK, 0) }
200
201Value     = v:NUMBER                        { $$ = NODE(VALUE, v) }
202          | v:SYMBOL                        { $$ = NODE(VALUE, v) }
203          | v:REGEXP                        { $$ = NODE(VALUE, v) }
204          | v:STRING1                       { $$ = NODE(STRING, v) }
205          | v:STRING2                       { $$ = NODE(STRING, v) }
206          | v:CONST                         { $$ = NODE(CONST, v) }
207          | 'nil'                           { $$ = NODE(NIL, 0) }
208          | 'true'                          { $$ = NODE(BOOL, TR_TRUE) }
209          | 'false'                         { $$ = NODE(BOOL, TR_FALSE) }
210          | 'self'                          { $$ = NODE(SELF, 0) }
211          | name:IVAR                       { $$ = NODE(GETIVAR, name) }
212          | name:CVAR                       { $$ = NODE(GETCVAR, name) }
213          | name:GLOBAL                     { $$ = NODE(GETGLOBAL, name) } # TODO
214          | '[' - ']'                       { $$ = NODE(ARRAY, NODES_N(0)) }
215          | '[' - items:AryItems - ']'      { $$ = NODE(ARRAY, items) }
216          | '{' - '}'                       { $$ = NODE(HASH, NODES_N(0)) }
217          | '{' - items:HashItems - '}'     { $$ = NODE(HASH, items) }
218          | '(' - Expr - ')'
219
220AryItems  = - head:Expr -                   { head = NODES(head) }
221            ( ',' - tail:Expr -             { PUSH_NODE(head, tail) }
222            )*                              { $$ = head }
223
224HashItems = head:Expr - '=>' - val:Expr     { head = NODES_N(2, head, val) }
225            ( - ',' - key:Expr -            { PUSH_NODE(head, key) }
226                '=>' - val:Expr             { PUSH_NODE(head, val) }
227            )*                              { $$ = head }
228
229KEYWORD   = 'while' | 'until' | 'do' | 'end' |
230            'if' | 'unless' | 'else' |
231            'true' | 'false' | 'nil' | 'self' |
232            'class' | 'module' | 'def' |
233            'yield' | 'return' | 'break'
234
235NAME      = [a-zA-Z0-9_]+
236ID        = !'self'                         # self is special, can never be a method name
237            < KEYWORD > &('.' | '(' | '[')  { $$ = TrSymbol_new(yyvm, yytext) } # hm, there's probably a better way
238          | < KEYWORD NAME >                { $$ = TrSymbol_new(yyvm, yytext) }
239          | !KEYWORD
240            < [a-z_] NAME?
241              ( '=' &'(' | '!'| '?' )? >    { $$ = TrSymbol_new(yyvm, yytext) }
242CONST     = < [A-Z] NAME? >                 { $$ = TrSymbol_new(yyvm, yytext) }
243BINOP     = < ( '**' | '^'  | '&'  | '|'  | '~'  |
244                '+'  | '-'  | '*'  | '/'  | '%'  | '<=>' |
245                '<<' | '>>' | '==' | '=~' | '!=' | '===' |
246                '<'  | '>'  | '<=' | '>='
247              ) >                           { $$ = TrSymbol_new(yyvm, yytext) }
248UNOP      = < ( '-@' | '!' ) >              { $$ = TrSymbol_new(yyvm, yytext) }
249METHOD    = ID | UNOP | BINOP
250ASSIGN    = < '=' > &(!'=')                 { $$ = TrSymbol_new(yyvm, yytext) }
251IVAR      = < '@' NAME >                    { $$ = TrSymbol_new(yyvm, yytext) }
252CVAR      = < '@@' NAME >                   { $$ = TrSymbol_new(yyvm, yytext) }
253GLOBAL    = < '$' NAME >                    { $$ = TrSymbol_new(yyvm, yytext) }
254NUMBER    = < [0-9]+ >                      { $$ = TR_INT2FIX(atoi(yytext)) }
255SYMBOL    = ':' < (NAME | KEYWORD) >        { $$ = TrSymbol_new(yyvm, yytext) }
256
257STRING1   = '\''                            { STRING_START }
258            (
259              '\\\''                        { STRING_PUSH("'", 1) }
260            | < [^\'] >                     { STRING_PUSH(yytext, yyleng) }
261            )* '\''                         { $$ = TrString_new2(yyvm, sbuf) }
262
263ESC_CHAR  = '\\n'                           { STRING_PUSH("\n", 1) }
264          | '\\b'                           { STRING_PUSH("\b", 1) }
265          | '\\f'                           { STRING_PUSH("\f", 1) }
266          | '\\r'                           { STRING_PUSH("\r", 1) }
267          | '\\t'                           { STRING_PUSH("\t", 1) }
268          | '\\\"'                          { STRING_PUSH("\"", 1) }
269          | '\\\\'                          { STRING_PUSH("\\", 1) }
270
271STRING2   = '"'                             { STRING_START }
272            (
273              ESC_CHAR
274            | < [^\"] >                     { STRING_PUSH(yytext, yyleng) }  #" for higlighting
275            )*
276            '"'                             { $$ = TrString_new2(yyvm, sbuf) }
277
278REGEXP    = '/'                             { STRING_START }
279            (
280              ESC_CHAR
281            | < [^/] >                      { STRING_PUSH(yytext, yyleng) }
282            )*
283            '/'                             { $$ = TrRegexp_new(yyvm, sbuf, 0) }
284
285-         = [ \t]*
286SPACE     = [ ]+
287EOL       = ( '\n' | '\r\n' | '\r' )        { compiler->line++ }
288EOF       = !.
289SEP       = ( - Comment? (EOL | ';') )+
290
291%%
292
293/* Raise a syntax error. */
294OBJ yyerror() {
295  VM = yyvm;
296  OBJ msg = tr_sprintf(vm, "SyntaxError in %s at line %d", TR_STR_PTR(compiler->filename), compiler->line);
297  /* Stupid ugly code, just to build a string... I suck... */
298  if (yytext[0]) TrString_push(vm, msg, tr_sprintf(vm, " near token '%s'", yytext));
299  if (yypos < yylimit) {
300    yybuf[yylimit]= '\0';
301    TrString_push(vm, msg, tr_sprintf(vm, " before text \""));
302    while (yypos < yylimit) {
303      if ('\n' == yybuf[yypos] || '\r' == yybuf[yypos]) break;
304      char c[2] = { yybuf[yypos++], '\0' };
305      TrString_push(vm, msg, tr_sprintf(vm, c));
306    }
307    TrString_push(vm, msg, tr_sprintf(vm, "\""));
308  }
309  /* TODO msg should not be a String object */
310  tr_raise(SyntaxError, TR_STR_PTR(msg));
311}
312
313/* Compiles code to a TrBlock.
314   Returns NULL on error, error is stored in TR_EXCEPTION. */
315TrBlock *TrBlock_compile(VM, char *code, char *fn, size_t lineno) {
316  assert(!compiler && "parser not reentrant");
317  charbuf = code;
318  compiler = TrCompiler_new(vm, fn);
319  compiler->line += lineno;
320  compiler->filename = TrString_new2(vm, fn);
321  TrBlock *b = NULL;
322
323  if (!yyparse()) {
324    yyerror();
325    goto error;
326  }
327
328  TrCompiler_compile(compiler);
329  b = compiler->block;
330error:
331  charbuf = 0;
332  compiler = 0;
333  return b;
334}