Skip to content

Commit f4378bb

Browse files
committed
Adding Value and ValueList nodes for IN clause
Signed-off-by: RJ Garcia <[email protected]>
1 parent c005ec3 commit f4378bb

13 files changed

+159
-41
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ OR
5757
OpExpression ::= Element "!=" OpExpression
5858
OpExpression ::= Element "<=" OpExpression
5959
OpExpression ::= Element ">=" OpExpression
60-
Element ::= string | number | IdExpression | "(" Expression ")"
60+
OpExpression ::= Element "in" "(" ValueList ")"
61+
Element ::= Value | IdExpression | "(" Expression ")"
62+
Value ::= string | number
63+
ValueList ::= Value | Value "," ValueList
6164
IdExpression ::= identifier | identifier "." IdExpression
6265

6366
string = "[^"]\*"

src/AST/ChainVisitor.php

+10
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,14 @@ public function visitOpExpression(OpExpression $node) {
3535
$visitor->visitOpExpression($node);
3636
}
3737
}
38+
public function visitValue(Value $node) {
39+
foreach ($this->visitors as $visitor) {
40+
$visitor->visitValue($node);
41+
}
42+
}
43+
public function visitValueList(ValueList $node) {
44+
foreach ($this->visitors as $visitor) {
45+
$visitor->visitValueList($node);
46+
}
47+
}
3848
}

src/AST/Element.php

+9-12
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,17 @@
44

