Skip to content
9 changes: 2 additions & 7 deletions src/main/php/lang/ast/Compiled.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,9 @@ private static function language($name, $emitter) {
}

private static function parse($lang, $in, $version, $out, $file) {
$language= isset(self::$lang[$lang])
? self::$lang[$lang]
: self::$lang[$lang]= self::language($lang, self::$emit[$version])
;

$language= self::$lang[$lang] ?? self::$lang[$lang]= self::language($lang, self::$emit[$version]);
try {
self::$emit[$version]->emitAll(new Result($out), $language->parse(new Tokens($in, $file))->stream());
return $out;
return self::$emit[$version]->write($language->parse(new Tokens($in, $file))->stream(), $out);
} finally {
$in->close();
}
Expand Down
38 changes: 30 additions & 8 deletions src/main/php/lang/ast/Emitter.class.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php namespace lang\ast;

use io\streams\OutputStream;
use lang\ast\{Node, Error, Errors};
use lang\reflect\Package;
use lang\{IllegalArgumentException, IllegalStateException, ClassLoader, XPClass};
Expand Down Expand Up @@ -128,30 +129,51 @@ public function emitAll($result, $nodes) {
*/
public function emitOne($result, $node) {

// Inlined Result::at()
if ($node->line > $result->line) {
$result->out->write(str_repeat("\n", $node->line - $result->line));
$result->line= $node->line;
}

// Check for transformations
if (isset($this->transformations[$node->kind])) {
foreach ($this->transformations[$node->kind] as $transformation) {
$r= $transformation($result->codegen, $node);
if ($r instanceof Node) {
if ($r->kind === $node->kind) continue;
$this->{"emit{$r->kind}"}($result, $r);
$this->{'emit'.$r->kind}($result, $r);
return;
} else if ($r) {
foreach ($r as $n) {
$this->{"emit{$n->kind}"}($result, $n);
$this->{'emit'.$n->kind}($result, $n);
$result->out->write(';');
}
return;
}
}
// Fall through, use default
}

$this->{'emit'.$node->kind}($result, $node);
}

/**
* Creates result
*
* @param io.streams.OutputStream $target
* @return lang.ast.Result
*/
protected abstract function result($target);

/**
* Emitter entry point, takes nodes and emits them to the given target.
*
* @param iterable $nodes
* @param io.streams.OutputStream $target
* @return io.streams.OutputStream
* @throws lang.ast.Errors
*/
public function write($nodes, OutputStream $target) {
$result= $this->result($target);
try {
$this->emitAll($result, $nodes);
return $target;
} finally {
$result->close();
}
}
}
5 changes: 2 additions & 3 deletions src/main/php/lang/ast/emit/AttributesAsComments.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@ protected function emitAnnotations($result, $annotations) {
$line= $annotations->line;
$result->out->write('#[');

$out= $result->out->stream();
$result->out->redirect(new Escaping($out, ["\n" => " "]));
$result->out= new Escaping($result->out, ["\n" => " "]);
foreach ($annotations->named as $annotation) {
$this->emitOne($result, $annotation);
$result->out->write(',');
}
$result->out->redirect($out);
$result->out= $result->out->original();

$result->out->write("]\n");
$result->line= $line + 1;
Expand Down
4 changes: 4 additions & 0 deletions src/main/php/lang/ast/emit/Escaping.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public function flush() {
public function close() {
$this->target->close();
}

public function original() {
return $this->target;
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
<?php namespace lang\ast;
<?php namespace lang\ast\emit;

use lang\ast\emit\{Declaration, Reflection};

class Result {
public $out;
public $codegen;
class GeneratedCode extends Result {
private $prolog, $epilog;
public $line= 1;
public $meta= [];
public $locals= [];
public $stack= [];
public $type= [];

/**
* Starts a result stream, including a preamble
* Starts a result stream, including an optional prolog and epilog
*
* @param io.streams.Writer $out
* @param string $preamble
* @param io.streams.OutputStream $out
* @param string $prolog
* @param string $epilog
*/
public function __construct($out, $preamble= '<?php ') {
$this->out= $out;
$this->out->write($preamble);
$this->codegen= new CodeGen();
public function __construct($out, $prolog= '', $epilog= '') {
$this->prolog= $prolog;
$this->epilog= $epilog;
parent::__construct($out);
}

/**
* Creates a temporary variable and returns its name
* Initialize result. Guaranteed to be called *once* from constructor.
* Without implementation here - overwrite in subclasses.
*
* @return string
* @return void
*/
public function temp() {
return '$'.$this->codegen->symbol();
protected function initialize() {
'' === $this->prolog || $this->out->write($this->prolog);
}

/**
* Write epilog
*
* @return void
*/
protected function finalize() {
'' === $this->epilog || $this->out->write($this->epilog);
}

/**
Expand All @@ -46,6 +51,15 @@ public function at($line) {
return $this;
}

/**
* Creates a temporary variable and returns its name
*
* @return string
*/
public function temp() {
return '$'.$this->codegen->symbol();
}

/**
* Looks up a given type
*
Expand Down
28 changes: 24 additions & 4 deletions src/main/php/lang/ast/emit/PHP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@
Variable
};
use lang\ast\types\{IsUnion, IsFunction, IsArray, IsMap, IsNullable};
use lang\ast\{Emitter, Node, Type};
use lang\ast\{Emitter, Node, Type, Result};

abstract class PHP extends Emitter {
const PROPERTY = 0;
const METHOD = 1;

protected $literals= [];

/**
* Creates result
*
* @param io.streams.OutputStream $target
* @return lang.ast.Result
*/
protected function result($target) {
return new GeneratedCode($target, '<?php ');
}

/**
* Emit type literal or NULL if no type should be emitted
*
Expand Down Expand Up @@ -454,8 +464,7 @@ protected function emitAnnotation($result, $annotation) {

// Found first non-constant argument, enclose in `eval`
$result->out->write('(eval: \'');
$out= $result->out->stream();
$result->out->redirect(new Escaping($out, ["'" => "\\'", '\\' => '\\\\']));
$result->out= new Escaping($result->out, ["'" => "\\'", '\\' => '\\\\']);

// If exactly one unnamed argument exists, emit its value directly
if (1 === sizeof($annotation->arguments) && 0 === key($annotation->arguments)) {
Expand All @@ -470,7 +479,7 @@ protected function emitAnnotation($result, $annotation) {
$result->out->write(']');
}

$result->out->redirect($out);
$result->out= $result->out->original();
$result->out->write('\')');
return;
}
Expand Down Expand Up @@ -1026,4 +1035,15 @@ protected function emitFrom($result, $from) {
$result->out->write('yield from ');
$this->emitOne($result, $from->iterable);
}

/**
* Emit single nodes
*
* @param lang.ast.Result $result
* @param lang.ast.Node $node
* @return void
*/
public function emitOne($result, $node) {
parent::emitOne($result->at($node->line), $node);
}
}
53 changes: 53 additions & 0 deletions src/main/php/lang/ast/emit/Result.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php namespace lang\ast\emit;

use io\streams\OutputStream;
use lang\Closeable;
use lang\ast\CodeGen;

class Result implements Closeable {
public $out;
public $codegen;
public $meta= [];
public $locals= [];
public $stack= [];

/**
* Starts a result stream.
*
* @param io.streams.OutputStream $out
*/
public function __construct(OutputStream $out) {
$this->out= $out;
$this->codegen= new CodeGen();
$this->initialize();
}

/**
* Initialize result. Guaranteed to be called *once* from constructor.
* Without implementation here - overwrite in subclasses.
*
* @return void
*/
protected function initialize() {
// NOOP
}

/**
* Finalize result. Guaranteed to be called *once* from within `close()`.
* Without implementation here - overwrite in subclasses.
*
* @return void
*/
protected function finalize() {
// NOOP
}

/** @return void */
public function close() {
if (null === $this->out) return;

$this->finalize();
$this->out->close();
unset($this->out);
}
}
5 changes: 1 addition & 4 deletions src/main/php/xp/compiler/CompileRunner.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ public static function main(array $args) {
$file= $path->toString('/');
$t->start();
try {
$parse= $lang->parse(new Tokens($source, $file));
$target= $output->target((string)$path);
$emit->emitAll(new Result($target), $parse->stream());
$emit->write($lang->parse(new Tokens($source, $file))->stream(), $output->target((string)$path));

$t->stop();
$quiet || Console::$err->writeLinef('> %s (%.3f seconds)', $file, $t->elapsedTime());
Expand All @@ -121,7 +119,6 @@ public static function main(array $args) {
$total++;
$time+= $t->elapsedTime();
$source->close();
$target->close();
}
}

Expand Down
Loading