Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/main/php/lang/ast/Visitor.class.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php namespace lang\ast;

use lang\ast\nodes\Literal;

/**
* Abstract base class for all visitors. Handles all node types without
* doing anything.
Expand Down Expand Up @@ -238,6 +240,46 @@ public function lambda($self) { }
*/
public function literal($self) { }

/**
* Visits strings
*
* @param lang.ast.Node $self
* @return var
*/
public function string($self) {
return $this->literal(new Literal($self->literal, $self->line));
}

/**
* Visits heredoc
*
* @param lang.ast.Node $self
* @return var
*/
public function heredoc($self) {
return $this->literal(new Literal($self->literal, $self->line));
}

/**
* Visits integers
*
* @param lang.ast.Node $self
* @return var
*/
public function integer($self) {
return $this->literal(new Literal($self->literal, $self->line));
}

/**
* Visits decimals
*
* @param lang.ast.Node $self
* @return var
*/
public function decimal($self) {
return $this->literal(new Literal($self->literal, $self->line));
}

/**
* Visits methods
*
Expand Down
16 changes: 16 additions & 0 deletions src/main/php/lang/ast/nodes/Scalar.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php namespace lang\ast\nodes;

use lang\ast\Node;

class Scalar extends Node {
public $literal;

public function __construct($literal, $kind, $line= -1) {
$this->literal= $literal;
$this->kind= $kind;
$this->line= $line;
}

/** @return string */
public function __toString() { return $this->literal; }
}
3 changes: 2 additions & 1 deletion src/main/php/lang/ast/syntax/PHP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
Placeholder,
Property,
ReturnStatement,
Scalar,
ScopeExpression,
Signature,
StaticLocals,
Expand Down Expand Up @@ -484,7 +485,7 @@ public function __construct() {
});

$this->prefix('(literal)', 0, function($parse, $token) {
return new Literal($token->value, $token->line);
return new Scalar($token->value, $token->kind, $token->line);
});

