Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 78e0f2d

Browse files
committedJun 26, 2021
Implement parser.
1 parent 1aa016b commit 78e0f2d

File tree

4 files changed

+2269
-0
lines changed

4 files changed

+2269
-0
lines changed
 

‎src/Parsing/Parser.php

Lines changed: 1070 additions & 0 deletions
Large diffs are not rendered by default.

‎src/Parsing/ParserState.php

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
<?php
2+
3+
namespace JamesWildDev\DBMLParser\Parsing;
4+
5+
/**
6+
* Values which identify the states of parsers.
7+
*/
8+
class ParserState
9+
{
10+
/**
11+
* No tokens are currently being aggregated.
12+
*/
13+
const BETWEEN_STATEMENTS = 0;
14+
15+
/**
16+
* The keyword "table" has been stated but nothing more.
17+
*/
18+
const TABLE = 1;
19+
20+
/**
21+
* The keyword "table" and the following name have been given.
22+
*/
23+
const TABLE_NAMED = 2;
24+
25+
/**
26+
* The alias of a table is next ("table {name} as").
27+
*/
28+
const TABLE_AS = 3;
29+
30+
/**
31+
* The next token should be the opening brace of a table.
32+
*/
33+
const TABLE_ALIASED = 4;
34+
35+
/**
36+
* Inside a table, but not parsing any particular thing yet.
37+
*/
38+
const TABLE_BODY = 5;
39+
40+
/**
41+
* A column has been named.
42+
*/
43+
const COLUMN_NAMED = 6;
44+
45+
/**
46+
* A column has been named and given a type.
47+
*/
48+
const COLUMN_TYPED = 7;
49+
50+
/**
51+
* The size of a column is expected next (following "(", the opening parenthesis of a column size).
52+
*/
53+
const COLUMN_BEFORE_SIZE = 8;
54+
55+
/**
56+
* The size of a column has been given and the closing parenthesis (")") is expected next.
57+
*/
58+
const COLUMN_SIZED = 9;
59+
60+
/**
61+
* The closing parenthesis (")") has been given following a column's size.
62+
*/
63+
const COLUMN_AFTER_SIZE = 10;
64+
65+
/**
66+
* Either a closing bracket ("]") or the start of a column attribute ("pk", "not null", etc.) is expected.
67+
*/
68+
const COLUMN_AFTER_COMMA = 11;
69+
70+
/**
71+
* The "default" attribute has been added to a column. Next is a semicolon.
72+
*/
73+
const COLUMN_DEFAULT = 12;
74+
75+
/**
76+
* The "default" attribute has been added to a column, and has been followed by a semicolon. Next is a string literal describing the default.
77+
*/
78+
const COLUMN_DEFAULT_SEMICOLON = 13;
79+
80+
/**
81+
* The "pk" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
82+
*/
83+
const COLUMN_AFTER_PRIMARY_KEY = 14;
84+
85+
/**
86+
* The "increment" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
87+
*/
88+
const COLUMN_AFTER_INCREMENT = 15;
89+
90+
/**
91+
* A constant "default" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
92+
*/
93+
const COLUMN_AFTER_CONSTANT_DEFAULT = 16;
94+
95+
/**
96+
* A calculated "default" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
97+
*/
98+
const COLUMN_AFTER_CALCULATED_DEFAULT = 17;
99+
100+
/**
101+
* The word "not" has been added to a column. Next is "null".
102+
*/
103+
const COLUMN_NOT = 18;
104+
105+
/**
106+
* A "not null" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
107+
*/
108+
const COLUMN_AFTER_NOT_NULL = 19;
109+
110+
/**
111+
* The word "note" has been added to a column. Next is a semicolon.
112+
*/
113+
const COLUMN_NOTE = 20;
114+
115+
/**
116+
* The word "note" and a semicolon have been added to a column. Next is a string literal.
117+
*/
118+
const COLUMN_NOTE_SEMICOLON = 21;
119+
120+
/**
121+
* The word "note", a semicolon and a string literal have been added to a column. Next is a closing bracket ("]") or a comma (",").
122+
*/
123+
const COLUMN_AFTER_NOTE = 22;
124+
125+
/**
126+
* The word "ref" has been added to a column. Next is a semicolon.
127+
*/
128+
const COLUMN_REF = 23;
129+
130+
/**
131+
* The word "ref" and a semicolon have been added to a column. Next is an operator.
132+
*/
133+
const COLUMN_REF_SEMICOLON = 24;
134+
135+
/**
136+
* The word "ref", a semicolon and an operator have been added to a column. Next is a referenced table name.
137+
*/
138+
const COLUMN_REF_OPERATOR = 25;
139+
140+
/**
141+
* The word "ref", a semicolon, an operator and a referenced table name or alias have been added to a column. Next is a period between the referenced table and column names.
142+
*/
143+
const COLUMN_REF_REFERENCED_TABLE_NAME_OR_ALIAS = 26;
144+
145+
/**
146+
* The word "ref", a semicolon, an operator, a referenced table name or alias and a period have been added to a column. Next is a referenced column name.
147+
*/
148+
const COLUMN_REF_PERIOD = 27;
149+
150+
/**
151+
* The word "ref", a semicolon, an operator, a referenced table name or alias, a period and a referenced column name have been added to a column. Next is a closing bracket ("]") or a comma (",").
152+
*/
153+
const COLUMN_AFTER_REF = 28;
154+
155+
/**
156+
* The word "indexes" has been given in a table body. Next is an opening brace ("{").
157+
*/
158+
const INDEXES = 29;
159+
160+
/**
161+
* Inside an index block, but not parsing any particular index yet.
162+
*/
163+
const INDEXES_BODY = 30;
164+
165+
/**
166+
* The columns of an index have been given. Next is a closing brace ("}"), opening bracket ("["), identifier of another column to index, or an opening parenthesis (of a list of columns).
167+
*/
168+
const INDEX_COLUMNS = 31;
169+
170+
/**
171+
* An opening parenthesis ("(") for a multiple-column index has been given. Next is an identifier of the first column.
172+
*/
173+
const INDEX_OPENING_PARENTHESIS = 32;
174+
175+
/**
176+
* A name of a column to index has been given. Next is a comma (",") or a closing parenthesis (")").
177+
*/
178+
const INDEX_MULTIPLE_NAME = 33;
179+
180+
/**
181+
* Between attributes of an index.
182+
*/
183+
const INDEX_AFTER_COMMA = 34;
184+
185+
/**
186+
* The "unique" attribute has been added to an index. Next is a comma (",") or a closing bracket ("]").
187+
*/
188+
const INDEX_AFTER_UNIQUE = 35;
189+
190+
/**
191+
* The word "name" has been added to an index. Next is a semicolon (":").
192+
*/
193+
const INDEX_NAME = 36;
194+
195+
/**
196+
* The word "name" and a semicolon (":") have been added to an index. Next is a name (a string literal).
197+
*/
198+
const INDEX_NAME_SEMICOLON = 37;
199+
200+
/**
201+
* A "name" attribute has been added to an index. Next is a comma (",") or a closing bracket ("]").
202+
*/
203+
const INDEX_AFTER_NAME = 38;
204+
205+
/**
206+
* The "unique" attribute has been added to a column. Next is a closing bracket ("]") or a comma (",").
207+
*/
208+
const COLUMN_AFTER_UNIQUE = 39;
209+
210+
/**
211+
* The word "note" has been added to a table. Next is a semicolon (":") or an opening brace ("{").
212+
*/
213+
const TABLE_NOTE = 40;
214+
215+
/**
216+
* The word "note" and a semicolon have been added to a table. Next is a string literal.
217+
*/
218+
const TABLE_NOTE_SEMICOLON = 41;
219+
220+
/**
221+
* The word "ref" has been added. Next is a semicolon.
222+
*/
223+
const REF = 42;
224+
225+
/**
226+
* The word "ref" and a semicolon have been added. Next is a referencing table name.
227+
*/
228+
const REF_SEMICOLON = 43;
229+
230+
/**
231+
* The word "ref", a semicolon, an operator and a referencing table name or alias have been added. Next is a period between the referencing table and column names.
232+
*/
233+
const REF_REFERENCING_TABLE_NAME_OR_ALIAS = 44;
234+
235+
/**
236+
* The word "ref", a semicolon, a referencing table name or alias and a period have been added. Next is a referencing column name.
237+
*/
238+
const REF_REFERENCING_PERIOD = 45;
239+
240+
/**
241+
* The word "ref", a semicolon, a referencing table name or alias, a period and a referencing column name have been added. Next is an operator.
242+
*/
243+
const REF_REFERENCING_COLUMN_NAME = 46;
244+
245+
/**
246+
* The word "ref", a semicolon, a referencing table name or alias, a period, a referencing column name and an operator have been added. Next is a referenced table name.
247+
*/
248+
const REF_OPERATOR = 47;
249+
250+
/**
251+
* The word "ref", a semicolon, a referencing table name or alias, a period, a referencing column name, an operator and a referenced table name have been added. Next is a period between the referenced table and column names.
252+
*/
253+
const REF_REFERENCED_TABLE_NAME_OR_ALIAS = 48;
254+
255+
/**
256+
* The word "ref", a semicolon, a referencing table name or alias, a period, a referencing column name, an operator, a referenced table name and a period have been added. Next is a referenced column name.
257+
*/
258+
const REF_REFERENCED_PERIOD = 49;
259+
260+
/**
261+
* The word "enum" has been added. Next is the name of the enum.
262+
*/
263+
const ENUM = 50;
264+
265+
/**
266+
* The word "enum" and the following name have been given. Next is the opening brace. ("{").
267+
*/
268+
const ENUM_NAME = 51;
269+
270+
/**
271+
* Inside an enum, but not parsing any particular thing yet.
272+
*/
273+
const ENUM_BODY = 52;
274+
275+
/**
276+
* The name of an enum value has been given.
277+
*/
278+
const ENUM_VALUE_NAME = 53;
279+
280+
/**
281+
* The name of an enum value and an opening bracket have been given. Next is the word "note" or a closing bracket ("]").
282+
*/
283+
const ENUM_VALUE_OPENING_BRACKET = 54;
284+
285+
/**
286+
* The name of an enum value, an opening bracket and the word "note" have been given. Next is a semicolon (":").
287+
*/
288+
const ENUM_VALUE_NOTE = 55;
289+
290+
/**
291+
* The name of an enum value, an opening bracket, the word "note" and a semicolon have been given. Next is the content of the note.
292+
*/
293+
const ENUM_VALUE_NOTE_SEMICOLON = 56;
294+
295+
/**
296+
* The name of an enum value, an opening bracket, the word "note", a semicolon and the content of the note have been given. Next is the closing bracket ("]").
297+
*/
298+
const ENUM_VALUE_NOTE_CONTENT = 57;
299+
300+
/**
301+
* The word "note" and an opening brace have been added to a table. Next is a string literal.
302+
*/
303+
const TABLE_NOTE_OPENING_BRACE = 58;
304+
305+
/**
306+
* The word "note", an opening brace and a string literal have been added to a table. Next is a closing brace ("}").
307+
*/
308+
const TABLE_NOTE_BRACED_CONTENT = 59;
309+
310+
/**
311+
* The word "ref" and an opening brace have been added. Next is a referencing table name.
312+
*/
313+
const BRACED_REF = 60;
314+
315+
/**
316+
* The word "ref", an opening brace, an operator and a referencing table name or alias have been added. Next is a period between the referencing table and column names.
317+
*/
318+
const BRACED_REF_REFERENCING_TABLE_NAME_OR_ALIAS = 61;
319+
320+
/**
321+
* The word "ref", an opening brace, a referencing table name or alias and a period have been added. Next is a referencing column name.
322+
*/
323+
const BRACED_REF_REFERENCING_PERIOD = 62;
324+
325+
/**
326+
* The word "ref", an opening brace, a referencing table name or alias, a period and a referencing column name have been added. Next is an operator.
327+
*/
328+
const BRACED_REF_REFERENCING_COLUMN_NAME = 63;
329+
330+
/**
331+
* The word "ref", an opening brace, a referencing table name or alias, a period, a referencing column name and an operator have been added. Next is a referenced table name.
332+
*/
333+
const BRACED_REF_OPERATOR = 64;
334+
335+
/**
336+
* The word "ref", an opening brace, a referencing table name or alias, a period, a referencing column name, an operator and a referenced table name have been added. Next is a period between the referenced table and column names.
337+
*/
338+
const BRACED_REF_REFERENCED_TABLE_NAME_OR_ALIAS = 65;
339+
340+
/**
341+
* The word "ref", an opening brace, a referencing table name or alias, a period, a referencing column name, an operator, a referenced table name and a period have been added. Next is a referenced column name.
342+
*/
343+
const BRACED_REF_REFERENCED_PERIOD = 66;
344+
345+
/**
346+
* The word "ref", an opening brace, a referencing table name or alias, a period, a referencing column name, an operator, a referenced table name, a period and a referenced column name have been added. Next is a closing brace ("}").
347+
*/
348+
const BRACED_REF_REFERENCED_COLUMN_NAME = 67;
349+
}

