source: Dev/trunk/src/client/util/docscripts/lib/parser2/JavaScriptSymbol.php

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 16.9 KB
Line 
1<?php
2
3require_once('Symbol.php');
4
5require_once('JavaScriptFunctionCall.php');
6require_once('JavaScriptLiteral.php');
7require_once('JavaScriptString.php');
8require_once('JavaScriptNumber.php');
9require_once('JavaScriptRegExp.php');
10require_once('JavaScriptFunction.php');
11require_once('JavaScriptTernary.php');
12require_once('JavaScriptObject.php');
13require_once('JavaScriptArray.php');
14require_once('JavaScriptVariable.php');
15require_once('JavaScriptAssignment.php');
16require_once('JavaScriptOr.php');
17
18class JavaScriptSymbol extends Symbol {
19  /**
20   * Guesses the type of the current symbol
21   */
22  public function type() {
23    switch($this->id){
24    case '{':
25      return 'Object';
26    case '[':
27      return 'Array';
28    case 'function':
29      return 'Function';
30    }
31  }
32
33  public function convert($recursing = FALSE) {
34    if ($this->arity == 'literal') {
35      switch($this->type){
36      case 'string':
37        return new JavaScriptString($this->value);
38      case 'number':
39        return new JavaScriptNumber($this->value);
40      case 'regex':
41        return new JavaScriptRegExp($this->value);
42      default:
43        return new JavaScriptLiteral($this->value);
44      }
45    }
46    else {
47      switch($this->id){
48      case '?':
49        return new JavaScriptTernary($this->first, $this->second, $this->third);
50      case '(':
51        return new JavaScriptFunctionCall($this->first, $this->second);
52      case 'function':
53        return new JavaScriptFunction($this);
54      case '{':
55        return new JavaScriptObject($this);
56      case '[':
57      case '.':
58        if ($this->arity == 'unary') {
59          return new JavaScriptArray($this->first);
60        }else{
61          return new JavaScriptVariable($this);
62        }
63      case '=':
64        return new JavaScriptAssignment($this->first, $this->second);
65      case '||':
66        $output = array();
67
68        $first = $this->first;
69        if (is_array($first) && count($first == 1)) {
70          $first = $first[0];
71        }
72        $first = $first->convert(TRUE);
73
74        if (is_array($first)) {
75          $output = array_merge($output, $first);
76        }
77        else {
78          $output[] = $first;
79        }
80
81        $seconds = $this->second;
82        if (!is_array($seconds)) {
83          $seconds = array($seconds);
84        }
85        foreach ($seconds as $second) {
86          $second = $second->convert(TRUE);
87          if (is_array($second)) {
88            $output = array_merge($output, $second);
89          }
90          else {
91            $output[] = $second;
92          }
93        }
94
95        return $recursing ? $output : new JavaScriptOr($output);
96      }
97
98      switch ($this->arity) {
99      case 'name':
100        if ($this->value == 'new') {
101          // TODO: Make new matter
102          if ($this->first->id == 'function') {
103            return new JavaScriptFunction($this->first, true);
104          }
105          elseif ($this->first->id == '(') {
106            return new JavaScriptFunctionCall($this->first, $this->second, true);
107          }
108          elseif ($this->first->id == '.' || $this->first->id == '[') {
109            return new JavaScriptVariable($this->first, true);
110          }
111          else {
112            throw new Exception("Line {$this->first->line_number}, char {$this->first->char_pos}: New statement preceeds unknown");
113          }
114        }
115        return new JavaScriptVariable($this);
116      case 'literal':
117      case 'operator':
118      case 'this':
119      case 'unary':
120        return new JavaScriptLiteral($this);
121      }
122    }
123    throw new Exception("No class for {$this->id}:{$this->arity}");
124  }
125
126  // led/nud/lbp functions
127
128  public function led_bracket($parser, $left) {
129    $this->first = $left;
130    $this->second = $parser->expression();
131    $this->arity = 'binary';
132    $parser->advance(']');
133    return $this;
134  }
135
136  public function nud_bracket($parser) {
137    $items = array();
138    if (!$parser->peek(']')) {
139      while (1) {
140        $items[] = $parser->expression();
141        if (!$parser->peek(',')) {
142          break;
143        }
144        $parser->advance(',');
145        if ($parser->peek(']')) {
146          // Be lenient about trailing commas
147          break;
148        }
149      }
150    }
151    $parser->advance(']');
152    $this->first = $items;
153    $this->arity = 'unary';
154    return $this;
155  }
156
157  public function std_break($parser) {
158    $parser->skip_terminators();
159    $this->arity = 'statement';
160    return $this;
161  }
162
163  public function lbp_colon($parser, $left) {
164    if ($left->arity == 'name' && $parser->peek2(array('for', 'while', 'do'))) {
165      return 100;
166    }
167    return 0;
168  }
169
170  public function led_colon($parser, $left) {
171    $this->first = $left;
172    if ($parser->token->arity != 'name') {
173      throw new Exception("Line {$left->line_number}, char {$left->char_pos}: Expected a property name");
174    }
175    $parser->token->arity = 'literal';
176    $this->second = $parser->expression();
177    $this->arity = 'binary';
178    $parser->skip_terminators();
179    return $this;
180  }
181
182  public function lbp_crement($parser, $left) {
183    if ($left->id == '.' || $left->id == '[' || $left->arity == 'name') {
184      return 100;
185    }
186    return 0;
187  }
188
189  public function led_crement($parser, $left) {
190    // Show that the in/decrementer is on the right
191    $this->first = $left;
192    $this->arity = 'unary';
193    return $this;
194  }
195
196  public function nud_crement($parser) {
197    // Show that the in/decrement is before the expression
198    $this->first = NULL;
199    $this->second = $parser->expression(75);
200    return $this;
201  }
202
203  public function nud_curly($parser) {
204    $values = array();
205
206    $this->comments = array();
207    if (!$parser->peek('}')) {
208      while (1) {
209        $token = $parser->token;
210        if ($token->arity != 'name' && $token->arity != 'literal') {
211          throw new Exception("Line {$token->line_number}, char {$token->char_pos}: Bad key: {$token->id}");
212        }
213        $comments = $parser->comments_before($token);
214        if (!empty($comments)) {
215          if (!empty($this->comments)) {
216            $this->comments[] = "\n";
217          }
218          $this->comments = array_merge($this->comments, $comments);
219        }
220        $parser->advance();
221        $parser->advance(':');
222        $expression = $parser->expression();
223        if (is_array($expression)) {
224          $expression = $expression[0];
225        }
226        $expression->key = $token->value;
227        $values[] = $expression;
228        if (!$parser->peek(',')) {
229          break;
230        }
231        $token = $parser->token;
232        $parser->advance(',');
233        if ($parser->peek('}')) {
234          // Be lenient about trailing commas
235          break;
236        }
237      }
238    }
239
240    if ($parser->peek('}')) {
241      $this->comments = array_merge($this->comments, $parser->comments_before($parser->token));
242    }
243    $parser->advance('}');
244    $this->first = $values;
245    $this->arity = 'unary';
246    return $this;
247  }
248
249  public function std_curly($parser) {
250    $statements = $parser->statements(array('}'));
251    $parser->advance('}');
252    return $statements;
253  }
254
255  public function std_do($parser) {
256    if ($parser->peek('{')) {
257      $this->first = $this->block($parser);
258    }
259    else {
260      $this->first = $parser->expression($parser);
261      $parser->skip_terminators();
262    }
263    $parser->advance('while');
264    $parser->advance('(');
265    $this->second = $parser->expression();
266    $parser->advance(')');
267    $parser->skip_terminators();
268    return $this;
269  }
270
271  public function led_equals($parser, $left) {
272    $this->first = $left;
273    $this->second = $parser->expression(9);
274    $parser->scope->assignment($this->first, $this->second);
275    return $this;
276  }
277
278  public function std_for($parser) {
279    $parser->advance('(');
280
281    if($parser->peek('var')) {
282      $token = $parser->token;
283      $parser->advance('var');
284      $this->first = $token->std($parser);
285      if ($parser->peek('in')) {
286        $parser->advance('in');
287        $this->second = $parser->expression();
288      }
289    }
290    else {
291      // Don't forget that expressionless for(;;) loops are valid
292      $this->first = $parser->peek(';') ? NULL : $parser->statements(array(')', ';'));
293    }
294
295    if (!$parser->peek(')')) {
296      // var can possibly swallow the ;
297      if ($parser->peek(';')) {
298        $parser->advance(';');
299      }
300      $this->second = $parser->peek(';') ? NULL : $parser->statements(array(';'));
301      $parser->advance(';');
302      $this->thid = $parser->peek(')') ? NULL : $parser->statements(array(')'));
303    }
304    $parser->advance(')');
305    if ($parser->peek('{')) {
306      $this->block = $this->block($parser);
307    }
308    elseif (!$parser->peek(';')) {
309      $this->block = $parser->expression();
310    }
311
312    $parser->skip_terminators();
313
314    $this->arity = 'statement';
315    return $this;
316  }
317
318  public function nud_function($parser) {
319    $arguments = array();
320    $parser->new_scope();
321    $this->scope = $parser->scope;
322    if ($parser->token->arity == 'name') {
323      $parser->scope->define($parser->token);
324      $this->name = $parser->token->value;
325      $parser->advance();
326    }
327    $parser->advance('(');
328    if (!$parser->peek(')')) {
329      while (1) {
330        if ($parser->token->arity != 'name') {
331          throw new Exception('Expected a parameter name');
332        }
333        $parser->scope->define($parser->token);
334        $argument = $parser->token;
335        $parser->advance();
336        $argument->comments = array_merge($parser->comments_before($argument), $parser->comments_after($argument));
337        $arguments[] = $argument;
338        if (!$parser->peek(',')) {
339          break;
340        }
341        $parser->advance(',');
342      }
343    }
344
345    $this->first = $arguments;
346    $parser->advance(')');
347    $parser->peek('{');
348    $this->comments = $parser->comments_after($parser->token);
349    $parser->advance('{');
350    $this->second = $parser->statements(array('}'));
351    $parser->advance('}');
352    $this->arity = 'function';
353    $parser->scope_pop();
354
355    return $this;
356  }
357
358  public function std_if($parser) {
359    $parser->advance('(');
360    $this->first = $parser->expression();
361    $parser->advance(')');
362    if ($parser->peek('{')) {
363      $this->second = $this->block($parser);
364    }
365    elseif (!$parser->peek(';')) {
366      $this->second = $parser->expression();
367    }
368
369    $parser->skip_terminators();
370   
371    if ($parser->peek('else')) {
372      $parser->advance('else');
373      if ($parser->peek('if')) {
374        $this->third = $parser->statement;
375      }
376      elseif ($parser->peek('{')) {
377        $this->third = $this->block($parser);
378      }
379      elseif (!$parser->peek(';')) {
380        $this->third = $parser->expression();
381      }
382
383      $parser->skip_terminators();
384    }
385    else {
386      $this->third = NULL;
387    }
388    $this->arity = 'statement';
389    return $this;
390  }
391
392  public function executed_function($function, $parser) {
393    // The function gets executed
394    if ($parser->peek('(')) {
395      // led_parenthesis might have already swallowed it
396      $parser->advance('(');
397    }
398
399    $arguments = array();
400    if (!$parser->peek(')')) {
401      while (1) {
402        $arguments[] = $parser->expression();
403        if (!$parser->peek(',')) {
404          break;
405        }
406        $parser->advance(',');
407      }
408    }
409    $parser->advance(')');
410    $parser->skip_terminators();
411
412    // Make assignments within the function scope (in $function)
413    // between the arguments in the expression and the passed arguments
414    foreach ($function->first as $i => $parameter) {
415      if ($arguments[$i]) {
416        // The passed argument is assigned immediately to the matching parameter
417        $function->scope->assignment($parameter, $arguments[$i]);
418      }
419    }
420
421    $this->first = $function;
422    $this->second = $arguments;
423    $this->arity = 'execution';
424    return $this;
425  }
426
427  public function led_parenthesis($parser, $left) {
428    if ($left->id == 'function') {
429      return $this->executed_function($left, $parser);
430    }
431
432    if ($left->id == '{') {
433      $expression = $parser->expression();
434      $parser->advance(')');
435      return $expression;
436    }
437
438    if (!$parser->peek(')')) {
439      while (1) {
440        $arguments[] = $parser->expression();
441        if (!$parser->peek(',')) {
442          break;
443        }
444        $parser->advance(',');
445      }
446    }
447
448    if ($left->arity == 'operator' && $left->id != '.' && $left->id != '[' && count($arguments) == 1) {
449      $arguments = $arguments[0];
450    }
451
452    // e.g. foo(bar) has a foo first, [bar] second
453    $this->arity = 'binary';
454    $this->first = $left;
455    $this->second = $arguments;
456
457    $parser->advance(')');
458    return $this;
459  }
460
461  public function nud_parenthesis($parser) {
462    // '(' can mean function call, or executed function
463    $is_function = $parser->peek('function');
464    $expressions = array();
465    while (1) {
466      $expressions[] = $parser->expression();
467      if ($parser->peek(')')) {
468        break;
469      }
470      $parser->advance(',');
471    }
472    $parser->advance(')');
473
474    if ($is_function && $parser->peek('(')) {
475      return $this->executed_function($expressions[0], $parser);
476    }
477
478    return $expressions;
479  }
480
481  public function led_period($parser, $left) {
482    $this->first = $left;
483    if ($parser->token->arity != 'name') {
484      throw new Exception('Expected a property name');
485    }
486    $parser->token->arity = 'literal';
487    $this->second = $parser->token;
488    $this->arity ='binary';
489    $parser->advance();
490    return $this;
491  }
492
493  public function led_questionmark($parser, $left) {
494    $this->first = $left;
495    $this->second = $parser->expression();
496    $parser->advance(':');
497    $this->third = $parser->expression();
498    $this->arity = 'ternary';
499    return $this;
500  }
501
502  public function nud_this($parser) {
503    $parser->scope->reserve($this);
504    $this->arity = 'this';
505    return $this;
506  }
507
508  public function std_try($parser) {
509    $this->first = $this->block($parser);
510
511    if ($parser->peek('catch')) {
512      $parser->advance('catch');
513      $catch = $parser->new_symbol('catch');
514      $parser->advance('(');
515      $catch->first = $parser->expression();
516      $parser->advance(')');
517      $catch->second = $this->block($parser);
518
519      $this->second = $catch;
520    }
521
522    if ($parser->peek('finally')) {
523      $parser->advance('finally');
524      $this->third = $this->block($parser);
525    }
526
527    $parser->skip_terminators();
528
529    $this->arity = 'statement';
530
531    return $this;
532  }
533
534  public function std_return($parser) {
535    if (!$parser->peek("\n") && !$parser->peek(';') && !$parser->peek('}')) {
536      $this->first = $parser->expression();
537    }
538    $parser->skip_terminators();
539    $this->arity = 'statement';
540    return $this;
541  }
542
543  public function std_switch($parser) {
544    // switch statements can have multiple
545    // levels of passthrough and expressions
546    // need to be aggregated for each current
547    // case statement until a break is reached
548    $branches = array();
549
550    $parser->advance('(');
551    $this->first = $parser->expression();
552    $parser->advance(')');
553    $parser->advance('{');
554    $this->second = array();
555
556    $cases = array();
557    while (1) {
558      if ($parser->peek('}')) {
559        break;
560      }
561
562      if ($parser->peek('default')) {
563        $cases[] = $parser->token;
564        $switch = 'default';
565        $parser->advance('default');
566      }
567      else {
568        $cases[] = $parser->token;
569        $parser->advance('case');
570        $switch = 'case';
571        $cases[] = $parser->expression();
572      }
573
574      $parser->advance(':');
575      $statements = $parser->statements(array('default', 'case', '}'));
576
577      if ($switch == 'default') {
578        $default = $parser->new_symbol('default');
579        $default->first = $statements;
580        $cases[] = $default;
581      }
582      elseif ($switch == 'case' && !empty($statements)) {
583        $case = $parser->new_symbol('case');
584        $case->first = $statements;
585        $cases[] = $case;
586      }
587    }
588
589    $this->second = $cases;
590    $parser->advance('}');
591    $this->arity = 'statement';
592
593    return $this;
594  }
595
596  public function std_var($parser) {
597    $assignments = array();
598    while (1) {
599      $parser->peek();
600      $token = $parser->token;
601      if ($token->arity != 'name') {
602        throw new Exception("Line {$token->line_number}, char {$token->char_pos}: Expected a new variable name");
603      }
604      $parser->scope->define($token);
605      $parser->advance();
606      if ($parser->peek('=')) {
607        $t = $parser->token;
608        $parser->advance('=');
609        $t->first = $token;
610        $t->second = $parser->expression();
611        $parser->scope->assignment($t->first, $t->second);
612        $t->arity = 'binary';
613        $assignments[] = $t;
614      }
615      else {
616        $t = $parser->new_symbol('=');
617        $t->first = $token;
618        $t->second = NULL;
619        $assignments[] = $t;
620      }
621      if (!$parser->peek(',')) {
622        break;
623      }
624      $parser->advance(',');
625    }
626    $parser->skip_terminators();
627    return $assignments;
628  }
629
630  public function std_while($parser) {
631    $parser->advance('(');
632    $this->first = $parser->statements(array(')'));
633    $parser->advance(')');
634    if ($parser->peek('{')) {
635      $this->second = $this->block($parser);
636    }
637    else {
638      $this->second = $parser->expression();
639    }
640    $parser->skip_terminators();
641    $this->arity = 'statement';
642    return $this;
643  }
644}
Note: See TracBrowser for help on using the repository browser.