$this->prefix('(name)', 0, function($parse, $token) {
Expand Down
36 changes: 22 additions & 14 deletions src/test/php/lang/ast/unittest/parse/AttributesTest.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace lang\ast\unittest\parse;

use lang\ast\nodes\{Annotated, ArrayLiteral, Literal};
use lang\ast\nodes\{Annotated, ArrayLiteral, Literal, Scalar};
use test\{Assert, Test, Values};

/** @see https://wiki.php.net/rfc/shorter_attribute_syntax_change */
Expand All @@ -23,8 +23,8 @@ private function type($source) {
*/
private function attributes() {
yield ['#[Service]', ['Service' => []]];
yield ['#[Test(version: 1)]', ['Test' => ['version' => new Literal('1', self::LINE)]]];
yield ['#[Service, Version(1)]', ['Service' => [], 'Version' => [new Literal('1', self::LINE)]]];
yield ['#[Test(version: 1)]', ['Test' => ['version' => new Scalar('1', 'integer', self::LINE)]]];
yield ['#[Service, Version(1)]', ['Service' => [], 'Version' => [new Scalar('1', 'integer', self::LINE)]]];
}

/**
Expand Down Expand Up @@ -55,7 +55,15 @@ public function with_empty_arguments() {
);
}

#[Test, Values(['"test"', '1', '1.5', 'true', 'false', 'null'])]
#[Test, Values([['"test"', 'string'], ['1', 'integer'], ['1.5', 'decimal']])]
public function with_scalar_argument($value, $kind) {
$this->assertAnnotated(
['Service' => [new Scalar($value, $kind, self::LINE)]],
$this->type('#[Service('.$value.')] class T { }')
);
}

#[Test, Values(['true', 'false', 'null'])]
public function with_literal_argument($value) {
$this->assertAnnotated(
['Service' => [new Literal($value, self::LINE)]],
Expand All @@ -66,8 +74,8 @@ public function with_literal_argument($value) {
#[Test]
public function with_array_argument() {
$elements= [
[null, new Literal('1', self::LINE)],
[null, new Literal('2', self::LINE)],
[null, new Scalar('1', 'integer', self::LINE)],
[null, new Scalar('2', 'integer', self::LINE)],
];
$this->assertAnnotated(
['Service' => [new ArrayLiteral($elements, self::LINE)]],
Expand All @@ -78,8 +86,8 @@ public function with_array_argument() {
#[Test]
public function with_map_argument() {
$elements= [
[new Literal('"one"', self::LINE), new Literal('1', self::LINE)],
[new Literal('"two"', self::LINE), new Literal('2', self::LINE)],
[new Scalar('"one"', 'string', self::LINE), new Scalar('1', 'integer', self::LINE)],
[new Scalar('"two"', 'string', self::LINE), new Scalar('2', 'integer', self::LINE)],
];
$this->assertAnnotated(
['Service' => [new ArrayLiteral($elements, self::LINE)]],
Expand All @@ -90,16 +98,16 @@ public function with_map_argument() {
#[Test]
public function with_named_arguments() {
$this->assertAnnotated(
['Impl' => ['class' => new Literal('"T"', self::LINE), 'version' => new Literal('1', self::LINE)]],
['Impl' => ['class' => new Scalar('"T"', 'string', self::LINE), 'version' => new Scalar('1', 'integer', self::LINE)]],
$this->type('#[Impl(class: "T", version: 1)] class T { }')
);
}

#[Test]
public function multiline() {
$elements= [
[new Literal('"one"', self::LINE + 2), new Literal('1', self::LINE + 2)],
[new Literal('"two"', self::LINE + 3), new Literal('2', self::LINE + 3)],
[new Scalar('"one"', 'string', self::LINE + 2), new Scalar('1', 'integer', self::LINE + 2)],
[new Scalar('"two"', 'string', self::LINE + 3), new Scalar('2', 'integer', self::LINE + 3)],
];
$this->assertAnnotated(['Values' => [new ArrayLiteral($elements, self::LINE + 1)]], $this->type('
#[Values([
Expand Down Expand Up @@ -127,23 +135,23 @@ class T { }
#[Test]
public function with_two_arguments() {
$this->assertAnnotated(
['Service' => [new Literal('1', self::LINE), new Literal('2', self::LINE)]],
['Service' => [new Scalar('1', 'integer', self::LINE), new Scalar('2', 'integer', self::LINE)]],
$this->type('#[Service(1, 2)] class T { }')
);
}

#[Test]
public function two_annotations() {
$this->assertAnnotated(
['Author' => [new Literal('"Test"', self::LINE)], 'Version' => [new Literal('2', self::LINE)]],
['Author' => [new Scalar('"Test"', 'string', self::LINE)], 'Version' => [new Scalar('2', 'integer', self::LINE)]],
$this->type('#[Author("Test"), Version(2)] class T { }')
);
}

#[Test]
public function trailing_comma() {
$this->assertAnnotated(
['Author' => [new Literal('"Test"', self::LINE)]],
['Author' => [new Scalar('"Test"', 'string', self::LINE)]],
$this->type('#[Author("Test"), ] class T { }')
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/test/php/lang/ast/unittest/parse/ClosuresTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Literal,
Parameter,
ReturnStatement,
Scalar,
Signature,
Variable
};
Expand All @@ -25,7 +26,7 @@ public function returns() {
new BinaryExpression(
new Variable('a', self::LINE),
'+',
new Literal('1', self::LINE),
new Scalar('1', 'integer', self::LINE),
self::LINE
),
self::LINE
Expand Down
26 changes: 13 additions & 13 deletions src/test/php/lang/ast/unittest/parse/CommentTest.class.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
<?php namespace lang\ast\unittest\parse;

use lang\ast\nodes\{Annotations, Block, ClassDeclaration, Comment, Constant, Literal, Method, Property, Signature};
use lang\ast\nodes\{Annotations, Block, ClassDeclaration, Comment, Constant, Literal, Method, Property, Scalar, Signature};
use lang\ast\types\IsValue;
use test\{Assert, Test};

class CommentTest extends ParseTest {

#[Test]
public function oneline_double_slash() {
$this->assertParsed([new Literal('"test"', 3)], '
$this->assertParsed([new Scalar('"test"', 'string', 3)], '
// This is a comment
"test";
');
}

#[Test]
public function oneline_double_slash_at_end() {
$this->assertParsed([new Literal('"test"', 2)], '
$this->assertParsed([new Scalar('"test"', 'string', 2)], '
"test"; // This is a comment
');
}

#[Test]
public function two_oneline_double_slash() {
$this->assertParsed([new Literal('"test"', 4)], '
$this->assertParsed([new Scalar('"test"', 'string', 4)], '
// This is a comment
// This is another
"test";
Expand All @@ -32,22 +32,22 @@ public function two_oneline_double_slash() {

#[Test]
public function oneline_hashtag() {
$this->assertParsed([new Literal('"test"', 3)], '
$this->assertParsed([new Scalar('"test"', 'string', 3)], '
# This is a comment
"test";
');
}

#[Test]
public function oneline_hashtag_at_end() {
$this->assertParsed([new Literal('"test"', 2)], '
$this->assertParsed([new Scalar('"test"', 'string', 2)], '
"test"; # This is a comment
');
}

#[Test]
public function two_oneline_hashtags() {
$this->assertParsed([new Literal('"test"', 4)], '
$this->assertParsed([new Scalar('"test"', 'string', 4)], '
# This is a comment
# This is another
"test";
Expand All @@ -56,29 +56,29 @@ public function two_oneline_hashtags() {

#[Test]
public function oneline_slash_asterisk() {
$this->assertParsed([new Literal('"test"', 3)], '
$this->assertParsed([new Scalar('"test"', 'string', 3)], '
/* This is a comment */
"test";
');
}

#[Test]
public function oneline_slash_asterisk_at_end() {
$this->assertParsed([new Literal('"test"', 2)], '
$this->assertParsed([new Scalar('"test"', 'string', 2)], '
"test"; /* This is a comment */
');
}

#[Test]
public function oneline_slash_asterisk_inbetween() {
$this->assertParsed([new Literal('"before"', 2), new Literal('"after"', 2)], '
$this->assertParsed([new Scalar('"before"', 'string', 2), new Scalar('"after"', 'string', 2)], '
"before"; /* This is a comment */ "after";
');
}

#[Test]
public function multiline_slash_asterisk() {
$this->assertParsed([new Literal('"test"', 5)], '
$this->assertParsed([new Scalar('"test"', 'string', 5)], '
/* This is a comment
* spanning multiple lines.
*/
Expand All @@ -88,7 +88,7 @@ public function multiline_slash_asterisk() {

#[Test]
public function apidoc_comment_at_end_discarded() {
$this->assertParsed([new Literal('"test"', 2)], '
$this->assertParsed([new Scalar('"test"', 'string', 2)], '
"test"; /** Discarded */
');
}
Expand Down Expand Up @@ -120,7 +120,7 @@ class T { }
#[Test]
public function apidoc_comment_attached_to_next_constant() {
$class= new ClassDeclaration([], new IsValue('\\T'), null, [], [], null, null, 2);
$class->declare(new Constant(['public'], 'FIXTURE', null, new Literal('1', 4), null, new Comment('/** @api */', 3), 4));
$class->declare(new Constant(['public'], 'FIXTURE', null, new Scalar('1', 'integer', 4), null, new Comment('/** @api */', 3), 4));

$this->assertParsed([$class], '
class T {
Expand Down
27 changes: 19 additions & 8 deletions src/test/php/lang/ast/unittest/parse/ConditionalTest.class.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
<?php namespace lang\ast\unittest\parse;

use lang\ast\Errors;
use lang\ast\nodes\{CaseLabel, IfStatement, InvokeExpression, Literal, MatchCondition, MatchExpression, ScopeExpression, SwitchStatement, Variable};
use lang\ast\nodes\{
CaseLabel,
IfStatement,
InvokeExpression,
Literal,
MatchCondition,
MatchExpression,
Scalar,
ScopeExpression,
SwitchStatement,
Variable
};
use test\{Assert, Before, Expect, Test};

class ConditionalTest extends ParseTest {
Expand Down Expand Up @@ -57,7 +68,7 @@ public function empty_switch() {

#[Test]
public function switch_with_one_case() {
$cases= [new CaseLabel(new Literal('1', self::LINE), $this->blocks[1], self::LINE)];
$cases= [new CaseLabel(new Scalar('1', 'integer', self::LINE), $this->blocks[1], self::LINE)];
$this->assertParsed(
[new SwitchStatement(new Variable('condition', self::LINE), $cases, self::LINE)],
'switch ($condition) { case 1: action1(); }'
Expand Down Expand Up @@ -85,8 +96,8 @@ public function switch_with_class_constant() {
#[Test]
public function switch_with_two_cases() {
$cases= [
new CaseLabel(new Literal('1', self::LINE), $this->blocks[1], self::LINE),
new CaseLabel(new Literal('2', self::LINE), $this->blocks[2], self::LINE)
new CaseLabel(new Scalar('1', 'integer', self::LINE), $this->blocks[1], self::LINE),
new CaseLabel(new Scalar('2', 'integer', self::LINE), $this->blocks[2], self::LINE)
];
$this->assertParsed(
[new SwitchStatement(new Variable('condition', self::LINE), $cases, self::LINE)],
Expand Down Expand Up @@ -114,7 +125,7 @@ public function empty_match() {
#[Test]
public function match_with_trailing_comma() {
$cases= [
new MatchCondition([new Literal('1', self::LINE)], $this->blocks[1][0], self::LINE),
new MatchCondition([new Scalar('1', 'integer', self::LINE)], $this->blocks[1][0], self::LINE),
];
$this->assertParsed(
[new MatchExpression(new Variable('condition', self::LINE), $cases, null, self::LINE)],
Expand All @@ -125,8 +136,8 @@ public function match_with_trailing_comma() {
#[Test]
public function match_with_two_cases() {
$cases= [
new MatchCondition([new Literal('1', self::LINE)], $this->blocks[1][0], self::LINE),
new MatchCondition([new Literal('2', self::LINE)], $this->blocks[2][0], self::LINE)
new MatchCondition([new Scalar('1', 'integer', self::LINE)], $this->blocks[1][0], self::LINE),
new MatchCondition([new Scalar('2', 'integer', self::LINE)], $this->blocks[2][0], self::LINE)
];
$this->assertParsed(
[new MatchExpression(new Variable('condition', self::LINE), $cases, null, self::LINE)],
Expand All @@ -137,7 +148,7 @@ public function match_with_two_cases() {
#[Test]
public function match_with_multi_expression_case_and_default() {
$cases= [
new MatchCondition([new Literal('1', self::LINE), new Literal('2', self::LINE)], $this->blocks[1][0], self::LINE),
new MatchCondition([new Scalar('1', 'integer', self::LINE), new Scalar('2', 'integer', self::LINE)], $this->blocks[1][0], self::LINE),
];
$this->assertParsed(
[new MatchExpression(new Variable('condition', self::LINE), $cases, $this->blocks[2][0], self::LINE)],
Expand Down
Loading
Loading