‎src/Parsing/TokenIs.php

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
<?php
2+
3+
namespace JamesWildDev\DBMLParser\Parsing;
4+
5+
use JamesWildDev\DBMLParser\Tokenization\TokenType;
6+
use JamesWildDev\DBMLParser\Tokenization\TokenizerState;
7+
8+
/**
9+
* Helpers for identifying the classes of tokens.
10+
*/
11+
class TokenIs
12+
{
13+
/**
14+
* Determines whether a token is meaningful (not a comment or white space).
15+
* @param TokenType $type The type of the token.
16+
* @return bool True when the token is meaningful (not a comment or white space), otherwise, false.
17+
*/
18+
public static function meaningful($type)
19+
{
20+
return $type !== TokenType::WHITE_SPACE && $type !== TokenType::LINE_COMMENT && $type != TokenType::UNKNOWN;
21+
}
22+
23+
/**
24+
* Determines whether a token could be interpreted as an identifier (a bare word or a string literal).
25+
* @param TokenType $type The type of the token.
26+
* @return bool True when the token could be interpreted as an identifier (a bare word or a string literal), otherwise, false.
27+
*/
28+
public static function anIdentifier($type, $content)
29+
{
30+
return (
31+
$type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER
32+
&& ! self::anOpeningBrace($type, $content)
33+
&& ! self::aClosingBrace($type, $content)
34+
&& ! self::anOpeningParenthesis($type, $content)
35+
&& ! self::aClosingParenthesis($type, $content)
36+
&& ! self::anOpeningBracket($type, $content)
37+
&& ! self::aClosingBracket($type, $content)
38+
&& ! self::aSemicolon($type, $content)
39+
&& ! self::aLessThanSymbol($type, $content)
40+
&& ! self::aGreaterThanSymbol($type, $content)
41+
&& ! self::aHyphen($type, $content)
42+
&& ! self::aComma($type, $content)
43+
&& ! self::aPeriod($type, $content)
44+
) || $type === TokenType::STRING_LITERAL;
45+
}
46+
47+
/**
48+
* Determines whether a token means "as".
49+
* @param TokenType $type The type of the token.
50+
* @param string $content The content of the token.
51+
* @return bool True when the token means "as", otherwise, false.
52+
*/
53+
public static function isAs($type, $content)
54+
{
55+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'as';
56+
}
57+
58+
/**
59+
* Determines whether a token means "table".
60+
* @param TokenType $type The type of the token.
61+
* @param string $content The content of the token.
62+
* @return bool True when the token means "table", otherwise, false.
63+
*/
64+
public static function table($type, $content)
65+
{
66+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'table';
67+
}
68+
69+
/**
70+
* Determines whether a token means "enum".
71+
* @param TokenType $type The type of the token.
72+
* @param string $content The content of the token.
73+
* @return bool True when the token means "enum", otherwise, false.
74+
*/
75+
public static function enum($type, $content)
76+
{
77+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'enum';
78+
}
79+
80+
/**
81+
* Determines whether a token means "ref".
82+
* @param TokenType $type The type of the token.
83+
* @param string $content The content of the token.
84+
* @return bool True when the token means "ref", otherwise, false.
85+
*/
86+
public static function ref($type, $content)
87+
{
88+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'ref';
89+
}
90+
91+
/**
92+
* Determines whether a token means "unique".
93+
* @param TokenType $type The type of the token.
94+
* @param string $content The content of the token.
95+
* @return bool True when the token means "unique", otherwise, false.
96+
*/
97+
public static function unique($type, $content)
98+
{
99+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'unique';
100+
}
101+
102+
/**
103+
* Determines whether a token means "name".
104+
* @param TokenType $type The type of the token.
105+
* @param string $content The content of the token.
106+
* @return bool True when the token means "name", otherwise, false.
107+
*/
108+
public static function name($type, $content)
109+
{
110+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'name';
111+
}
112+
113+
/**
114+
* Determines whether a token is an opening brace ("{").
115+
* @param TokenType $type The type of the token.
116+
* @param string $content The content of the token.
117+
* @return bool True when the token is an opening brace ("{"), otherwise, false.
118+
*/
119+
public static function anOpeningBrace($type, $content)
120+
{
121+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '{';
122+
}
123+
124+
/**
125+
* Determines whether a token is a closing brace ("}").
126+
* @param TokenType $type The type of the token.
127+
* @param string $content The content of the token.
128+
* @return bool True when the token is a closing brace ("}"), otherwise, false.
129+
*/
130+
public static function aClosingBrace($type, $content)
131+
{
132+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '}';
133+
}
134+
135+
/**
136+
* Determines whether a token is an opening bracket ("[").
137+
* @param TokenType $type The type of the token.
138+
* @param string $content The content of the token.
139+
* @return bool True when the token is an opening brace ("["), otherwise, false.
140+
*/
141+
public static function anOpeningBracket($type, $content)
142+
{
143+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '[';
144+
}
145+
146+
/**
147+
* Determines whether a token is a closing bracket ("]").
148+
* @param TokenType $type The type of the token.
149+
* @param string $content The content of the token.
150+
* @return bool True when the token is a closing bracket ("]"), otherwise, false.
151+
*/
152+
public static function aClosingBracket($type, $content)
153+
{
154+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === ']';
155+
}
156+
157+
/**
158+
* Determines whether a token is an opening parenthesis ("(").
159+
* @param TokenType $type The type of the token.
160+
* @param string $content The content of the token.
161+
* @return bool True when the token is an opening parenthesis ("("), otherwise, false.
162+
*/
163+
public static function anOpeningParenthesis($type, $content)
164+
{
165+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '(';
166+
}
167+
168+
/**
169+
* Determines whether a token is a closing parenthesis (")").
170+
* @param TokenType $type The type of the token.
171+
* @param string $content The content of the token.
172+
* @return bool True when the token is a closing parenthesis (")"), otherwise, false.
173+
*/
174+
public static function aClosingParenthesis($type, $content)
175+
{
176+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === ')';
177+
}
178+
179+
/**
180+
* Determines whether a token is a semicolon (":").
181+
* @param TokenType $type The type of the token.
182+
* @param string $content The content of the token.
183+
* @return bool True when the token is a semicolon (":"), otherwise, false.
184+
*/
185+
public static function aSemicolon($type, $content)
186+
{
187+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === ':';
188+
}
189+
190+
/**
191+
* Determines whether a token is a less than symbol ("<").
192+
* @param TokenType $type The type of the token.
193+
* @param string $content The content of the token.
194+
* @return bool True when the token is a less than symbol ("<"), otherwise, false.
195+
*/
196+
public static function aLessThanSymbol($type, $content)
197+
{
198+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '<';
199+
}
200+
201+
/**
202+
* Determines whether a token is a greater than symbol (">").
203+
* @param TokenType $type The type of the token.
204+
* @param string $content The content of the token.
205+
* @return bool True when the token is a grater than symbol (">"), otherwise, false.
206+
*/
207+
public static function aGreaterThanSymbol($type, $content)
208+
{
209+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '>';
210+
}
211+
212+
/**
213+
* Determines whether a token is a hyphen ("-").
214+
* @param TokenType $type The type of the token.
215+
* @param string $content The content of the token.
216+
* @return bool True when the token is a hyphen ("-"), otherwise, false.
217+
*/
218+
public static function aHyphen($type, $content)
219+
{
220+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '-';
221+
}
222+
223+
/**
224+
* Determines whether a token is a comma (",").
225+
* @param TokenType $type The type of the token.
226+
* @param string $content The content of the token.
227+
* @return bool True when the token is a comma (","), otherwise, false.
228+
*/
229+
public static function aComma($type, $content)
230+
{
231+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === ',';
232+
}
233+
234+
/**
235+
* Determines whether a token is a period (".").
236+
* @param TokenType $type The type of the token.
237+
* @param string $content The content of the token.
238+
* @return bool True when the token is a period ("."), otherwise, false.
239+
*/
240+
public static function aPeriod($type, $content)
241+
{
242+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && $content === '.';
243+
}
244+
245+
/**
246+
* Determines whether a token means "primary key" ("pk").
247+
* @param TokenType $type The type of the token.
248+
* @param string $content The content of the token.
249+
* @return bool True when the token means "primary key" ("pk"), otherwise, false.
250+
*/
251+
public static function primaryKey($type, $content)
252+
{
253+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'pk';
254+
}
255+
256+
/**
257+
* Determines whether a token means "auto increment" ("increment").
258+
* @param TokenType $type The type of the token.
259+
* @param string $content The content of the token.
260+
* @return bool True when the token means "auto increment" ("increment"), otherwise, false.
261+
*/
262+
public static function increment($type, $content)
263+
{
264+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'increment';
265+
}
266+
267+
/**
268+
* Determines whether a token means "default".
269+
* @param TokenType $type The type of the token.
270+
* @param string $content The content of the token.
271+
* @return bool True when the token means "default", otherwise, false.
272+
*/
273+
public static function defaultKeyword($type, $content)
274+
{
275+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'default';
276+
}
277+
278+
/**
279+
* Determines whether a token means "not".
280+
* @param TokenType $type The type of the token.
281+
* @param string $content The content of the token.
282+
* @return bool True when the token means "not", otherwise, false.
283+
*/
284+
public static function not($type, $content)
285+
{
286+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'not';
287+
}
288+
289+
/**
290+
* Determines whether a token means "null".
291+
* @param TokenType $type The type of the token.
292+
* @param string $content The content of the token.
293+
* @return bool True when the token means "null", otherwise, false.
294+
*/
295+
public static function null($type, $content)
296+
{
297+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'null';
298+
}
299+
300+
/**
301+
* Determines whether a token means "note".
302+
* @param TokenType $type The type of the token.
303+
* @param string $content The content of the token.
304+
* @return bool True when the token means "note", otherwise, false.
305+
*/
306+
public static function note($type, $content)
307+
{
308+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'note';
309+
}
310+
311+
/**
312+
* Determines whether a token means "indexes".
313+
* @param TokenType $type The type of the token.
314+
* @param string $content The content of the token.
315+
* @return bool True when the token means "indexes", otherwise, false.
316+
*/
317+
public static function indexes($type, $content)
318+
{
319+
return $type === TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER && strtolower($content) === 'indexes';
320+
}
321+
322+
/**
323+
* Determines whether a token could be taken as a backtick string literal.
324+
* @param TokenType $type The type of the token.
325+
* @return bool True when the token could be taken as a backtick string literal, otherwise, false.
326+
*/
327+
public static function aBacktickStringLiteral($type)
328+
{
329+
return $type === TokenType::BACKTICK_STRING_LITERAL;
330+
}
331+
}