55
class Element implements Node
66
{
7-
public $string;
8-
public $number;
7+
public $value;
98
public $id;
109
public $expr;
1110

1211
public function isValue() {
13-
return $this->string || $this->number;
12+
return $this->value;
1413
}
1514

16-
public static function string($string) {
15+
public static function value(Value $value) {
1716
$el = new self();
18-
$el->string = $string;
19-
return $el;
20-
}
21-
public static function number($number) {
22-
$el = new self();
23-
$el->number = $number;
17+
$el->value = $value;
2418
return $el;
2519
}
2620
public static function id(IdExpression $id) {
@@ -35,12 +29,15 @@ public static function expr(Expression $expr) {
3529
}
3630

3731
public function accept(Visitor $visitor) {
32+
$visitor->visitElement($this);
33+
34+
if ($this->value) {
35+
$this->value->accept($visitor);
36+
}
3837
if ($this->id) {
3938
$this->id->accept($visitor);
4039
} else if ($this->expr) {
4140
$this->expr->accept($visitor);
4241
}
43-
44-
return $visitor->visitElement($this);
4542
}
4643
}

src/AST/OpExpression.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ class OpExpression implements Node
77
public $left;
88
public $operator;
99
public $right;
10+
public $value_list;
1011

11-
public function __construct(Element $left, $operator = null, OpExpression $right = null) {
12+
public function __construct(Element $left, $operator = null, OpExpression $right = null, ValueList $value_list = null) {
1213
$this->left = $left;
1314
$this->operator = $operator;
1415
$this->right = $right;
16+
$this->value_list = $value_list;
1517
}
1618

1719
public function accept(Visitor $visitor) {
@@ -21,5 +23,8 @@ public function accept(Visitor $visitor) {
2123
if ($this->right) {
2224
$this->right->accept($visitor);
2325
}
26+
if ($this->value_list) {
27+
$this->value_list->accept($visitor);
28+
}
2429
}
2530
}

src/AST/Operators.php

+1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ final class Operators
1212
const OP_NEQ = '!=';
1313
const OP_AND = 'AND';
1414
const OP_OR = 'OR';
15+
const OP_IN = 'IN';
1516
}

src/AST/Value.php

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Krak\AQL\AST;
4+
5+
class Value implements Node
6+
{
7+
public $string;
8+
public $number;
9+
10+
public static function string($string) {
11+
$el = new self();
12+
$el->string = $string;
13+
return $el;
14+
}
15+
public static function number($number) {
16+
$el = new self();
17+
$el->number = $number;
18+
return $el;
19+
}
20+
21+
public function accept(Visitor $visitor) {
22+
return $visitor->visitValue($this);
23+
}
24+
}

src/AST/ValueList.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Krak\AQL\AST;
4+
5+
class ValueList implements Node
6+
{
7+
public $value;
8+
public $right;
9+
10+
public function __construct(Value $value, ValueList $right = null) {
11+
$this->value = $value;
12+
$this->right = $right;
13+
}
14+
15+
public function accept(Visitor $visitor) {
16+
return $visitor->visitValueList($this);
17+
}
18+
}

src/AST/Visitor.php

+2
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ public function visitElement(Element $node);
99
public function visitExpression(Expression $node);
1010
public function visitIdExpression(IdExpression $node);
1111
public function visitOpExpression(OpExpression $node);
12+
public function visitValue(Value $node);
13+
public function visitValueList(ValueList $node);
1214
}

src/Compiler/ExpressionCompiler.php

+22-6
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,17 @@ private function compileAndExpression(AST\AndExpression $expr) {
2929

3030
private function compileOpExpression(AST\OpExpression $expr) {
3131
$s = $this->compileElement($expr->left);
32-
if ($expr->operator) {
32+
if ($expr->right) {
3333
return $s . ' ' . $expr->operator . ' ' . $this->compileOpExpression($expr->right);
34+
} else if ($expr->value_list) {
35+
return $s . ' ' . $expr->operator . ' (' . $this->compileValueList($expr->value_list) . ')';
3436
}
3537
return $s;
3638
}
3739

3840
private function compileElement(AST\Element $el) {
39-
if ($el->string) {
40-
return $el->string->match;
41-
}
42-
if ($el->number) {
43-
return $el->number->match;
41+
if ($el->value) {
42+
return $this->compileValue($el->value);
4443
}
4544
if ($el->id) {
4645
return $this->compileIdExpression($el->id);
@@ -50,6 +49,23 @@ private function compileElement(AST\Element $el) {
5049
}
5150
}
5251

52+
private function compileValueList(AST\ValueList $list) {
53+
$s = $this->compileValue($list->value);
54+
if ($list->right) {
55+
return $s . ', ' . $this->compileValueList($list->right);
56+
}
57+
return $s;
58+
}
59+
60+
private function compileValue(AST\Value $value) {
61+
if ($value->string) {
62+
return $value->string->match;
63+
}
64+
if ($value->number) {
65+
return $value->number->match;
66+
}
67+
}
68+
5369
private function compileIdExpression(AST\IdExpression $expr) {
5470
$s = $expr->id->match;
5571
if ($expr->right) {

src/Parser/ExpressionParser.php

+38-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class ExpressionParser implements Parser
99
{
1010
const TOK_OR = 'or';
1111
const TOK_AND = 'and';
12+
const TOK_IN = 'in';
1213
const TOK_LT = '<';
1314
const TOK_LTE = '<=';
1415
const TOK_GT = '>';
@@ -21,6 +22,7 @@ class ExpressionParser implements Parser
2122
const TOK_RPAREN = ')';
2223
const TOK_ID = 'id';
2324
const TOK_DOT = '.';
25+
const TOK_COMMA = ',';
2426
const TOK_WS = 'whitespace';
2527

2628
private $lex;
@@ -68,6 +70,7 @@ public function parseOpExpression($stream) {
6870
$left = $this->parseElement($stream);
6971
$op = null;
7072
$right = null;
73+
$value_list = null;
7174

7275
if ($stream->isEmpty()) {
7376
return new AQL\AST\OpExpression($left);
@@ -106,21 +109,47 @@ public function parseOpExpression($stream) {
106109
$stream->getToken();
107110
$right = $this->parseOpExpression($stream);
108111
break;
112+
case self::TOK_IN:
113+
$op = AQL\AST\Operators::OP_IN;
114+
$stream->getToken();
115+
$this->expect($stream, self::TOK_LPAREN);
116+
$value_list = $this->parseValueList($stream);
117+
$this->expect($stream, self::TOK_RPAREN);
109118
}
110119

111-
return new AQL\AST\OpExpression($left, $op, $right);
120+
return new AQL\AST\OpExpression($left, $op, $right, $value_list);
112121
}
113122

114-
public function parseElement($stream) {
115-
$tok = $stream->peek();
123+
public function parseValueList($stream) {
124+
$value = $this->parseValue($stream);
125+
$right = null;
116126

117-
if ($tok->token == self::TOK_STRING) {
127+
if ($stream->peek()->token == self::TOK_COMMA) {
118128
$stream->getToken();
119-
return AQL\AST\Element::string($tok);
129+
$right = $this->parseValueList($stream);
130+
}
131+
132+
return new AQL\AST\ValueList($value, $right);
133+
}
134+
135+
public function parseValue($stream) {
136+
$tok = $stream->getToken();
137+
138+
if ($tok->token == self::TOK_STRING) {
139+
return AQL\AST\Value::string($tok);
120140
}
121141
if ($tok->token == self::TOK_NUMBER) {
122-
$stream->getToken();
123-
return AQL\AST\Element::number($tok);
142+
return AQL\AST\Value::number($tok);
143+
}
144+
145+
throw new ParseException('Expected value');
146+
}
147+
148+
public function parseElement($stream) {
149+
$tok = $stream->peek();
150+
151+
if ($tok->token == self::TOK_STRING || $tok->token == self::TOK_NUMBER) {
152+
return AQL\AST\Element::value($this->parseValue($stream));
124153
}
125154
if ($tok->token == self::TOK_LPAREN) {
126155
$stream->getToken();
@@ -157,6 +186,7 @@ public static function createLexer() {
157186
$lex = Lex\lexer([
158187
'/or(?![a-z])/iA' => self::TOK_OR,
159188
'/and(?![a-z])/iA' => self::TOK_AND,
189+
'/in(?![a-z])/iA' => self::TOK_IN,
160190
'/<=/A' => self::TOK_LTE,
161191
'/>=/A' => self::TOK_GTE,
162192
'/</A' => self::TOK_LT,
@@ -168,6 +198,7 @@ public static function createLexer() {
168198
'/"[^"]*"/A' => self::TOK_STRING,
169199
'/(\d*\.\d+|\d+)/A' => self::TOK_NUMBER,
170200
'/[_a-zA-Z][_a-zA-Z0-9]*/A' => self::TOK_ID,
201+
'/,/A' => self::TOK_COMMA,
171202
'/\./A' => self::TOK_DOT,
172203
'/\s+/A' => self::TOK_WS
173204
]);

src/SA/EnforceDomain.php

+6
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,10 @@ public function visitIdExpression(AST\IdExpression $node) {
6060
public function visitOpExpression(AST\OpExpression $node) {
6161
$this->last_node = $node;
6262
}
63+
public function visitValue(AST\Value $node) {
64+
$this->last_node = $node;
65+
}
66+
public function visitValueList(AST\ValueList $node) {
67+
$this->last_node = $node;
68+
}
6369
}

src/SA/EnforceSimpleExpressions.php

+17-14
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,26 @@ public function visitOpExpression(AST\OpExpression $node) {
2323
return;
2424
}
2525

26-
if (!$node->right) {
27-
throw new SAException('Expressions must have two sides');
28-
}
29-
30-
if ($node->right->right) {
31-
throw new SAException('Cannot chain operator expressions');
32-
}
33-
34-
$is_valid = ($node->left->id && $node->right->left->isValue()) ||
35-
($node->right->left->id && $node->left->isValue());
36-
37-
38-
$this->ignore = $node->right;
39-
if ($is_valid) {
26+
if ($node->right) {
27+
if ($node->right->right) {
28+
throw new SAException('Cannot chain operator expressions');
29+
}
30+
31+
$is_valid = ($node->left->id && $node->right->left->isValue()) ||
32+
($node->right->left->id && $node->left->isValue());
33+
34+
$this->ignore = $node->right;
35+
if ($is_valid) {
36+
return;
37+
}
38+
} else if ($node->value_list && $node->left->id) {
4039
return;
40+
} else {
41+
throw new SAException('Expressions must have two sides');
4142
}
4243

4344
throw new SAException('Expression must be between an identifier and value');
4445
}
46+
public function visitValue(AST\Value $node) {}
47+
public function visitValueList(AST\ValueList $node) {}
4548
}

src/Visitor/RenameIdVisitor.php

+2
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ public function visitIdExpression(AST\IdExpression $node) {
2727
}
2828
}
2929
public function visitOpExpression(AST\OpExpression $node) {}
30+
public function visitValue(AST\Value $node) {}
31+
public function visitValueList(AST\ValueList $node) {}
3032
}

0 commit comments

Comments
 (0)