diff --git a/src/main/php/lang/ast/Emitter.class.php b/src/main/php/lang/ast/Emitter.class.php index ff35ac06..f532d13d 100755 --- a/src/main/php/lang/ast/Emitter.class.php +++ b/src/main/php/lang/ast/Emitter.class.php @@ -4,6 +4,7 @@ use lang\IllegalArgumentException; use io\streams\MemoryOutputStream; use io\streams\StringWriter; +use lang\ast\nodes\Value; abstract class Emitter { const PROPERTY = 0; @@ -110,7 +111,7 @@ protected function type($name) { * @return iterable */ protected function search($arg, $arity) { - if ($arg instanceof Node) { + if ($arg instanceof Node) { // TODO: Do we need this? if ($arg->arity === $arity) { yield $arg; } else { @@ -118,6 +119,12 @@ protected function search($arg, $arity) { yield $result; } } + } else if ($arg instanceof Value) { // TODO: Move recursion into Kind subclasses + foreach ((array)$arg as $node) { + foreach ($this->search($node, $arity) as $result) { + yield $result; + } + } } else if (is_array($arg)) { foreach ($arg as $node) { foreach ($this->search($node, $arity) as $result) { @@ -135,12 +142,6 @@ protected function returnType($name) { return $this->type($name); } - protected function catches($catch) { - $this->out->write('catch('.implode('|', $catch[0]).' $'.$catch[1].') {'); - $this->emit($catch[2]); - $this->out->write('}'); - } - protected function param($param) { if ($param[2] && $t= $this->paramType($param[2]->literal())) { $this->out->write($t.' '); @@ -184,40 +185,40 @@ private function annotations($list) { } } - protected function emitStart($node) { + protected function emitStart($start) { $this->out->write('out->write('namespace '.$node->value.";\n"); + protected function emitPackage($package) { + $this->out->write('namespace '.$package.";\n"); } - protected function emitImport($node) { - foreach ($node->value as $type => $alias) { + protected function emitImport($import) { + foreach ($import as $type => $alias) { $this->out->write('use '.$type.($alias ? ' as '.$alias : '').';'); } } - protected function emitAnnotation($node) { + protected function emitAnnotation($annotations) { // NOOP } - protected function emitLiteral($node) { - $this->out->write($node->value); + protected function emitLiteral($literal) { + $this->out->write($literal); } - protected function emitName($node) { - $this->out->write($node->value); + protected function emitName($name) { + $this->out->write($name); } - protected function emitBlock($node) { + protected function emitBlock($block) { $this->out->write('{'); - $this->emit($node->value); + $this->emit($block); $this->out->write('}'); } - protected function emitStatic($node) { - foreach ($node->value as $variable => $initial) { + protected function emitStatic($static) { + foreach ($static as $variable => $initial) { $this->out->write('static $'.$variable); if ($initial) { $this->out->write('='); @@ -227,36 +228,36 @@ protected function emitStatic($node) { } } - protected function emitVariable($node) { - $this->out->write('$'.$node->value); + protected function emitVariable($variable) { + $this->out->write('$'.$variable); } - protected function emitCast($node) { + protected function emitCast($cast) { static $native= ['string' => true, 'int' => true, 'float' => true, 'bool' => true, 'array' => true, 'object' => true]; - $name= $node->value[0]->name(); + $name= $cast->type->name(); if ('?' === $name{0}) { $this->out->write('cast('); - $this->emit($node->value[1]); + $this->emit($cast->expression); $this->out->write(',\''.$name.'\', false)'); } else if (isset($native[$name])) { - $this->out->write('('.$node->value[0]->literal().')'); - $this->emit($node->value[1]); + $this->out->write('('.$cast->type->literal().')'); + $this->emit($cast->expression); } else { $this->out->write('cast('); - $this->emit($node->value[1]); + $this->emit($cast->expression); $this->out->write(',\''.$name.'\')'); } } - protected function emitArray($node) { - if (empty($node->value)) { + protected function emitArray($array) { + if (empty($array)) { $this->out->write('[]'); return; } $unpack= false; - foreach ($node->value as $pair) { + foreach ($array as $pair) { if ('unpack' === $pair[1]->arity) { $unpack= true; break; @@ -265,7 +266,7 @@ protected function emitArray($node) { if ($unpack) { $this->out->write('array_merge(['); - foreach ($node->value as $pair) { + foreach ($array as $pair) { if ($pair[0]) { $this->emit($pair[0]); $this->out->write('=>'); @@ -289,7 +290,7 @@ protected function emitArray($node) { $this->out->write('])'); } else { $this->out->write('['); - foreach ($node->value as $pair) { + foreach ($array as $pair) { if ($pair[0]) { $this->emit($pair[0]); $this->out->write('=>'); @@ -301,73 +302,70 @@ protected function emitArray($node) { } } - // [$name, $signature, $statements] - protected function emitFunction($node) { - $this->out->write('function '.$node->value[0].'('); - $this->params($node->value[1][0]); + protected function emitFunction($function) { + $this->out->write('function '.$function->name.'('); + $this->params($function->signature[0]); $this->out->write(')'); - if ($t= $this->returnType($node->value[1][1])) { + if ($t= $this->returnType($function->signature[1])) { $this->out->write(':'.$t); } $this->out->write('{'); - $this->emit($node->value[2]); + $this->emit($function->body); $this->out->write('}'); } - // [$signature, $use, $statements] - protected function emitClosure($node) { + protected function emitClosure($closure) { $this->out->write('function('); - $this->params($node->value[0][0]); + $this->params($closure->signature[0]); $this->out->write(')'); - if ($node->value[0][1] && $t= $this->returnType($node->value[0][1]->literal())) { + if ($closure->signature[1] && $t= $this->returnType($closure->signature[1]->literal())) { $this->out->write(':'.$t); } - if (isset($node->value[1])) { - $this->out->write(' use('.implode(',', $node->value[1]).') '); + if ($closure->use) { + $this->out->write(' use('.implode(',', $closure->use).') '); } $this->out->write('{'); - $this->emit($node->value[2]); + $this->emit($closure->body); $this->out->write('}'); } - // [$signature, $expression] - protected function emitLambda($node) { + protected function emitLambda($lambda) { $this->out->write('function('); - $this->params($node->value[0][0]); + $this->params($lambda->signature[0]); $this->out->write(')'); - if ($node->value[0][1] && $t= $this->returnType($node->value[0][1]->literal())) { + if ($lambda->signature[1] && $t= $this->returnType($lambda->signature[1]->literal())) { $this->out->write(':'.$t); } $capture= []; - foreach ($this->search($node->value[1], 'variable') as $var) { + foreach ($this->search($lambda->body, 'variable') as $var) { $capture[$var->value]= true; } unset($capture['this']); - foreach ($node->value[0][0] as $param) { + foreach ($lambda->signature[0] as $param) { unset($capture[$param[0]]); } $capture && $this->out->write(' use($'.implode(', $', array_keys($capture)).')'); $this->out->write('{ return '); - $this->emit($node->value[1]); + $this->emit($lambda->body); $this->out->write('; }'); } - protected function emitClass($node) { + protected function emitClass($class) { array_unshift($this->meta, []); - $this->out->write(implode(' ', $node->value[1]).' class '.$this->declaration($node->value[0])); - $node->value[2] && $this->out->write(' extends '.$node->value[2]); - $node->value[3] && $this->out->write(' implements '.implode(', ', $node->value[3])); + $this->out->write(implode(' ', $class->modifiers).' class '.$this->declaration($class->name)); + $class->parent && $this->out->write(' extends '.$class->parent); + $class->implements && $this->out->write(' implements '.implode(', ', $class->implements)); $this->out->write('{'); - foreach ($node->value[4] as $member) { + foreach ($class->body as $member) { $this->emit($member); } $this->out->write('static function __init() {'); - $this->emitMeta($node->value[0], $node->value[5], $node->value[6]); - $this->out->write('}} '.$node->value[0].'::__init();'); + $this->emitMeta($class->name, $class->annotations, $class->comment); + $this->out->write('}} '.$class->name.'::__init();'); } protected function emitMeta($name, $annotations, $comment) { @@ -396,41 +394,41 @@ protected function emitMeta($name, $annotations, $comment) { $this->out->write('];'); } - protected function emitInterface($node) { + protected function emitInterface($interface) { array_unshift($this->meta, []); - $this->out->write('interface '.$this->declaration($node->value[0])); - $node->value[2] && $this->out->write(' extends '.implode(', ', $node->value[2])); + $this->out->write('interface '.$this->declaration($interface->name)); + $interface->parents && $this->out->write(' extends '.implode(', ', $interface->parents)); $this->out->write('{'); - foreach ($node->value[3] as $member) { + foreach ($interface->body as $member) { $this->emit($member); $this->out->write("\n"); } $this->out->write('}'); - $this->emitMeta($node->value[0], $node->value[4], $node->value[5]); + $this->emitMeta($interface->name, $interface->annotations, $interface->comment); } - protected function emitTrait($node) { + protected function emitTrait($trait) { array_unshift($this->meta, []); - $this->out->write('trait '.$this->declaration($node->value[0])); + $this->out->write('trait '.$this->declaration($trait->name)); $this->out->write('{'); - foreach ($node->value[2] as $member) { + foreach ($trait->body as $member) { $this->emit($member); $this->out->write("\n"); } $this->out->write('static function __init() {'); - $this->emitMeta($node->value[0], $node->value[3], $node->value[4]); - $this->out->write('}} '.$node->value[0].'::__init();'); + $this->emitMeta($trait->name, $trait->annotations, $trait->comment); + $this->out->write('}} '.$trait->name.'::__init();'); } - protected function emitUse($node) { - $this->out->write('use '.implode(',', $node->value[0])); - if ($node->value[1]) { + protected function emitUse($use) { + $this->out->write('use '.implode(',', $use->types)); + if ($use->aliases) { $this->out->write('{'); - foreach ($node->value[1] as $reference => $alias) { + foreach ($use->aliases as $reference => $alias) { $this->out->write($reference.' as '.$alias.';'); } $this->out->write('}'); @@ -439,41 +437,40 @@ protected function emitUse($node) { } } - protected function emitConst($node) { - $this->out->write(implode(' ', $node->value[1]).' const '.$node->value[0].'='); - $this->emit($node->value[2]); + protected function emitConst($const) { + $this->out->write(implode(' ', $const->modifiers).' const '.$const->name.'='); + $this->emit($const->expression); $this->out->write(';'); } - protected function emitProperty($node) { - $this->meta[0][self::PROPERTY][$node->value[0]]= [ - DETAIL_RETURNS => $node->value[3] ? $node->value[3]->name() : 'var', - DETAIL_ANNOTATIONS => $node->value[4] ? $node->value[4] : [], - DETAIL_COMMENT => $node->value[5], + protected function emitProperty($property) { + $this->meta[0][self::PROPERTY][$property->name]= [ + DETAIL_RETURNS => $property->type ? $property->type->name() : 'var', + DETAIL_ANNOTATIONS => $property->annotations ? $property->annotations : [], + DETAIL_COMMENT => $property->comment, DETAIL_TARGET_ANNO => [], DETAIL_ARGUMENTS => [] ]; - $this->out->write(implode(' ', $node->value[1]).' $'.$node->value[0]); - if (isset($node->value[2])) { + $this->out->write(implode(' ', $property->modifiers).' $'.$property->name); + if (isset($property->expression)) { $this->out->write('='); - $this->emit($node->value[2]); + $this->emit($property->expression); } $this->out->write(';'); } - // [$name, $modifiers, $signature, $annotations, $statements] - protected function emitMethod($node) { + protected function emitMethod($method) { $meta= [ - DETAIL_RETURNS => $node->value[2][1] ? $node->value[2][1]->name() : 'var', - DETAIL_ANNOTATIONS => isset($node->value[3]) ? $node->value[3] : [], - DETAIL_COMMENT => $node->value[5], + DETAIL_RETURNS => $method->signature[1] ? $method->signature[1]->name() : 'var', + DETAIL_ANNOTATIONS => isset($method->annotations) ? $method->annotations : [], + DETAIL_COMMENT => $method->comment, DETAIL_TARGET_ANNO => [], DETAIL_ARGUMENTS => [] ]; $declare= $promote= $params= ''; - foreach ($node->value[2][0] as $param) { + foreach ($method->signature[0] as $param) { if (isset($param[4])) { $declare.= $param[4].' $'.$param[0].';'; $promote.= '$this->'.$param[0].'= $'.$param[0].';'; @@ -489,278 +486,284 @@ protected function emitMethod($node) { $meta[DETAIL_ARGUMENTS][]= $param[2] ? $param[2]->name() : 'var'; } $this->out->write($declare); - $this->out->write(implode(' ', $node->value[1]).' function '.$node->value[0].'('); - $this->params($node->value[2][0]); + $this->out->write(implode(' ', $method->modifiers).' function '.$method->name.'('); + $this->params($method->signature[0]); $this->out->write(')'); - if ($node->value[2][1] && $t= $this->returnType($node->value[2][1]->literal())) { + if ($method->signature[1] && $t= $this->returnType($method->signature[1]->literal())) { $this->out->write(':'.$t); } - if (null === $node->value[4]) { + if (null === $method->body) { $this->out->write(';'); } else { $this->out->write(' {'.$promote); - $this->emit($node->value[4]); + $this->emit($method->body); $this->out->write('}'); } - $this->meta[0][self::METHOD][$node->value[0]]= $meta; + $this->meta[0][self::METHOD][$method->name]= $meta; } - protected function emitBraced($node) { + protected function emitBraced($braced) { $this->out->write('('); - $this->emit($node->value); + $this->emit($braced); $this->out->write(')'); } - protected function emitBinary($node) { - $this->emit($node->value[0]); - $this->out->write(' '.$node->symbol->id.' '); - $this->emit($node->value[1]); + protected function emitBinary($binary) { + $this->emit($binary->left); + $this->out->write(' '.$binary->operator.' '); + $this->emit($binary->right); } - protected function emitUnary($node) { - $this->out->write($node->symbol->id); - $this->emit($node->value); + protected function emitUnary($unary) { + $this->out->write($unary->operator); + $this->emit($unary->expression); } - protected function emitTernary($node) { - $this->emit($node->value[0]); + protected function emitTernary($ternary) { + $this->emit($ternary->condition); $this->out->write('?'); - $this->emit($node->value[1]); + $this->emit($ternary->expression); $this->out->write(':'); - $this->emit($node->value[2]); + $this->emit($ternary->otherwise); } - protected function emitOffset($node) { - $this->emit($node->value[0]); - if (null === $node->value[1]) { + protected function emitOffset($offset) { + $this->emit($offset->expression); + if (null === $offset->offset) { $this->out->write('[]'); } else { $this->out->write('['); - $this->emit($node->value[1]); + $this->emit($offset->offset); $this->out->write(']'); } } - protected function emitAssignment($node) { - $this->emit($node->value[0]); - $this->out->write($node->symbol->id); - $this->emit($node->value[1]); + protected function emitAssignment($assignment) { + $this->emit($assignment->variable); + $this->out->write($assignment->operator); + $this->emit($assignment->expression); } - protected function emitReturn($node) { + protected function emitReturn($return) { $this->out->write('return '); - $this->emit($node->value); + $this->emit($return); $this->out->write(';'); } - protected function emitIf($node) { + protected function emitIf($if) { $this->out->write('if ('); - $this->emit($node->value[0]); + $this->emit($if->expression); $this->out->write(') {'); - $this->emit($node->value[1]); + $this->emit($if->body); $this->out->write('}'); - if (isset($node->value[2])) { + if (isset($if->otherwise)) { $this->out->write('else {'); - $this->emit($node->value[2]); + $this->emit($if->otherwise); $this->out->write('}'); } } - protected function emitSwitch($node) { + protected function emitSwitch($switch) { $this->out->write('switch ('); - $this->emit($node->value[0]); + $this->emit($switch->expression); $this->out->write(') {'); - foreach ($node->value[1] as $case) { - if ($case[0]) { + foreach ($switch->cases as $case) { + if ($case->expression) { $this->out->write('case '); - $this->emit($case[0]); + $this->emit($case->expression); $this->out->write(':'); } else { $this->out->write('default:'); } - $this->emit($case[1]); + $this->emit($case->body); } $this->out->write('}'); } - protected function emitTry($node) { + protected function emitCatch($catch) { + $this->out->write('catch('.implode('|', $catch->types).' $'.$catch->variable.') {'); + $this->emit($catch->body); + $this->out->write('}'); + } + + protected function emitTry($try) { $this->out->write('try {'); - $this->emit($node->value[0]); + $this->emit($try->body); $this->out->write('}'); - if (isset($node->value[1])) { - foreach ($node->value[1] as $catch) { - $this->catches($catch); + if (isset($try->catches)) { + foreach ($try->catches as $catch) { + $this->emitCatch($catch); } } - if (isset($node->value[2])) { + if (isset($try->finally)) { $this->out->write('finally {'); - $this->emit($node->value[2]); + $this->emit($try->finally); $this->out->write('}'); } } - protected function emitThrow($node) { + protected function emitThrow($throw) { $this->out->write('throw '); - $this->emit($node->value); + $this->emit($throw); $this->out->write(';'); } - protected function emitForeach($node) { + protected function emitForeach($foreach) { $this->out->write('foreach ('); - $this->emit($node->value[0]); + $this->emit($foreach->expression); $this->out->write(' as '); - if ($node->value[1]) { - $this->emit($node->value[1]); + if ($foreach->ley) { + $this->emit($foreach->ley); $this->out->write(' => '); } - $this->emit($node->value[2]); + $this->emit($foreach->value); $this->out->write(')'); - if ('block' === $node->value[3]->arity) { + if ('block' === $foreach->body->arity) { $this->out->write('{'); - $this->emit($node->value[3]->value); + $this->emit($foreach->body->value); $this->out->write('}'); } else { - $this->emit($node->value[3]); + $this->emit($foreach->body); $this->out->write(';'); } } - protected function emitFor($node) { + protected function emitFor($for) { $this->out->write('for ('); - $this->arguments($node->value[0]); + $this->arguments($for->initialization); $this->out->write(';'); - $this->arguments($node->value[1]); + $this->arguments($for->condition); $this->out->write(';'); - $this->arguments($node->value[2]); + $this->arguments($for->loop); $this->out->write(')'); - if ('block' === $node->value[3]->arity) { + if ('block' === $for->body->arity) { $this->out->write('{'); - $this->emit($node->value[3]->value); + $this->emit($for->body->value); $this->out->write('}'); } else { - $this->emit($node->value[3]); + $this->emit($for->body); $this->out->write(';'); } } - protected function emitDo($node) { + protected function emitDo($do) { $this->out->write('do'); - if ('block' === $node->value[1]->arity) { + if ('block' === $do->body->arity) { $this->out->write('{'); - $this->emit($node->value[1]->value); + $this->emit($do->body->value); $this->out->write('}'); } else { - $this->emit($node->value[1]); + $this->emit($do->body); $this->out->write(';'); } $this->out->write('while ('); - $this->emit($node->value[0]); + $this->emit($do->expression); $this->out->write(');'); } - protected function emitWhile($node) { + protected function emitWhile($while) { $this->out->write('while ('); - $this->emit($node->value[0]); + $this->emit($while->expression); $this->out->write(')'); - if ('block' === $node->value[1]->arity) { + if ('block' === $while->body->arity) { $this->out->write('{'); - $this->emit($node->value[1]->value); + $this->emit($while->body->value); $this->out->write('}'); } else { - $this->emit($node->value[1]); + $this->emit($while->body); $this->out->write(';'); } } - protected function emitBreak($node) { + protected function emitBreak($break) { $this->out->write('break '); - $node->value && $this->emit($node->value); + $break && $this->emit($break); $this->out->write(';'); } - protected function emitContinue($node) { + protected function emitContinue($continue) { $this->out->write('continue '); - $node->value && $this->emit($node->value); + $continue && $this->emit($continue); $this->out->write(';'); } - protected function emitInstanceOf($node) { - $this->emit($node->value[0]); + protected function emitInstanceOf($instanceof) { + $this->emit($instanceof->expression); $this->out->write(' instanceof '); - if ($node->value[1] instanceof Node) { - $this->emit($node->value[1]); + if ($instanceof->type instanceof Node) { + $this->emit($instanceof->type); } else { - $this->out->write($node->value[1]); + $this->out->write($instanceof->type); } } - protected function emitNew($node) { - if (null === $node->value[0]) { + protected function emitNew($new) { + if ($new->type instanceof Value) { $this->out->write('new class('); - $this->arguments($node->value[1]); + $this->arguments($new->arguments); $this->out->write(')'); - $definition= $node->value[2]; - $definition[2] && $this->out->write(' extends '.$definition[2]); - $definition[3] && $this->out->write(' implements '.implode(', ', $definition[3])); + $definition= $new->type; + $definition->parent && $this->out->write(' extends '.$definition->parent); + $definition->implements && $this->out->write(' implements '.implode(', ', $definition->implements)); $this->out->write('{'); - foreach ($definition[4] as $member) { + foreach ($definition->body as $member) { $this->emit($member); $this->out->write("\n"); } $this->out->write('}'); } else { - $this->out->write('new '.$node->value[0].'('); - $this->arguments($node->value[1]); + $this->out->write('new '.$new->type.'('); + $this->arguments($new->arguments); $this->out->write(')'); } } - protected function emitInvoke($node) { - $this->emit($node->value[0]); + protected function emitInvoke($invoke) { + $this->emit($invoke->expression); $this->out->write('('); - $this->arguments($node->value[1]); + $this->arguments($invoke->arguments); $this->out->write(')'); } - protected function emitScope($node) { - $this->out->write($node->value[0].'::'); - $this->emit($node->value[1]); + protected function emitScope($scope) { + $this->out->write($scope->type.'::'); + $this->emit($scope->member); } - protected function emitInstance($node) { - if ('new' === $node->value[0]->arity) { + protected function emitInstance($instance) { + if ('new' === $instance->expression->arity) { $this->out->write('('); - $this->emit($node->value[0]); + $this->emit($instance->expression); $this->out->write(')->'); } else { - $this->emit($node->value[0]); + $this->emit($instance->expression); $this->out->write('->'); } - $this->emit($node->value[1]); + $this->emit($instance->member); } - protected function emitUnpack($node) { + protected function emitUnpack($unpack) { $this->out->write('...'); - $this->emit($node->value); + $this->emit($unpack); } - protected function emitYield($node) { + protected function emitYield($yield) { $this->out->write('yield '); - if ($node->value[0]) { - $this->emit($node->value[0]); + if ($yield->key) { + $this->emit($yield->key); $this->out->write('=>'); } - if ($node->value[1]) { - $this->emit($node->value[1]); + if ($yield->value) { + $this->emit($yield->value); } } - protected function emitFrom($node) { + protected function emitFrom($from) { $this->out->write('yield from '); - $this->emit($node->value); + $this->emit($from); } public function emit($arg) { @@ -769,14 +772,14 @@ public function emit($arg) { $this->out->write("\n"); $this->line++; } - $this->{'emit'.$arg->arity}($arg); + $this->{'emit'.$arg->arity}($arg->value); } else { foreach ($arg as $node) { while ($node->line > $this->line) { $this->out->write("\n"); $this->line++; } - $this->{'emit'.$node->arity}($node); + $this->{'emit'.$node->arity}($node->value); isset($node->symbol->std) || $this->out->write(';'); } } diff --git a/src/main/php/lang/ast/Parse.class.php b/src/main/php/lang/ast/Parse.class.php index 6ef6d08e..fe85d920 100755 --- a/src/main/php/lang/ast/Parse.class.php +++ b/src/main/php/lang/ast/Parse.class.php @@ -1,5 +1,37 @@ symbol('(end)'); $this->symbol('(name)'); - $this->symbol('(literal)')->nud= function($node) { return $node; }; - $this->symbol('(variable)')->nud= function($node) { return $node; }; + $this->symbol('(literal)'); + $this->symbol('(variable)'); $this->constant('true', 'true'); $this->constant('false', 'false'); @@ -62,10 +94,10 @@ private function setup() { $this->infix('instanceof', 60, function($node, $left) { if ('name' === $this->token->arity) { - $node->value= [$left, $this->scope->resolve($this->token->value)]; + $node->value= new InstanceOfValue($left, $this->scope->resolve($this->token->value)); $this->token= $this->advance(); } else { - $node->value= [$left, $this->expression(0)]; + $node->value= new InstanceOfValue($left, $this->expression(0)); } $node->arity= 'instanceof'; @@ -80,14 +112,14 @@ private function setup() { $expr= $this->token; } - $node->value= [$left, $expr]; + $node->value= new InstanceValue($left, $expr); $node->arity= 'instance'; $this->token= $this->advance(); return $node; }); $this->infix('::', 80, function($node, $left) { - $node->value= [$this->scope->resolve($left->value), $this->token]; + $node->value= new ScopeValue($this->scope->resolve($left->value), $this->token); $node->arity= 'scope'; $this->token= $this->advance(); return $node; @@ -95,7 +127,7 @@ private function setup() { $this->infix('==>', 80, function($node, $left) { $signature= [[[$left->value, false, null, false, false, null]], null]; - $node->value= [$signature, $this->expression(0)]; + $node->value= new LambdaValue($signature, $this->expression(0)); $node->arity= 'lambda'; return $node; }); @@ -103,7 +135,7 @@ private function setup() { $this->infix('(', 80, function($node, $left) { $arguments= $this->arguments(); $this->token= $this->expect(')'); - $node->value= [$left, $arguments]; + $node->value= new InvokeValue($left, $arguments); $node->arity= 'invoke'; return $node; }); @@ -116,7 +148,7 @@ private function setup() { } $this->token= $this->expect(']'); - $node->value= [$left, $expr]; + $node->value= new OffsetValue($left, $expr); $node->arity= 'offset'; return $node; }); @@ -125,7 +157,7 @@ private function setup() { $expr= $this->expression(0); $this->token= $this->expect('}'); - $node->value= [$left, $expr]; + $node->value= new OffsetValue($left, $expr); $node->arity= 'offset'; return $node; }); @@ -134,7 +166,7 @@ private function setup() { $when= $this->expression(0); $this->token= $this->expect(':'); $else= $this->expression(0); - $node->value= [$left, $when, $else]; + $node->value= new TernaryValue($left, $when, $else); $node->arity= 'ternary'; return $node; }); @@ -203,7 +235,7 @@ private function setup() { $this->token= $this->advance(); $signature= $this->signature(); $this->token= $this->advance(); - $node->value= [$signature, $this->expression(0)]; + $node->value= new LambdaValue($signature, $this->expression(0)); } else if ($cast && '(' === $this->token->value || 'operator' !== $this->token->arity) { $node->arity= 'cast'; @@ -211,7 +243,7 @@ private function setup() { $this->token= $this->expect('('); $type= $this->type0(false); $this->token= $this->expect(')'); - $node->value= [$type, $this->expression(0)]; + $node->value= new CastValue($type, $this->expression(0)); } else { $node->arity= 'braced'; @@ -263,11 +295,11 @@ private function setup() { // Anonymous classes $node->arity= 'new'; if ('variable' === $type->arity) { - $node->value= ['$'.$type->value, $arguments]; + $node->value= new NewValue('$'.$type->value, $arguments); } else if ('class' === $type->value) { - $node->value= [null, $arguments, $this->clazz(null)]; + $node->value= new NewValue($this->clazz(null), $arguments); } else { - $node->value= [$this->scope->resolve($type->value), $arguments]; + $node->value= new NewValue($this->scope->resolve($type->value), $arguments); } return $node; }); @@ -275,7 +307,7 @@ private function setup() { $this->prefix('yield', function($node) { if (';' === $this->token->symbol->id) { $node->arity= 'yield'; - $node->value= [null, null]; + $node->value= new YieldValue(null, null); } else if ('from' === $this->token->value) { $this->token= $this->advance(); $node->arity= 'from'; @@ -285,9 +317,9 @@ private function setup() { $expr= $this->expression(0); if ('=>' === $this->token->symbol->id) { $this->token= $this->advance(); - $node->value= [$expr, $this->expression(0)]; + $node->value= new YieldValue($expr, $this->expression(0)); } else { - $node->value= [null, $expr]; + $node->value= new YieldValue(null, $expr); } } return $node; @@ -331,7 +363,7 @@ private function setup() { $statements= $this->statements(); $this->token= $this->expect('}'); - $node->value= [$signature, $use, $statements]; + $node->value= new ClosureValue($signature, $use, $statements); } else { $node->arity= 'function'; $name= $this->token->value; @@ -353,7 +385,7 @@ private function setup() { $this->queue= [$this->token]; $this->token= new Node($this->symbol(';')); - $node->value= [$name, $signature, $statements]; + $node->value= new FunctionValue($name, $signature, $statements); } return $node; @@ -465,7 +497,7 @@ private function setup() { $otherwise= null; } - $node->value= [$condition, $when, $otherwise]; + $node->value= new IfValue($condition, $when, $otherwise); $node->arity= 'if'; return $node; }); @@ -481,47 +513,45 @@ private function setup() { if ('default' === $this->token->symbol->id) { $this->token= $this->advance(); $this->token= $this->expect(':'); - $cases[]= [null, []]; + $cases[]= new CaseValue(null, []); } else if ('case' === $this->token->symbol->id) { $this->token= $this->advance(); $expr= $this->expression(0); $this->token= $this->expect(':'); - $cases[]= [$expr, []]; + $cases[]= new CaseValue($expr, []); } else { - $cases[sizeof($cases) - 1][1][]= $this->statement(); + $cases[sizeof($cases) - 1]->body[]= $this->statement(); } }; $this->token= $this->expect('}'); - $node->value= [$condition, $cases]; + $node->value= new SwitchValue($condition, $cases); $node->arity= 'switch'; return $node; }); $this->stmt('break', function($node) { if (';' === $this->token->value) { - $expr= null; + $node->value= null; $this->token= $this->advance(); } else { - $expr= $this->expression(0); + $node->value= $this->expression(0); $this->token= $this->expect(';'); } - $node->value= $expr; $node->arity= 'break'; return $node; }); $this->stmt('continue', function($node) { if (';' === $this->token->value) { - $expr= null; + $node->value= null; $this->token= $this->advance(); } else { - $expr= $this->expression(0); + $node->value= $this->expression(0); $this->token= $this->expect(';'); } - $node->value= $expr; $node->arity= 'continue'; return $node; }); @@ -535,7 +565,7 @@ private function setup() { $this->token= $this->expect(')'); $this->token= $this->expect(';'); - $node->value= [$expression, $loop]; + $node->value= new DoValue($expression, $loop); $node->arity= 'do'; return $node; }); @@ -546,7 +576,7 @@ private function setup() { $this->token= $this->expect(')'); $loop= $this->statement(); - $node->value= [$expression, $loop]; + $node->value= new WhileValue($expression, $loop); $node->arity= 'while'; return $node; }); @@ -562,7 +592,7 @@ private function setup() { $stmt= $this->statement(); - $node->value= [$init, $cond, $loop, $stmt]; + $node->value= new ForValue($init, $cond, $loop, $stmt); $node->arity= 'for'; return $node; }); @@ -586,7 +616,7 @@ private function setup() { $this->token= $this->expect(')'); $loop= $this->statement(); - $node->value= [$expression, $key, $value, $loop]; + $node->value= new ForeachValue($expression, $key, $value, $loop); $node->arity= 'foreach'; return $node; }); @@ -621,7 +651,7 @@ private function setup() { $this->token= $this->expect(')'); $this->token= $this->expect('{'); - $catches[]= [$types, $variable->value, $this->statements()]; + $catches[]= new CatchValue($types, $variable->value, $this->statements()); $this->token= $this->expect('}'); } @@ -634,7 +664,7 @@ private function setup() { $finally= null; } - $node->value= [$statements, $catches, $finally]; + $node->value= new TryValue($statements, $catches, $finally); $node->arity= 'try'; return $node; }); @@ -732,7 +762,7 @@ private function setup() { $body= $this->body(); $this->token= $this->expect('}'); - $node->value= [$type, [], $parents, $body, $this->scope->annotations, $this->comment]; + $node->value= new InterfaceValue($type, [], $parents, $body, $this->scope->annotations, $this->comment); $node->arity= 'interface'; $this->scope->annotations= []; $this->comment= null; @@ -747,7 +777,7 @@ private function setup() { $body= $this->body(); $this->token= $this->expect('}'); - $node->value= [$type, [], $body, $this->scope->annotations, $this->comment]; + $node->value= new TraitValue($type, [], $body, $this->scope->annotations, $this->comment); $node->arity= 'trait'; $this->scope->annotations= []; $this->comment= null; @@ -947,7 +977,7 @@ private function clazz($name, $modifiers= []) { $body= $this->body(); $this->token= $this->expect('}'); - $return= [$name, $modifiers, $parent, $implements, $body, $this->scope->annotations, $comment]; + $return= new ClassValue($name, $modifiers, $parent, $implements, $body, $this->scope->annotations, $comment); $this->scope->annotations= []; return $return; } @@ -1024,7 +1054,7 @@ private function body() { $this->token= $this->expect(';'); } - $member->value= [$types, $aliases]; + $member->value= new UseValue($types, $aliases); $body[]= $member; } else if ('function' === $this->token->symbol->id) { $member= new Node($this->token->symbol); @@ -1053,7 +1083,7 @@ private function body() { $this->token= $this->expect('{, ; or ==>'); } - $member->value= [$name, $modifiers, $signature, $annotations, $statements, $this->comment]; + $member->value= new MethodValue($name, $modifiers, $signature, $annotations, $statements, $this->comment); $body[$name.'()']= $member; $modifiers= []; $annotations= null; @@ -1070,7 +1100,7 @@ private function body() { $this->token= $this->advance(); $this->token= $this->expect('='); - $member->value= [$name, $modifiers, $this->expression(0)]; + $member->value= new ConstValue($name, $modifiers, $this->expression(0)); $body[$name]= $member; if (',' === $this->token->symbol->id) { $this->token= $this->expect(','); @@ -1089,9 +1119,9 @@ private function body() { if ('=' === $this->token->symbol->id) { $this->token= $this->expect('='); - $member->value= [$name, $modifiers, $this->expression(0), $type, $annotations, $this->comment]; + $member->value= new PropertyValue($name, $modifiers, $this->expression(0), $type, $annotations, $this->comment); } else { - $member->value= [$name, $modifiers, null, $type, $annotations, $this->comment]; + $member->value= new PropertyValue($name, $modifiers, null, $type, $annotations, $this->comment); } $body['$'.$name]= $member; @@ -1220,7 +1250,7 @@ private function assignment($id) { $infix->led= function($node, $left) use($id) { $result= new Node($this->symbol($id)); $result->arity= 'assignment'; - $result->value= [$left, $this->expression(9)]; + $result->value= new AssignmentValue($left, $id, $this->expression(9)); return $result; }; return $infix; @@ -1228,8 +1258,8 @@ private function assignment($id) { private function infix($id, $bp, $led= null) { $infix= $this->symbol($id, $bp); - $infix->led= $led ?: function($node, $left) use($bp) { - $node->value= [$left, $this->expression($bp)]; + $infix->led= $led ?: function($node, $left) use($id, $bp) { + $node->value= new BinaryValue($left, $id, $this->expression($bp)); $node->arity= 'binary'; return $node; }; @@ -1238,8 +1268,8 @@ private function infix($id, $bp, $led= null) { private function infixr($id, $bp, $led= null) { $infix= $this->symbol($id, $bp); - $infix->led= $led ?: function($node, $left) use($bp) { - $node->value= [$left, $this->expression($bp - 1)]; + $infix->led= $led ?: function($node, $left) use($id, $bp) { + $node->value= new BinaryValue($left, $id, $this->expression($bp - 1)); $node->arity= 'binary'; return $node; }; @@ -1248,8 +1278,8 @@ private function infixr($id, $bp, $led= null) { private function prefix($id, $nud= null) { $prefix= $this->symbol($id); - $prefix->nud= $nud ?: function($node) { - $node->value= $this->expression(70); + $prefix->nud= $nud ?: function($node) use($id) { + $node->value= new UnaryValue($this->expression(70), $id); $node->arity= 'unary'; return $node; }; @@ -1258,8 +1288,8 @@ private function prefix($id, $nud= null) { private function suffix($id, $bp, $led= null) { $suffix= $this->symbol($id, $bp); - $suffix->led= $led ?: function($node, $left) { - $node->value= $left; + $suffix->led= $led ?: function($node, $left) use($id) { + $node->value= new UnaryValue($left, $id); $node->arity= 'unary'; return $node; }; diff --git a/src/main/php/lang/ast/emit/PHP56.class.php b/src/main/php/lang/ast/emit/PHP56.class.php index 7d1adad2..a06996f5 100755 --- a/src/main/php/lang/ast/emit/PHP56.class.php +++ b/src/main/php/lang/ast/emit/PHP56.class.php @@ -1,5 +1,7 @@ types); $label= sprintf('c%u', crc32($last)); - foreach ($catch[0] as $type) { - $this->out->write('catch('.$type.' $'.$catch[1].') { goto '.$label.'; }'); + foreach ($catch->types as $type) { + $this->out->write('catch('.$type.' $'.$catch->variable.') { goto '.$label.'; }'); } - $this->out->write('catch('.$last.' $'.$catch[1].') { '.$label.':'); - $this->emit($catch[2]); + $this->out->write('catch('.$last.' $'.$catch->variable.') { '.$label.':'); + $this->emit($catch->body); $this->out->write('}'); } - protected function emitConst($node) { - $this->out->write('const '.$node->value[0].'='); - $this->emit($node->value[2]); + protected function emitConst($const) { + $this->out->write('const '.$const->name.'='); + $this->emit($const->expression); $this->out->write(';'); } - protected function emitAssignment($node) { - if ('array' === $node->value[0]->arity) { + protected function emitAssignment($assignment) { + if ('array' === $assignment->variable->arity) { $this->out->write('list('); - foreach ($node->value[0]->value as $expr) { - $this->emit($expr[1]); + foreach ($assignment->variable->value as $pair) { + $this->emit($pair[1]); $this->out->write(','); } $this->out->write(')'); - $this->out->write($node->symbol->id); - $this->emit($node->value[1]); + $this->out->write($assignment->operator); + $this->emit($assignment->expression); } else { - parent::emitAssignment($node); + parent::emitAssignment($assignment); } } - protected function emitBinary($node) { - if ('??' === $node->symbol->id) { + protected function emitBinary($binary) { + if ('??' === $binary->operator) { $this->out->write('isset('); - $this->emit($node->value[0]); + $this->emit($binary->left); $this->out->write(') ?'); - $this->emit($node->value[0]); + $this->emit($binary->left); $this->out->write(' : '); - $this->emit($node->value[1]); - } else if ('<=>' === $node->symbol->id) { + $this->emit($binary->right); + } else if ('<=>' === $binary->operator) { $l= $this->temp(); $r= $this->temp(); $this->out->write('('.$l.'= '); - $this->emit($node->value[0]); + $this->emit($binary->left); $this->out->write(') < ('.$r.'='); - $this->emit($node->value[1]); + $this->emit($binary->right); $this->out->write(') ? -1 : ('.$l.' == '.$r.' ? 0 : 1)'); } else { - parent::emitBinary($node); + parent::emitBinary($binary); } } /** @see https://wiki.php.net/rfc/context_sensitive_lexer */ - protected function emitInvoke($node) { - $expr= $node->value[0]; + protected function emitInvoke($invoke) { + $expr= $invoke->expression; if ('braced' === $expr->arity) { $t= $this->temp(); $this->out->write('(('.$t.'='); $this->emit($expr->value); $this->out->write(') ? '.$t); $this->out->write('('); - $this->arguments($node->value[1]); + $this->arguments($invoke->arguments); $this->out->write(') : __error(E_RECOVERABLE_ERROR, "Function name must be a string", __FILE__, __LINE__))'); - } else if ('scope' === $expr->arity && 'name' === $expr->value[1]->arity && isset(self::$keywords[strtolower($expr->value[1]->value)])) { - $this->out->write($expr->value[0].'::{\''.$expr->value[1]->value.'\'}'); + } else if ( + 'scope' === $expr->arity && + 'name' === $expr->value->member->arity && + isset(self::$keywords[strtolower($expr->value->member->value)]) + ) { + $this->out->write($expr->value->type.'::{\''.$expr->value->member->value.'\'}'); $this->out->write('('); - $this->arguments($node->value[1]); + $this->arguments($invoke->arguments); $this->out->write(')'); } else { - parent::emitInvoke($node); + parent::emitInvoke($invoke); } } - protected function emitNew($node) { - if (null === $node->value[0]) { - $this->out->write('\\lang\\ClassLoader::defineType("class©anonymous'.md5($node->hashCode()).'", ["kind" => "class"'); - $definition= $node->value[2]; - $this->out->write(', "extends" => '.($definition[2] ? '[\''.$definition[2].'\']' : 'null')); - $this->out->write(', "implements" => '.($definition[3] ? '[\''.implode('\', \'', $definition[3]).'\']' : 'null')); + protected function emitNew($new) { + if ($new->type instanceof Value) { + $this->out->write('\\lang\\ClassLoader::defineType("class©anonymous'.md5(uniqid()).'", ["kind" => "class"'); + $definition= $new->type; + $this->out->write(', "extends" => '.($definition->parent ? '[\''.$definition->parent.'\']' : 'null')); + $this->out->write(', "implements" => '.($definition->implements ? '[\''.implode('\', \'', $definition->implements).'\']' : 'null')); $this->out->write(', "use" => []'); $this->out->write('], \'{'); $this->out->write(str_replace('\'', '\\\'', $this->buffer(function() use($definition) { - foreach ($definition[4] as $member) { + foreach ($definition->body as $member) { $this->emit($member); $this->out->write("\n"); } }))); $this->out->write('}\')->newInstance('); - $this->arguments($definition[1]); + $this->arguments($new->arguments); $this->out->write(')'); } else { - parent::emitNew($node); + parent::emitNew($new); } } - protected function emitFrom($node) { + protected function emitFrom($from) { $this->out->write('foreach ('); - $this->emit($node->value); + $this->emit($from); $this->out->write(' as $key => $val) yield $key => $val;'); } /** @see https://wiki.php.net/rfc/context_sensitive_lexer */ - protected function emitMethod($node) { - if (isset(self::$keywords[strtolower($node->value[0])])) { - $this->call[in_array('static', $node->value[1])][]= $node->value[0]; - $node->value[0]= '__'.$node->value[0]; - } else if ('__call' === $node->value[0] || '__callStatic' === $node->value[0]) { - $node->value[0].= '0'; + protected function emitMethod($method) { + if (isset(self::$keywords[strtolower($method->name)])) { + $this->call[in_array('static', $method->modifiers)][]= $method->name; + $method->name= '__'.$method->name; + } else if ('__call' === $method->name || '__callStatic' === $method->name) { + $method->name.= '0'; } - parent::emitMethod($node); + parent::emitMethod($method); } - protected function emitClass($node) { + protected function emitClass($class) { $this->call= [false => [], true => []]; array_unshift($this->meta, []); - $this->out->write(implode(' ', $node->value[1]).' class '.$this->declaration($node->value[0])); - $node->value[2] && $this->out->write(' extends '.$node->value[2]); - $node->value[3] && $this->out->write(' implements '.implode(', ', $node->value[3])); + $this->out->write(implode(' ', $class->modifiers).' class '.$this->declaration($class->name)); + $class->parent && $this->out->write(' extends '.$class->parent); + $class->implements && $this->out->write(' implements '.implode(', ', $class->implements)); $this->out->write('{'); - foreach ($node->value[4] as $member) { + foreach ($class->body as $member) { $this->emit($member); } @@ -231,7 +237,7 @@ protected function emitClass($node) { } $this->out->write('static function __init() {'); - $this->emitMeta($node->value[0], $node->value[5], $node->value[6]); - $this->out->write('}} '.$node->value[0].'::__init();'); + $this->emitMeta($class->name, $class->annotations, $class->comment); + $this->out->write('}} '.$class->name.'::__init();'); } } \ No newline at end of file diff --git a/src/main/php/lang/ast/emit/PHP70.class.php b/src/main/php/lang/ast/emit/PHP70.class.php index 68936e57..33996f5a 100755 --- a/src/main/php/lang/ast/emit/PHP70.class.php +++ b/src/main/php/lang/ast/emit/PHP70.class.php @@ -19,36 +19,36 @@ class PHP70 extends \lang\ast\Emitter { 'iterable' => 71 ]; - protected function catches($catch) { - $last= array_pop($catch[0]); + protected function emitCatch($catch) { + $last= array_pop($catch->types); $label= sprintf('c%u', crc32($last)); - foreach ($catch[0] as $type) { - $this->out->write('catch('.$type.' $'.$catch[1].') { goto '.$label.'; }'); + foreach ($catch->types as $type) { + $this->out->write('catch('.$type.' $'.$catch->variable.') { goto '.$label.'; }'); } - $this->out->write('catch('.$last.' $'.$catch[1].') { '.$label.':'); - $this->emit($catch[2]); + $this->out->write('catch('.$last.' $'.$catch->variable.') { '.$label.':'); + $this->emit($catch->body); $this->out->write('}'); } - protected function emitAssignment($node) { - if ('array' === $node->value[0]->arity) { + protected function emitAssignment($assignment) { + if ('array' === $assignment->variable->arity) { $this->out->write('list('); - foreach ($node->value[0]->value as $expr) { - $this->emit($expr[1]); + foreach ($assignment->variable->value as $pair) { + $this->emit($pair[1]); $this->out->write(','); } $this->out->write(')'); - $this->out->write($node->symbol->id); - $this->emit($node->value[1]); + $this->out->write($assignment->operator); + $this->emit($assignment->expression); } else { - parent::emitAssignment($node); + parent::emitAssignment($assignment); } } - protected function emitConst($node) { - $this->out->write('const '.$node->value[0].'='); - $this->emit($node->value[2]); + protected function emitConst($const) { + $this->out->write('const '.$const->name.'='); + $this->emit($const->expression); $this->out->write(';'); } } \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/AssignmentValue.class.php b/src/main/php/lang/ast/nodes/AssignmentValue.class.php new file mode 100755 index 00000000..bb763ed4 --- /dev/null +++ b/src/main/php/lang/ast/nodes/AssignmentValue.class.php @@ -0,0 +1,11 @@ +variable= $variable; + $this->operator= $operator; + $this->expression= $expression; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/BinaryValue.class.php b/src/main/php/lang/ast/nodes/BinaryValue.class.php new file mode 100755 index 00000000..f278c091 --- /dev/null +++ b/src/main/php/lang/ast/nodes/BinaryValue.class.php @@ -0,0 +1,11 @@ +left= $left; + $this->operator= $operator; + $this->right= $right; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/CaseValue.class.php b/src/main/php/lang/ast/nodes/CaseValue.class.php new file mode 100755 index 00000000..088affb6 --- /dev/null +++ b/src/main/php/lang/ast/nodes/CaseValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/CastValue.class.php b/src/main/php/lang/ast/nodes/CastValue.class.php new file mode 100755 index 00000000..91143a73 --- /dev/null +++ b/src/main/php/lang/ast/nodes/CastValue.class.php @@ -0,0 +1,10 @@ +type= $type; + $this->expression= $expression; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/CatchValue.class.php b/src/main/php/lang/ast/nodes/CatchValue.class.php new file mode 100755 index 00000000..0c1154f3 --- /dev/null +++ b/src/main/php/lang/ast/nodes/CatchValue.class.php @@ -0,0 +1,11 @@ +types= $types; + $this->variable= $variable; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ClassValue.class.php b/src/main/php/lang/ast/nodes/ClassValue.class.php new file mode 100755 index 00000000..507234eb --- /dev/null +++ b/src/main/php/lang/ast/nodes/ClassValue.class.php @@ -0,0 +1,15 @@ +name= $name; + $this->modifiers= $modifiers; + $this->parent= $parent; + $this->implements= $implements; + $this->body= $body; + $this->annotations= $annotations; + $this->comment= $comment; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ClosureValue.class.php b/src/main/php/lang/ast/nodes/ClosureValue.class.php new file mode 100755 index 00000000..db14dcfc --- /dev/null +++ b/src/main/php/lang/ast/nodes/ClosureValue.class.php @@ -0,0 +1,11 @@ +signature= $signature; + $this->use= $use; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ConstValue.class.php b/src/main/php/lang/ast/nodes/ConstValue.class.php new file mode 100755 index 00000000..155c7d95 --- /dev/null +++ b/src/main/php/lang/ast/nodes/ConstValue.class.php @@ -0,0 +1,11 @@ +name= $name; + $this->modifiers= $modifiers; + $this->expression= $expression; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/DoValue.class.php b/src/main/php/lang/ast/nodes/DoValue.class.php new file mode 100755 index 00000000..5e70d3fd --- /dev/null +++ b/src/main/php/lang/ast/nodes/DoValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ForValue.class.php b/src/main/php/lang/ast/nodes/ForValue.class.php new file mode 100755 index 00000000..97b57cd6 --- /dev/null +++ b/src/main/php/lang/ast/nodes/ForValue.class.php @@ -0,0 +1,12 @@ +initialization= $initialization; + $this->condition= $condition; + $this->loop= $loop; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ForeachValue.class.php b/src/main/php/lang/ast/nodes/ForeachValue.class.php new file mode 100755 index 00000000..2f5547f4 --- /dev/null +++ b/src/main/php/lang/ast/nodes/ForeachValue.class.php @@ -0,0 +1,12 @@ +expression= $expression; + $this->key= $key; + $this->value= $value; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/FunctionValue.class.php b/src/main/php/lang/ast/nodes/FunctionValue.class.php new file mode 100755 index 00000000..2387e64f --- /dev/null +++ b/src/main/php/lang/ast/nodes/FunctionValue.class.php @@ -0,0 +1,11 @@ +name= $name; + $this->signature= $signature; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/IfValue.class.php b/src/main/php/lang/ast/nodes/IfValue.class.php new file mode 100755 index 00000000..ef8b4c35 --- /dev/null +++ b/src/main/php/lang/ast/nodes/IfValue.class.php @@ -0,0 +1,11 @@ +expression= $expression; + $this->body= $body; + $this->otherwise= $otherwise; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/InstanceOfValue.class.php b/src/main/php/lang/ast/nodes/InstanceOfValue.class.php new file mode 100755 index 00000000..5203c464 --- /dev/null +++ b/src/main/php/lang/ast/nodes/InstanceOfValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->type= $type; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/InstanceValue.class.php b/src/main/php/lang/ast/nodes/InstanceValue.class.php new file mode 100755 index 00000000..9b87bee4 --- /dev/null +++ b/src/main/php/lang/ast/nodes/InstanceValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->member= $member; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/InterfaceValue.class.php b/src/main/php/lang/ast/nodes/InterfaceValue.class.php new file mode 100755 index 00000000..aabe77ad --- /dev/null +++ b/src/main/php/lang/ast/nodes/InterfaceValue.class.php @@ -0,0 +1,14 @@ +name= $name; + $this->modifiers= $modifiers; + $this->parents= $parents; + $this->body= $body; + $this->annotations= $annotations; + $this->comment= $comment; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/InvokeValue.class.php b/src/main/php/lang/ast/nodes/InvokeValue.class.php new file mode 100755 index 00000000..059bfc06 --- /dev/null +++ b/src/main/php/lang/ast/nodes/InvokeValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->arguments= $arguments; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/LambdaValue.class.php b/src/main/php/lang/ast/nodes/LambdaValue.class.php new file mode 100755 index 00000000..27181905 --- /dev/null +++ b/src/main/php/lang/ast/nodes/LambdaValue.class.php @@ -0,0 +1,10 @@ +signature= $signature; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/MethodValue.class.php b/src/main/php/lang/ast/nodes/MethodValue.class.php new file mode 100755 index 00000000..2eee3973 --- /dev/null +++ b/src/main/php/lang/ast/nodes/MethodValue.class.php @@ -0,0 +1,14 @@ +name= $name; + $this->modifiers= $modifiers; + $this->signature= $signature; + $this->annotations= $annotations; + $this->body= $body; + $this->comment= $comment; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/NewValue.class.php b/src/main/php/lang/ast/nodes/NewValue.class.php new file mode 100755 index 00000000..96edec67 --- /dev/null +++ b/src/main/php/lang/ast/nodes/NewValue.class.php @@ -0,0 +1,10 @@ +type= $type; + $this->arguments= $arguments; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/OffsetValue.class.php b/src/main/php/lang/ast/nodes/OffsetValue.class.php new file mode 100755 index 00000000..58cf2280 --- /dev/null +++ b/src/main/php/lang/ast/nodes/OffsetValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->offset= $offset; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/PropertyValue.class.php b/src/main/php/lang/ast/nodes/PropertyValue.class.php new file mode 100755 index 00000000..af5e8e1a --- /dev/null +++ b/src/main/php/lang/ast/nodes/PropertyValue.class.php @@ -0,0 +1,14 @@ +name= $name; + $this->modifiers= $modifiers; + $this->expression= $expression; + $this->type= $type; + $this->annotations= $annotations; + $this->comment= $comment; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/ScopeValue.class.php b/src/main/php/lang/ast/nodes/ScopeValue.class.php new file mode 100755 index 00000000..58e7cc29 --- /dev/null +++ b/src/main/php/lang/ast/nodes/ScopeValue.class.php @@ -0,0 +1,10 @@ +type= $type; + $this->member= $member; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/SwitchValue.class.php b/src/main/php/lang/ast/nodes/SwitchValue.class.php new file mode 100755 index 00000000..598f9756 --- /dev/null +++ b/src/main/php/lang/ast/nodes/SwitchValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->cases= $cases; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/TernaryValue.class.php b/src/main/php/lang/ast/nodes/TernaryValue.class.php new file mode 100755 index 00000000..01b9a3f8 --- /dev/null +++ b/src/main/php/lang/ast/nodes/TernaryValue.class.php @@ -0,0 +1,11 @@ +condition= $condition; + $this->expression= $expression; + $this->otherwise= $otherwise; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/TraitValue.class.php b/src/main/php/lang/ast/nodes/TraitValue.class.php new file mode 100755 index 00000000..d2c5d73f --- /dev/null +++ b/src/main/php/lang/ast/nodes/TraitValue.class.php @@ -0,0 +1,13 @@ +name= $name; + $this->modifiers= $modifiers; + $this->body= $body; + $this->annotations= $annotations; + $this->comment= $comment; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/TryValue.class.php b/src/main/php/lang/ast/nodes/TryValue.class.php new file mode 100755 index 00000000..33cfd0e8 --- /dev/null +++ b/src/main/php/lang/ast/nodes/TryValue.class.php @@ -0,0 +1,11 @@ +body= $body; + $this->catches= $catches; + $this->finally= $finally; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/UnaryValue.class.php b/src/main/php/lang/ast/nodes/UnaryValue.class.php new file mode 100755 index 00000000..b224a4a9 --- /dev/null +++ b/src/main/php/lang/ast/nodes/UnaryValue.class.php @@ -0,0 +1,10 @@ +expression= $expression; + $this->operator= $operator; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/UseValue.class.php b/src/main/php/lang/ast/nodes/UseValue.class.php new file mode 100755 index 00000000..1e91674b --- /dev/null +++ b/src/main/php/lang/ast/nodes/UseValue.class.php @@ -0,0 +1,10 @@ +types= $types; + $this->aliases= $aliases; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/Value.class.php b/src/main/php/lang/ast/nodes/Value.class.php new file mode 100755 index 00000000..6dade200 --- /dev/null +++ b/src/main/php/lang/ast/nodes/Value.class.php @@ -0,0 +1,5 @@ +expression= $expression; + $this->body= $body; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/nodes/YieldValue.class.php b/src/main/php/lang/ast/nodes/YieldValue.class.php new file mode 100755 index 00000000..28d0ff98 --- /dev/null +++ b/src/main/php/lang/ast/nodes/YieldValue.class.php @@ -0,0 +1,10 @@ +key= $key; + $this->value= $value; + } +} \ No newline at end of file diff --git a/src/test/php/lang/ast/unittest/emit/InvocationTest.class.php b/src/test/php/lang/ast/unittest/emit/InvocationTest.class.php index 2c513187..443a32a1 100755 --- a/src/test/php/lang/ast/unittest/emit/InvocationTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/InvocationTest.class.php @@ -72,4 +72,17 @@ public function run() { }' )); } + + #[@test] + public function global_function() { + $this->assertEquals('function', $this->run( + 'function fixture() { return "function"; } + class { + + public function run() { + return fixture(); + } + }' + )); + } } \ No newline at end of file diff --git a/src/test/php/lang/ast/unittest/parse/FunctionsTest.class.php b/src/test/php/lang/ast/unittest/parse/FunctionsTest.class.php index 07f77b2c..628a1176 100755 --- a/src/test/php/lang/ast/unittest/parse/FunctionsTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/FunctionsTest.class.php @@ -86,7 +86,7 @@ public function with_return_type() { #[@test] public function default_closure() { - $block= ['+' => [['(variable)' => 'a'], ['(literal)' => '1']]]; + $block= ['+' => [['(variable)' => 'a'], '+', ['(literal)' => '1']]]; $this->assertNodes( [['function' => [[[], null], null, [['return' => $block]]]]], $this->parse('function() { return $a + 1; };') @@ -95,7 +95,7 @@ public function default_closure() { #[@test] public function default_closure_with_use_by_value() { - $block= ['+' => [['(variable)' => 'a'], ['(literal)' => '1']]]; + $block= ['+' => [['(variable)' => 'a'], '+', ['(literal)' => '1']]]; $this->assertNodes( [['function' => [[[], null], ['$a', '$b'], [['return' => $block]]]]], $this->parse('function() use($a, $b) { return $a + 1; };') @@ -104,7 +104,7 @@ public function default_closure_with_use_by_value() { #[@test] public function default_closure_with_use_by_reference() { - $block= ['+' => [['(variable)' => 'a'], ['(literal)' => '1']]]; + $block= ['+' => [['(variable)' => 'a'], '+', ['(literal)' => '1']]]; $this->assertNodes( [['function' => [[[], null], ['$a', '&$b'], [['return' => $block]]]]], $this->parse('function() use($a, &$b) { return $a + 1; };') diff --git a/src/test/php/lang/ast/unittest/parse/LambdasTest.class.php b/src/test/php/lang/ast/unittest/parse/LambdasTest.class.php index 005ddd85..4964bbe6 100755 --- a/src/test/php/lang/ast/unittest/parse/LambdasTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/LambdasTest.class.php @@ -4,7 +4,7 @@ class LambdasTest extends ParseTest { #[@test] public function short_closure() { - $block= ['+' => [['(variable)' => 'a'], ['(literal)' => '1']]]; + $block= ['+' => [['(variable)' => 'a'], '+', ['(literal)' => '1']]]; $this->assertNodes( [['(' => [[[['a', false, null, false, null, null, []]], null], $block]]], $this->parse('($a) ==> $a + 1;') @@ -13,7 +13,7 @@ public function short_closure() { #[@test] public function short_closure_as_arg() { - $block= ['+' => [['(variable)' => 'a'], ['(literal)' => '1']]]; + $block= ['+' => [['(variable)' => 'a'], '+', ['(literal)' => '1']]]; $this->assertNodes( [['(' => [['exec' => 'exec'], [ ['(' => [[[['a', false, null, false, null, null, []]], null], $block]] diff --git a/src/test/php/lang/ast/unittest/parse/LoopsTest.class.php b/src/test/php/lang/ast/unittest/parse/LoopsTest.class.php index e31c3947..6f59b223 100755 --- a/src/test/php/lang/ast/unittest/parse/LoopsTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/LoopsTest.class.php @@ -51,9 +51,9 @@ public function foreach_value_without_curly_braces() { public function for_loop() { $this->assertNodes( [['for' => [ - [['=' => [['(variable)' => 'i'], ['(literal)' => '0']]]], - [['<' => [['(variable)' => 'i'], ['(literal)' => '10']]]], - [['++' => ['(variable)' => 'i']]], + [['=' => [['(variable)' => 'i'], '=', ['(literal)' => '0']]]], + [['<' => [['(variable)' => 'i'], '<', ['(literal)' => '10']]]], + [['++' => [['(variable)' => 'i'], '++']]], ['{' => [$this->block]] ]]], $this->parse('for ($i= 0; $i < 10; $i++) { loop(); }') diff --git a/src/test/php/lang/ast/unittest/parse/OperatorTest.class.php b/src/test/php/lang/ast/unittest/parse/OperatorTest.class.php index c2b672e9..298e1681 100755 --- a/src/test/php/lang/ast/unittest/parse/OperatorTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/OperatorTest.class.php @@ -10,7 +10,7 @@ class OperatorTest extends ParseTest { #])] public function binary($operator) { $this->assertNodes( - [[$operator => [['(variable)' => 'a'], ['(variable)' => 'b']]]], + [[$operator => [['(variable)' => 'a'], $operator, ['(variable)' => 'b']]]], $this->parse('$a '.$operator.' $b;') ); } @@ -30,7 +30,7 @@ public function ternary() { #])] public function comparison($operator) { $this->assertNodes( - [[$operator => [['(variable)' => 'a'], ['(variable)' => 'b']]]], + [[$operator => [['(variable)' => 'a'], $operator, ['(variable)' => 'b']]]], $this->parse('$a '.$operator.' $b;') ); } @@ -38,7 +38,7 @@ public function comparison($operator) { #[@test, @values(['++', '--'])] public function suffix($operator) { $this->assertNodes( - [[$operator => ['(variable)' => 'a']]], + [[$operator => [['(variable)' => 'a'], $operator]]], $this->parse('$a'.$operator.';') ); } @@ -46,7 +46,7 @@ public function suffix($operator) { #[@test, @values(['!', '~', '-', '+', '++', '--'])] public function prefix($operator) { $this->assertNodes( - [[$operator => ['(variable)' => 'a']]], + [[$operator => [['(variable)' => 'a'], $operator]]], $this->parse(''.$operator.'$a;') ); } @@ -59,7 +59,7 @@ public function prefix($operator) { #])] public function assignment($operator) { $this->assertNodes( - [[$operator => [['(variable)' => 'a'], ['(variable)' => 'b']]]], + [[$operator => [['(variable)' => 'a'], $operator, ['(variable)' => 'b']]]], $this->parse('$a '.$operator.' $b;') ); } @@ -67,7 +67,7 @@ public function assignment($operator) { #[@test] public function assignment_to_offset() { $this->assertNodes( - [['=' => [['[' => [['(variable)' => 'a'], ['(literal)' => '0']]], ['(literal)' => '1']]]], + [['=' => [['[' => [['(variable)' => 'a'], ['(literal)' => '0']]], '=', ['(literal)' => '1']]]], $this->parse('$a[0]= 1;') ); } @@ -75,7 +75,7 @@ public function assignment_to_offset() { #[@test] public function destructuring_assignment() { $this->assertNodes( - [['=' => [['[' => [[null, ['(variable)' => 'a']], [null, ['(variable)' => 'b']]]], ['(' => [['result' => 'result'], []]]]]], + [['=' => [['[' => [[null, ['(variable)' => 'a']], [null, ['(variable)' => 'b']]]], '=', ['(' => [['result' => 'result'], []]]]]], $this->parse('[$a, $b]= result();') ); } @@ -83,7 +83,7 @@ public function destructuring_assignment() { #[@test] public function comparison_to_assignment() { $this->assertNodes( - [['===' => [['(literal)' => '1'], ['(' => ['=' => [['(variable)' => 'a'], ['(literal)' => '1']]]]]]], + [['===' => [['(literal)' => '1'], '===', ['(' => ['=' => [['(variable)' => 'a'], '=', ['(literal)' => '1']]]]]]], $this->parse('1 === ($a= 1);') ); } @@ -91,7 +91,7 @@ public function comparison_to_assignment() { #[@test] public function append_array() { $this->assertNodes( - [['=' => [['[' => [['(variable)' => 'a'], null]], ['(literal)' => '1']]]], + [['=' => [['[' => [['(variable)' => 'a'], null]], '=', ['(literal)' => '1']]]], $this->parse('$a[]= 1;') ); } @@ -99,7 +99,7 @@ public function append_array() { #[@test] public function clone_expression() { $this->assertNodes( - [['clone' => ['(variable)' => 'a']]], + [['clone' => [['(variable)' => 'a'], 'clone']]], $this->parse('clone $a;') ); } @@ -107,7 +107,7 @@ public function clone_expression() { #[@test] public function error_suppression() { $this->assertNodes( - [['@' => ['(variable)' => 'a']]], + [['@' => [['(variable)' => 'a'], '@']]], $this->parse('@$a;') ); } @@ -115,7 +115,7 @@ public function error_suppression() { #[@test] public function reference() { $this->assertNodes( - [['&' => ['(variable)' => 'a']]], + [['&' => [['(variable)' => 'a'], '&']]], $this->parse('&$a;') ); } @@ -139,7 +139,7 @@ public function new_type_with_args() { #[@test] public function new_anonymous_extends() { $this->assertNodes( - [['new' => [null, [], [null, [], '\\T', [], [], [], null]]]], + [['new' => [[null, [], '\\T', [], [], [], null], []]]], $this->parse('new class() extends T { };') ); } @@ -147,7 +147,7 @@ public function new_anonymous_extends() { #[@test] public function new_anonymous_implements() { $this->assertNodes( - [['new' => [null, [], [null, [], null, ['\\A', '\\B'], [], [], null]]]], + [['new' => [[null, [], null, ['\\A', '\\B'], [], [], null], []]]], $this->parse('new class() implements A, B { };') ); } @@ -157,6 +157,7 @@ public function precedence_of_object_operator() { $this->assertNodes( [['.' => [ ['->' => [['(variable)' => 'this'], ['a' => 'a']]], + '.', ['(literal)' => '"test"'] ]]], $this->parse('$this->a."test";') @@ -168,6 +169,7 @@ public function precedence_of_scope_resolution_operator() { $this->assertNodes( [['.' => [ ['::' => ['self', ['class' => 'class']]], + '.', ['(literal)' => '"test"'] ]]], $this->parse('self::class."test";') diff --git a/src/test/php/lang/ast/unittest/parse/ParseTest.class.php b/src/test/php/lang/ast/unittest/parse/ParseTest.class.php index 72f6ff57..40fae598 100755 --- a/src/test/php/lang/ast/unittest/parse/ParseTest.class.php +++ b/src/test/php/lang/ast/unittest/parse/ParseTest.class.php @@ -4,6 +4,7 @@ use lang\ast\Parse; use lang\ast\Node; use lang\ast\Tokens; +use lang\ast\nodes\Value; abstract class ParseTest extends \unittest\TestCase { @@ -16,6 +17,12 @@ abstract class ParseTest extends \unittest\TestCase { private function value($arg) { if ($arg instanceof Node) { return [$arg->symbol->id => $this->value($arg->value)]; + } else if ($arg instanceof Value) { + $r= []; + foreach ((array)$arg as $key => $value) { + $r[]= $this->value($value); + } + return $r; } else if (is_array($arg)) { $r= []; foreach ($arg as $key => $value) {