‎tests/Unit/Parsing/ParserTest.php

Lines changed: 519 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,519 @@
1+
<?php
2+
3+
namespace JamesWildDev\DBMLParser\Tests\Unit\Parsing;
4+
5+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnCalculatedDefaultEvent;
6+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnConstantDefaultEvent;
7+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnEvent;
8+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnIncrementEvent;
9+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnNoteEvent;
10+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnNotNullEvent;
11+
use JamesWildDev\DBMLParser\Parsing\Logging\ColumnPrimaryKeyEvent;
12+
use JamesWildDev\DBMLParser\Parsing\Logging\EndOfFileEvent;
13+
use JamesWildDev\DBMLParser\Parsing\Logging\EnumEvent;
14+
use JamesWildDev\DBMLParser\Parsing\Logging\EnumValueEvent;
15+
use JamesWildDev\DBMLParser\Parsing\Logging\EnumValueNoteEvent;
16+
use JamesWildDev\DBMLParser\Parsing\Logging\IndexEvent;
17+
use JamesWildDev\DBMLParser\Parsing\Logging\LogParserTarget;
18+
use JamesWildDev\DBMLParser\Parsing\Logging\RefEvent;
19+
use JamesWildDev\DBMLParser\Parsing\Logging\TableAliasEvent;
20+
use JamesWildDev\DBMLParser\Parsing\Logging\TableEvent;
21+
use JamesWildDev\DBMLParser\Parsing\Logging\TableNoteEvent;
22+
use JamesWildDev\DBMLParser\Parsing\Logging\UnknownEvent;
23+
use JamesWildDev\DBMLParser\Parsing\Parser;
24+
use JamesWildDev\DBMLParser\Parsing\RefOperator;
25+
use JamesWildDev\DBMLParser\Tokenization\TokenType;
26+
use JamesWildDev\DBMLParser\Tokenization\Tokenizer;
27+
use JamesWildDev\DBMLParser\Tokenization\Logging\TokenEvent;;
28+
use PHPUnit\Framework\TestCase;
29+
30+
final class ParserTest extends TestCase
31+
{
32+
public function test_empty_file()
33+
{
34+
$logParserTarget = new LogParserTarget();
35+
$parser = new Parser($logParserTarget);
36+
37+
$parser->endOfFile(1, 1);
38+
39+
$this->assertEquals([new EndOfFileEvent(true)], $logParserTarget->events);
40+
}
41+
42+
public function test_non_empty_file()
43+
{
44+
$logParserTarget = new LogParserTarget();
45+
$parser = new Parser($logParserTarget);
46+
$tokenizer = new Tokenizer($parser);
47+
48+
foreach (str_split("
49+
tABlE table_a_name as table_a_alias {
50+
column_a_name column_a_type
51+
column_b_name column_b_type(column_b_size)
52+
column_c_name column_c_type [pk]
53+
column_d_name column_d_type [increment]
54+
column_e_name column_e_type [default: 'column e default']
55+
column_f_name column_f_type [default: `column f default`]
56+
column_g_name column_g_type [not null] /
57+
column_h_name column_h_type [note: 'column h note']
58+
column_i_name column_i_type [ref: > column_i_ref_table.column_i_ref_column]
59+
column_j_name column_j_type [ref: < column_j_ref_table.column_j_ref_column] ]->
60+
column_k_name column_k_type [ref: - column_k_ref_table.column_k_ref_column]
61+
'column_l_name' column_l_type // Comments are ignored.
62+
column_m_name column_m_type [ref: > 'column_m_ref_table'.column_m_ref_column]
63+
column_n_name column_n_type [ref: > column_n_ref_table.'column_n_ref_column']
64+
column_o_name column_o_type [unique]
65+
column_p_name column_p_type [default: column_p_default]
66+
IndeXES {
67+
index_a_column_a
68+
(index_b_column_a)
69+
(index_c_column_a, index_c_column_b)
70+
(index_d_column_a, index_d_column_b, index_d_column_c)
71+
index_e_column_a [name: 'test index e name']
72+
index_f_column_a [unique]
73+
index_g_column_a [name: 'test index g name', unique]
74+
'index_h_column_a'
75+
('index_i_column_a', 'index_i_column_b', 'index_i_column_c')
76+
}
77+
NoTE: 'table_a_note'
78+
}
79+
80+
rEF: ref_a_table_a.ref_a_column_a > ref_a_table_b.ref_a_column_b
81+
REf: ref_b_table_a.ref_b_column_a < ref_b_table_b.ref_b_column_b
82+
ReF: ref_c_table_a.ref_c_column_a - ref_c_table_b.ref_c_column_b
83+
rEf: 'ref_d_table_a'.ref_d_column_a > ref_d_table_b.ref_d_column_b
84+
ref: ref_e_table_a.'ref_e_column_a' > ref_e_table_b.ref_e_column_b
85+
REF: ref_f_table_a.ref_f_column_a > 'ref_f_table_b'.ref_f_column_b
86+
ref: ref_g_table_a.ref_g_column_a > ref_g_table_b.'ref_g_column_b'
87+
88+
eNuM enum_a_name {
89+
enum_a_value_a
90+
'enum_a_value_b'
91+
enum_a_value_c [note: 'enum_a_value_c_note']
92+
}
93+
94+
ENum 'enum_b_name' {}
95+
96+
TABle 'table_b_name' {}
97+
tabLE table_c_name as 'table_c_alias' {
98+
NoTE { 'table_c_note' }
99+
}
100+
101+
rEF { ref_h_table_a.ref_h_column_a > ref_h_table_b.ref_h_column_b }
102+
REf { ref_i_table_a.ref_i_column_a < ref_i_table_b.ref_i_column_b }
103+
ReF { ref_j_table_a.ref_j_column_a - ref_j_table_b.ref_j_column_b }
104+
rEf { 'ref_k_table_a'.ref_k_column_a > ref_k_table_b.ref_k_column_b }
105+
ref { ref_l_table_a.'ref_l_column_a' > ref_l_table_b.ref_l_column_b }
106+
REF { ref_m_table_a.ref_m_column_a > 'ref_m_table_b'.ref_m_column_b }
107+
ref { ref_n_table_a.ref_n_column_a > ref_n_table_b.'ref_n_column_b' }
108+
109+
unexpected
110+
111+
table with_empty_column_attributes { name_of_column_with_empty_attributes type_of_column_with_empty_attributes [] }
112+
table with_all_attributes_in_one_column { name_of_column_with_all_attributes type_of_column_with_all_attributes [pk, increment, not null, note: 'example note', unique, default: 'example default', ref: > other_table_name.other_column_name, default: `other example default`, unique] }
113+
table which_tests_index_edge_cases {
114+
indexes {}
115+
indexes {(abc, def, ghi) [unique, name: 'example name']}
116+
}
117+
enum which_tests_edge_cases { example_value_name }
118+
table which_tests_column_edge_cases {
119+
example_column_name_a example_column_type_a(example_column_size_a) [note: example_column_note_a]
120+
example_column_name_b example_column_type_b
121+
}
122+
table which_tests_additional_column_edge_cases {
123+
example_column_name_a example_column_type_a(example_column_size_a)
124+
}
125+
") as $character) {
126+
$tokenizer->character($character);
127+
}
128+
$tokenizer->endOfFile();
129+
130+
$this->assertEquals([
131+
new TableEvent('table_a_name', 2, 13, 2, 24),
132+
new TableAliasEvent('table_a_name', 'table_a_alias', 2, 29, 2, 41),
133+
new ColumnEvent('table_a_name', 'column_a_name', 3, 9, 3, 21, 'column_a_type', null),
134+
new ColumnEvent('table_a_name', 'column_b_name', 4, 9, 4, 21, 'column_b_type', 'column_b_size'),
135+
new ColumnEvent('table_a_name', 'column_c_name', 5, 9, 5, 21, 'column_c_type', null),
136+
new ColumnPrimaryKeyEvent('table_a_name', 'column_c_name'),
137+
new ColumnEvent('table_a_name', 'column_d_name', 6, 9, 6, 21, 'column_d_type', null),
138+
new ColumnIncrementEvent('table_a_name', 'column_d_name'),
139+
new ColumnEvent('table_a_name', 'column_e_name', 7, 9, 7, 21, 'column_e_type', null),
140+
new ColumnConstantDefaultEvent('table_a_name', 'column_e_name', 'column e default', 7, 47, 7, 64),
141+
new ColumnEvent('table_a_name', 'column_f_name', 8, 9, 8, 21, 'column_f_type', null),
142+
new ColumnCalculatedDefaultEvent('table_a_name', 'column_f_name', 'column f default', 8, 47, 8, 64),
143+
new ColumnEvent('table_a_name', 'column_g_name', 9, 9, 9, 21, 'column_g_type', null),
144+
new ColumnNotNullEvent('table_a_name', 'column_g_name'),
145+
new ColumnEvent('table_a_name', 'column_h_name', 10, 9, 10, 21, 'column_h_type', null),
146+
new ColumnNoteEvent('table_a_name', 'column_h_name', 'column h note', 10, 44, 10, 58),
147+
new ColumnEvent('table_a_name', 'column_i_name', 11, 9, 11, 21, 'column_i_type', null),
148+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_i_name', 11, 9, 11, 21, RefOperator::MANY_TO_ONE, 'column_i_ref_table', 11, 45, 11, 62, 'column_i_ref_column', 11, 64, 11, 82),
149+
new ColumnEvent('table_a_name', 'column_j_name', 12, 9, 12, 21, 'column_j_type', null),
150+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_j_name', 12, 9, 12, 21, RefOperator::ONE_TO_MANY, 'column_j_ref_table', 12, 45, 12, 62, 'column_j_ref_column', 12, 64, 12, 82),
151+
new UnknownEvent([
152+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 85, 12, 85, ']', ']'),
153+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 86, 12, 86, '-', '-'),
154+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 87, 12, 87, '>', '>'),
155+
]),
156+
new ColumnEvent('table_a_name', 'column_k_name', 13, 9, 13, 21, 'column_k_type', null),
157+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_k_name', 13, 9, 13, 21, RefOperator::ONE_TO_ONE, 'column_k_ref_table', 13, 45, 13, 62, 'column_k_ref_column', 13, 64, 13, 82),
158+
new ColumnEvent('table_a_name', 'column_l_name', 14, 9, 14, 23, 'column_l_type', null),
159+
new ColumnEvent('table_a_name', 'column_m_name', 15, 9, 15, 21, 'column_m_type', null),
160+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_m_name', 15, 9, 15, 21, RefOperator::MANY_TO_ONE, 'column_m_ref_table', 15, 45, 15, 64, 'column_m_ref_column', 15, 66, 15, 84),
161+
new ColumnEvent('table_a_name', 'column_n_name', 16, 9, 16, 21, 'column_n_type', null),
162+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_n_name', 16, 9, 16, 21, RefOperator::MANY_TO_ONE, 'column_n_ref_table', 16, 45, 16, 62, 'column_n_ref_column', 16, 64, 16, 84),
163+
new ColumnEvent('table_a_name', 'column_o_name', 17, 9, 17, 21, 'column_o_type', null),
164+
new IndexEvent('table_a_name', [
165+
[
166+
'name' => 'column_o_name',
167+
'nameStartLine' => 17,
168+
'nameStartColumn' => 9,
169+
'nameEndLine' => 17,
170+
'nameEndColumn' => 21,
171+
],
172+
], null, null, null, null, null, true),
173+
new ColumnEvent('table_a_name', 'column_p_name', 18, 9, 18, 21, 'column_p_type', null),
174+
new ColumnConstantDefaultEvent('table_a_name', 'column_p_name', 'column_p_default', 18, 47, 18, 62),
175+
new IndexEvent('table_a_name', [
176+
[
177+
'name' => 'index_a_column_a',
178+
'nameStartLine' => 20,
179+
'nameStartColumn' => 11,
180+
'nameEndLine' => 20,
181+
'nameEndColumn' => 26,
182+
],
183+
], null, null, null, null, null, false),
184+
new IndexEvent('table_a_name', [
185+
[
186+
'name' => 'index_b_column_a',
187+
'nameStartLine' => 21,
188+
'nameStartColumn' => 12,
189+
'nameEndLine' => 21,
190+
'nameEndColumn' => 27,
191+
],
192+
], null, null, null, null, null, false),
193+
new IndexEvent('table_a_name', [
194+
[
195+
'name' => 'index_c_column_a',
196+
'nameStartLine' => 22,
197+
'nameStartColumn' => 12,
198+
'nameEndLine' => 22,
199+
'nameEndColumn' => 27,
200+
],
201+
[
202+
'name' => 'index_c_column_b',
203+
'nameStartLine' => 22,
204+
'nameStartColumn' => 30,
205+
'nameEndLine' => 22,
206+
'nameEndColumn' => 45,
207+
],
208+
], null, null, null, null, null, false),
209+
new IndexEvent('table_a_name', [
210+
[
211+
'name' => 'index_d_column_a',
212+
'nameStartLine' => 23,
213+
'nameStartColumn' => 12,
214+
'nameEndLine' => 23,
215+
'nameEndColumn' => 27,
216+
],
217+
[
218+
'name' => 'index_d_column_b',
219+
'nameStartLine' => 23,
220+
'nameStartColumn' => 30,
221+
'nameEndLine' => 23,
222+
'nameEndColumn' => 45,
223+
],
224+
[
225+
'name' => 'index_d_column_c',
226+
'nameStartLine' => 23,
227+
'nameStartColumn' => 48,
228+
'nameEndLine' => 23,
229+
'nameEndColumn' => 63,
230+
],
231+
], null, null, null, null, null, false),
232+
new IndexEvent('table_a_name', [
233+
[
234+
'name' => 'index_e_column_a',
235+
'nameStartLine' => 24,
236+
'nameStartColumn' => 11,
237+
'nameEndLine' => 24,
238+
'nameEndColumn' => 26,
239+
],
240+
], 'test index e name', 24, 35, 24, 53, false),
241+
new IndexEvent('table_a_name', [
242+
[
243+
'name' => 'index_f_column_a',
244+
'nameStartLine' => 25,
245+
'nameStartColumn' => 11,
246+
'nameEndLine' => 25,
247+
'nameEndColumn' => 26,
248+
],
249+
], null, null, null, null, null, true),
250+
new IndexEvent('table_a_name', [
251+
[
252+
'name' => 'index_g_column_a',
253+
'nameStartLine' => 26,
254+
'nameStartColumn' => 11,
255+
'nameEndLine' => 26,
256+
'nameEndColumn' => 26,
257+
],
258+
], 'test index g name', 26, 35, 26, 53, true),
259+
new IndexEvent('table_a_name', [
260+
[
261+
'name' => 'index_h_column_a',
262+
'nameStartLine' => 27,
263+
'nameStartColumn' => 11,
264+
'nameEndLine' => 27,
265+
'nameEndColumn' => 28,
266+
],
267+
], null, null, null, null, null, false),
268+
new IndexEvent('table_a_name', [
269+
[
270+
'name' => 'index_i_column_a',
271+
'nameStartLine' => 28,
272+
'nameStartColumn' => 12,
273+
'nameEndLine' => 28,
274+
'nameEndColumn' => 29,
275+
],
276+
[
277+
'name' => 'index_i_column_b',
278+
'nameStartLine' => 28,
279+
'nameStartColumn' => 32,
280+
'nameEndLine' => 28,
281+
'nameEndColumn' => 49,
282+
],
283+
[
284+
'name' => 'index_i_column_c',
285+
'nameStartLine' => 28,
286+
'nameStartColumn' => 52,
287+
'nameEndLine' => 28,
288+
'nameEndColumn' => 69,
289+
],
290+
], null, null, null, null, null, false),
291+
new TableNoteEvent('table_a_name', 'table_a_note', 30, 15, 30, 28),
292+
new RefEvent('ref_a_table_a', 33, 12, 33, 24, 'ref_a_column_a', 33, 26, 33, 39, RefOperator::MANY_TO_ONE, 'ref_a_table_b', 33, 43, 33, 55, 'ref_a_column_b', 33, 57, 33, 70),
293+
new RefEvent('ref_b_table_a', 34, 12, 34, 24, 'ref_b_column_a', 34, 26, 34, 39, RefOperator::ONE_TO_MANY, 'ref_b_table_b', 34, 43, 34, 55, 'ref_b_column_b', 34, 57, 34, 70),
294+
new RefEvent('ref_c_table_a', 35, 12, 35, 24, 'ref_c_column_a', 35, 26, 35, 39, RefOperator::ONE_TO_ONE, 'ref_c_table_b', 35, 43, 35, 55, 'ref_c_column_b', 35, 57, 35, 70),
295+
new RefEvent('ref_d_table_a', 36, 12, 36, 26, 'ref_d_column_a', 36, 28, 36, 41, RefOperator::MANY_TO_ONE, 'ref_d_table_b', 36, 45, 36, 57, 'ref_d_column_b', 36, 59, 36, 72),
296+
new RefEvent('ref_e_table_a', 37, 12, 37, 24, 'ref_e_column_a', 37, 26, 37, 41, RefOperator::MANY_TO_ONE, 'ref_e_table_b', 37, 45, 37, 57, 'ref_e_column_b', 37, 59, 37, 72),
297+
new RefEvent('ref_f_table_a', 38, 12, 38, 24, 'ref_f_column_a', 38, 26, 38, 39, RefOperator::MANY_TO_ONE, 'ref_f_table_b', 38, 43, 38, 57, 'ref_f_column_b', 38, 59, 38, 72),
298+
new RefEvent('ref_g_table_a', 39, 12, 39, 24, 'ref_g_column_a', 39, 26, 39, 39, RefOperator::MANY_TO_ONE, 'ref_g_table_b', 39, 43, 39, 55, 'ref_g_column_b', 39, 57, 39, 72),
299+
new EnumEvent('enum_a_name', 41, 12, 41, 22),
300+
new EnumValueEvent('enum_a_name', 'enum_a_value_a', 42, 9, 42, 22),
301+
new EnumValueEvent('enum_a_name', 'enum_a_value_b', 43, 9, 43, 24),
302+
new EnumValueEvent('enum_a_name', 'enum_a_value_c', 44, 9, 44, 22),
303+
new EnumValueNoteEvent('enum_a_name', 'enum_a_value_c', 'enum_a_value_c_note', 44, 31, 44, 51),
304+
new EnumEvent('enum_b_name', 47, 12, 47, 24),
305+
new TableEvent('table_b_name', 49, 13, 49, 26),
306+
new TableEvent('table_c_name', 50, 13, 50, 24),
307+
new TableAliasEvent('table_c_name', 'table_c_alias', 50, 29, 50, 43),
308+
new TableNoteEvent('table_c_name', 'table_c_note', 51, 16, 51, 29),
309+
new RefEvent('ref_h_table_a', 54, 13, 54, 25, 'ref_h_column_a', 54, 27, 54, 40, RefOperator::MANY_TO_ONE, 'ref_h_table_b', 54, 44, 54, 56, 'ref_h_column_b', 54, 58, 54, 71),
310+
new RefEvent('ref_i_table_a', 55, 13, 55, 25, 'ref_i_column_a', 55, 27, 55, 40, RefOperator::ONE_TO_MANY, 'ref_i_table_b', 55, 44, 55, 56, 'ref_i_column_b', 55, 58, 55, 71),
311+
new RefEvent('ref_j_table_a', 56, 13, 56, 25, 'ref_j_column_a', 56, 27, 56, 40, RefOperator::ONE_TO_ONE, 'ref_j_table_b', 56, 44, 56, 56, 'ref_j_column_b', 56, 58, 56, 71),
312+
new RefEvent('ref_k_table_a', 57, 13, 57, 27, 'ref_k_column_a', 57, 29, 57, 42, RefOperator::MANY_TO_ONE, 'ref_k_table_b', 57, 46, 57, 58, 'ref_k_column_b', 57, 60, 57, 73),
313+
new RefEvent('ref_l_table_a', 58, 13, 58, 25, 'ref_l_column_a', 58, 27, 58, 42, RefOperator::MANY_TO_ONE, 'ref_l_table_b', 58, 46, 58, 58, 'ref_l_column_b', 58, 60, 58, 73),
314+
new RefEvent('ref_m_table_a', 59, 13, 59, 25, 'ref_m_column_a', 59, 27, 59, 40, RefOperator::MANY_TO_ONE, 'ref_m_table_b', 59, 44, 59, 58, 'ref_m_column_b', 59, 60, 59, 73),
315+
new RefEvent('ref_n_table_a', 60, 13, 60, 25, 'ref_n_column_a', 60, 27, 60, 40, RefOperator::MANY_TO_ONE, 'ref_n_table_b', 60, 44, 60, 56, 'ref_n_column_b', 60, 58, 60, 73),
316+
new UnknownEvent([
317+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 62, 7, 62, 16, 'unexpected', 'unexpected'),
318+
]),
319+
new TableEvent('with_empty_column_attributes', 64, 13, 64, 40),
320+
new ColumnEvent('with_empty_column_attributes', 'name_of_column_with_empty_attributes', 64, 44, 64, 79, 'type_of_column_with_empty_attributes', null),
321+
new TableEvent('with_all_attributes_in_one_column', 65, 13, 65, 45),
322+
new ColumnEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes', 65, 49, 65, 82, 'type_of_column_with_all_attributes', null),
323+
new ColumnPrimaryKeyEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes'),
324+
new ColumnIncrementEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes'),
325+
new ColumnNotNullEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes'),
326+
new ColumnNoteEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes', 'example note', 65, 151, 65, 164),
327+
new IndexEvent(
328+
'with_all_attributes_in_one_column',
329+
[
330+
[
331+
'name' => 'name_of_column_with_all_attributes',
332+
'nameStartLine' => 65,
333+
'nameStartColumn' => 49,
334+
'nameEndLine' => 65,
335+
'nameEndColumn' => 82,
336+
],
337+
],
338+
null,
339+
null,
340+
null,
341+
null,
342+
null,
343+
true
344+
),
345+
new ColumnConstantDefaultEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes', 'example default', 65, 184, 65, 200),
346+
new RefEvent('with_all_attributes_in_one_column', 65, 13, 65, 45, 'name_of_column_with_all_attributes', 65, 49, 65, 82, RefOperator::MANY_TO_ONE, 'other_table_name', 65, 210, 65, 225, 'other_column_name', 65, 227, 65, 243),
347+
new ColumnCalculatedDefaultEvent('with_all_attributes_in_one_column', 'name_of_column_with_all_attributes', 'other example default', 65, 255, 65, 277),
348+
new IndexEvent(
349+
'with_all_attributes_in_one_column',
350+
[
351+
[
352+
'name' => 'name_of_column_with_all_attributes',
353+
'nameStartLine' => 65,
354+
'nameStartColumn' => 49,
355+
'nameEndLine' => 65,
356+
'nameEndColumn' => 82,
357+
],
358+
],
359+
null,
360+
null,
361+
null,
362+
null,
363+
null,
364+
true
365+
),
366+
new TableEvent('which_tests_index_edge_cases', 66, 13, 66, 40),
367+
new IndexEvent(
368+
'which_tests_index_edge_cases',
369+
[
370+
[
371+
'name' => 'abc',
372+
'nameStartLine' => 68,
373+
'nameStartColumn' => 19,
374+
'nameEndLine' => 68,
375+
'nameEndColumn' => 21,
376+
],
377+
[
378+
'name' => 'def',
379+
'nameStartLine' => 68,
380+
'nameStartColumn' => 24,
381+
'nameEndLine' => 68,
382+
'nameEndColumn' => 26,
383+
],
384+
[
385+
'name' => 'ghi',
386+
'nameStartLine' => 68,
387+
'nameStartColumn' => 29,
388+
'nameEndLine' => 68,
389+
'nameEndColumn' => 31,
390+
],
391+
],
392+
'example name',
393+
68,
394+
49,
395+
68,
396+
62,
397+
true
398+
),
399+
new EnumEvent('which_tests_edge_cases', 70, 12, 70, 33),
400+
new EnumValueEvent('which_tests_edge_cases', 'example_value_name', 70, 37, 70, 54),
401+
new TableEvent('which_tests_column_edge_cases', 71, 13, 71, 41),
402+
new ColumnEvent('which_tests_column_edge_cases', 'example_column_name_a', 72, 9, 72, 29, 'example_column_type_a', 'example_column_size_a'),
403+
new ColumnNoteEvent('which_tests_column_edge_cases', 'example_column_name_a', 'example_column_note_a', 72, 83, 72, 103),
404+
new ColumnEvent('which_tests_column_edge_cases', 'example_column_name_b', 73, 9, 73, 29, 'example_column_type_b', null),
405+
new TableEvent('which_tests_additional_column_edge_cases', 75, 13, 75, 52),
406+
new ColumnEvent('which_tests_additional_column_edge_cases', 'example_column_name_a', 76, 9, 76, 29, 'example_column_type_a', 'example_column_size_a'),
407+
new EndOfFileEvent(true),
408+
], $logParserTarget->events);
409+
}
410+
411+
public function test_file_with_interrupted_structure()
412+
{
413+
$logParserTarget = new LogParserTarget();
414+
$parser = new Parser($logParserTarget);
415+
$tokenizer = new Tokenizer($parser);
416+
417+
foreach (str_split("
418+
tABlE table_a_name as table_a_alias {
419+
column_a_name column_a_type
420+
column_b_name column_b_type(column_b_size)
421+
column_c_name column_c_type [pk]
422+
column_d_name column_d_type [increment]
423+
column_e_name column_e_type [default: 'column e default']
424+
column_f_name column_f_type [default: `column f default`]
425+
column_g_name column_g_type [not null] /
426+
column_h_name column_h_type [note: 'column h note']
427+
column_i_name column_i_type [ref: > column_i_ref_table.column_i_ref_column]
428+
column_j_name column_j_type [ref: < column_j_ref_table.column_j_ref_column] ]->
429+
column_k_name column_k_type [ref: - column_k_ref_table.column_k_ref_column]
430+
") as $character) {
431+
$tokenizer->character($character);
432+
}
433+
$tokenizer->endOfFile();
434+
435+
$this->assertEquals([
436+
new TableEvent('table_a_name', 2, 13, 2, 24),
437+
new TableAliasEvent('table_a_name', 'table_a_alias', 2, 29, 2, 41),
438+
new ColumnEvent('table_a_name', 'column_a_name', 3, 9, 3, 21, 'column_a_type', null),
439+
new ColumnEvent('table_a_name', 'column_b_name', 4, 9, 4, 21, 'column_b_type', 'column_b_size'),
440+
new ColumnEvent('table_a_name', 'column_c_name', 5, 9, 5, 21, 'column_c_type', null),
441+
new ColumnPrimaryKeyEvent('table_a_name', 'column_c_name'),
442+
new ColumnEvent('table_a_name', 'column_d_name', 6, 9, 6, 21, 'column_d_type', null),
443+
new ColumnIncrementEvent('table_a_name', 'column_d_name'),
444+
new ColumnEvent('table_a_name', 'column_e_name', 7, 9, 7, 21, 'column_e_type', null),
445+
new ColumnConstantDefaultEvent('table_a_name', 'column_e_name', 'column e default', 7, 47, 7, 64),
446+
new ColumnEvent('table_a_name', 'column_f_name', 8, 9, 8, 21, 'column_f_type', null),
447+
new ColumnCalculatedDefaultEvent('table_a_name', 'column_f_name', 'column f default', 8, 47, 8, 64),
448+
new ColumnEvent('table_a_name', 'column_g_name', 9, 9, 9, 21, 'column_g_type', null),
449+
new ColumnNotNullEvent('table_a_name', 'column_g_name'),
450+
new ColumnEvent('table_a_name', 'column_h_name', 10, 9, 10, 21, 'column_h_type', null),
451+
new ColumnNoteEvent('table_a_name', 'column_h_name', 'column h note', 10, 44, 10, 58),
452+
new ColumnEvent('table_a_name', 'column_i_name', 11, 9, 11, 21, 'column_i_type', null),
453+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_i_name', 11, 9, 11, 21, RefOperator::MANY_TO_ONE, 'column_i_ref_table', 11, 45, 11, 62, 'column_i_ref_column', 11, 64, 11, 82),
454+
new ColumnEvent('table_a_name', 'column_j_name', 12, 9, 12, 21, 'column_j_type', null),
455+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_j_name', 12, 9, 12, 21, RefOperator::ONE_TO_MANY, 'column_j_ref_table', 12, 45, 12, 62, 'column_j_ref_column', 12, 64, 12, 82),
456+
new UnknownEvent([
457+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 85, 12, 85, ']', ']'),
458+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 86, 12, 86, '-', '-'),
459+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 87, 12, 87, '>', '>'),
460+
]),
461+
new ColumnEvent('table_a_name', 'column_k_name', 13, 9, 13, 21, 'column_k_type', null),
462+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_k_name', 13, 9, 13, 21, RefOperator::ONE_TO_ONE, 'column_k_ref_table', 13, 45, 13, 62, 'column_k_ref_column', 13, 64, 13, 82),
463+
new EndOfFileEvent(false),
464+
], $logParserTarget->events);
465+
}
466+
467+
public function test_file_with_interrupted_invalid_characters()
468+
{
469+
$logParserTarget = new LogParserTarget();
470+
$parser = new Parser($logParserTarget);
471+
$tokenizer = new Tokenizer($parser);
472+
473+
foreach (str_split("
474+
tABlE table_a_name as table_a_alias {
475+
column_a_name column_a_type
476+
column_b_name column_b_type(column_b_size)
477+
column_c_name column_c_type [pk]
478+
column_d_name column_d_type [increment]
479+
column_e_name column_e_type [default: 'column e default']
480+
column_f_name column_f_type [default: `column f default`]
481+
column_g_name column_g_type [not null] /
482+
column_h_name column_h_type [note: 'column h note']
483+
column_i_name column_i_type [ref: > column_i_ref_table.column_i_ref_column]
484+
column_j_name column_j_type [ref: < column_j_ref_table.column_j_ref_column] ]->
485+
") as $character) {
486+
$tokenizer->character($character);
487+
}
488+
$tokenizer->endOfFile();
489+
490+
$this->assertEquals([
491+
new TableEvent('table_a_name', 2, 13, 2, 24),
492+
new TableAliasEvent('table_a_name', 'table_a_alias', 2, 29, 2, 41),
493+
new ColumnEvent('table_a_name', 'column_a_name', 3, 9, 3, 21, 'column_a_type', null),
494+
new ColumnEvent('table_a_name', 'column_b_name', 4, 9, 4, 21, 'column_b_type', 'column_b_size'),
495+
new ColumnEvent('table_a_name', 'column_c_name', 5, 9, 5, 21, 'column_c_type', null),
496+
new ColumnPrimaryKeyEvent('table_a_name', 'column_c_name'),
497+
new ColumnEvent('table_a_name', 'column_d_name', 6, 9, 6, 21, 'column_d_type', null),
498+
new ColumnIncrementEvent('table_a_name', 'column_d_name'),
499+
new ColumnEvent('table_a_name', 'column_e_name', 7, 9, 7, 21, 'column_e_type', null),
500+
new ColumnConstantDefaultEvent('table_a_name', 'column_e_name', 'column e default', 7, 47, 7, 64),
501+
new ColumnEvent('table_a_name', 'column_f_name', 8, 9, 8, 21, 'column_f_type', null),
502+
new ColumnCalculatedDefaultEvent('table_a_name', 'column_f_name', 'column f default', 8, 47, 8, 64),
503+
new ColumnEvent('table_a_name', 'column_g_name', 9, 9, 9, 21, 'column_g_type', null),
504+
new ColumnNotNullEvent('table_a_name', 'column_g_name'),
505+
new ColumnEvent('table_a_name', 'column_h_name', 10, 9, 10, 21, 'column_h_type', null),
506+
new ColumnNoteEvent('table_a_name', 'column_h_name', 'column h note', 10, 44, 10, 58),
507+
new ColumnEvent('table_a_name', 'column_i_name', 11, 9, 11, 21, 'column_i_type', null),
508+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_i_name', 11, 9, 11, 21, RefOperator::MANY_TO_ONE, 'column_i_ref_table', 11, 45, 11, 62, 'column_i_ref_column', 11, 64, 11, 82),
509+
new ColumnEvent('table_a_name', 'column_j_name', 12, 9, 12, 21, 'column_j_type', null),
510+
new RefEvent('table_a_name', 2, 13, 2, 24, 'column_j_name', 12, 9, 12, 21, RefOperator::ONE_TO_MANY, 'column_j_ref_table', 12, 45, 12, 62, 'column_j_ref_column', 12, 64, 12, 82),
511+
new UnknownEvent([
512+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 85, 12, 85, ']', ']'),
513+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 86, 12, 86, '-', '-'),
514+
new TokenEvent(TokenType::KEYWORD_SYMBOL_OR_IDENTIFIER, 12, 87, 12, 87, '>', '>'),
515+
]),
516+
new EndOfFileEvent(false),
517+
], $logParserTarget->events);
518+
}
519+
}

0 commit comments

Comments
 (0)
Please sign in to comment.