packages/bison-parser/tests/Unit/Printer/DeclarationPrinterTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Printer;
6
7use BisonParser\Ast\Declaration\Code;
8use BisonParser\Ast\Declaration\CodeProps;
9use BisonParser\Ast\Declaration\Define;
10use BisonParser\Ast\Declaration\DefineForm;
11use BisonParser\Ast\Declaration\Expect;
12use BisonParser\Ast\Declaration\Flag;
13use BisonParser\Ast\Declaration\InitialAction;
14use BisonParser\Ast\Declaration\Option;
15use BisonParser\Ast\Declaration\Param;
16use BisonParser\Ast\Declaration\ParamKind;
17use BisonParser\Ast\Declaration\Prologue;
18use BisonParser\Ast\Declaration\Start;
19use BisonParser\Ast\Declaration\Symbols\Alias;
20use BisonParser\Ast\Declaration\Symbols\Associativity;
21use BisonParser\Ast\Declaration\Symbols\PrecedenceDeclaration;
22use BisonParser\Ast\Declaration\Symbols\SymbolClass;
23use BisonParser\Ast\Declaration\Symbols\SymbolDeclaration;
24use BisonParser\Ast\Declaration\Symbols\SymbolEntry;
25use BisonParser\Ast\Declaration\UnionDeclaration;
26use BisonParser\Ast\Line;
27use BisonParser\Ast\Location;
28use BisonParser\Ast\Symbol;
29use BisonParser\Ast\SymbolKind;
30use BisonParser\Ast\Tag;
31use BisonParser\Printer\DeclarationPrinter;
32use BisonParser\Printer\SymbolPrinter;
33use BisonParser\Scanner\Escapes;
34use PHPUnit\Framework\Attributes\CoversClass;
35use PHPUnit\Framework\Attributes\Small;
36use PHPUnit\Framework\Attributes\UsesClass;
37use PHPUnit\Framework\TestCase;
38
39#[CoversClass(DeclarationPrinter::class)]
40#[UsesClass(Alias::class)]
41#[UsesClass(Associativity::class)]
42#[UsesClass(Code::class)]
43#[UsesClass(CodeProps::class)]
44#[UsesClass(Define::class)]
45#[UsesClass(DefineForm::class)]
46#[UsesClass(Escapes::class)]
47#[UsesClass(Expect::class)]
48#[UsesClass(Flag::class)]
49#[UsesClass(InitialAction::class)]
50#[UsesClass(Line::class)]
51#[UsesClass(Location::class)]
52#[UsesClass(Option::class)]
53#[UsesClass(Param::class)]
54#[UsesClass(ParamKind::class)]
55#[UsesClass(PrecedenceDeclaration::class)]
56#[UsesClass(Prologue::class)]
57#[UsesClass(Start::class)]
58#[UsesClass(Symbol::class)]
59#[UsesClass(SymbolClass::class)]
60#[UsesClass(SymbolDeclaration::class)]
61#[UsesClass(SymbolEntry::class)]
62#[UsesClass(SymbolKind::class)]
63#[UsesClass(SymbolPrinter::class)]
64#[UsesClass(Tag::class)]
65#[UsesClass(UnionDeclaration::class)]
66#[Small]
67final class DeclarationPrinterTest extends TestCase
68{
69 public function testPrint(): void
70 {
71 $printer = new DeclarationPrinter();
72 $at = new Location(1, 1);
73 $num = new Symbol(SymbolKind::Identifier, 'NUM', $at);
74
75 self::assertSame('%{ int x; %}', $printer->print(new Prologue(' int x; ', $at)));
76 self::assertSame('%pure_parser', $printer->print(new Flag('pure-parser', '%pure_parser', $at)));
77 self::assertSame('%defines', $printer->print(new Option('header', '%defines', null, $at)));
78 self::assertSame('%require "3.8"', $printer->print(new Option('require', '%require', '3.8', $at)));
79 self::assertSame('%define api.pure full', $printer->print(new Define('api.pure', 'full', DefineForm::Keyword, $at)));
80 self::assertSame('%expect 2', $printer->print(new Expect(2, false, $at)));
81 self::assertSame('%expect-rr 1', $printer->print(new Expect(1, true, $at)));
82 self::assertSame('%initial-action { @$ = 1; }', $printer->print(new InitialAction(' @$ = 1; ', $at)));
83 self::assertSame('%lex-param {int a} {int b}', $printer->print(new Param(ParamKind::Lex, ['int a', 'int b'], $at)));
84 self::assertSame('%union { int i; }', $printer->print(new UnionDeclaration(null, ' int i; ', $at)));
85 self::assertSame('%union YYSTYPE { int i; }', $printer->print(new UnionDeclaration('YYSTYPE', ' int i; ', $at)));
86 self::assertSame('%start program', $printer->print(new Start([new Symbol(SymbolKind::Identifier, 'program', $at)], $at)));
87 self::assertSame('%printer { p($$); } <*> NUM', $printer->print(new CodeProps(true, ' p($$); ', [new Tag(Tag::ANY, $at), $num], $at)));
88 self::assertSame('%destructor { free($$); } <>', $printer->print(new CodeProps(false, ' free($$); ', [new Tag(Tag::NONE, $at)], $at)));
89 self::assertSame('%code { x }', $printer->print(new Code(null, ' x ', $at)));
90 self::assertSame('%code requires { x }', $printer->print(new Code('requires', ' x ', $at)));
91 self::assertSame('%token <int> NUM 258 "number"', $printer->print(new SymbolDeclaration(SymbolClass::Token, [new SymbolEntry($num, 'int', 258, new Alias('number', false, $at))], $at)));
92 self::assertSame("%left '+' '-'", $printer->print(new PrecedenceDeclaration(Associativity::Left, [new SymbolEntry(new Symbol(SymbolKind::CharLiteral, '+', $at), null, null, null), new SymbolEntry(new Symbol(SymbolKind::CharLiteral, '-', $at), null, null, null)], $at)));
93 }
94
95 public function testOption(): void
96 {
97 $printer = new DeclarationPrinter();
98 $at = new Location(1, 1);
99
100 self::assertSame('%defines', $printer->option(new Option('header', '%defines', null, $at)));
101 self::assertSame('%require "3.8"', $printer->option(new Option('require', '%require', '3.8', $at)));
102 }
103
104 public function testExpect(): void
105 {
106 $printer = new DeclarationPrinter();
107
108 self::assertSame('%expect 2', $printer->expect(2, false));
109 self::assertSame('%expect-rr 1', $printer->expect(1, true));
110 }
111
112 public function testUnion(): void
113 {
114 $printer = new DeclarationPrinter();
115 $at = new Location(1, 1);
116
117 self::assertSame('%union { int i; }', $printer->union(new UnionDeclaration(null, ' int i; ', $at)));
118 self::assertSame('%union YYSTYPE { int i; }', $printer->union(new UnionDeclaration('YYSTYPE', ' int i; ', $at)));
119 }
120
121 public function testProps(): void
122 {
123 $printer = new DeclarationPrinter();
124 $at = new Location(1, 1);
125
126 self::assertSame('%printer { p($$); } <*>', $printer->props(new CodeProps(true, ' p($$); ', [new Tag(Tag::ANY, $at)], $at)));
127 self::assertSame('%destructor { free($$); } NUM', $printer->props(new CodeProps(false, ' free($$); ', [new Symbol(SymbolKind::Identifier, 'NUM', $at)], $at)));
128 }
129
130 public function testCode(): void
131 {
132 $printer = new DeclarationPrinter();
133 $at = new Location(1, 1);
134
135 self::assertSame('%code { x }', $printer->code(new Code(null, ' x ', $at)));
136 self::assertSame('%code requires { x }', $printer->code(new Code('requires', ' x ', $at)));
137 }
138
139 public function testLine(): void
140 {
141 $printer = new DeclarationPrinter();
142 $at = new Location(1, 1);
143
144 self::assertSame('#line 12 "dir/file.y"', $printer->line(new Line(12, 'dir/file.y', $at)));
145 self::assertSame('#line 3', $printer->line(new Line(3, null, $at)));
146 self::assertSame('#line 3', $printer->print(new Line(3, null, $at)));
147 }
148
149 public function testDefine(): void
150 {
151 $printer = new DeclarationPrinter();
152 $at = new Location(1, 1);
153
154 self::assertSame('%define api.pure full', $printer->define(new Define('api.pure', 'full', DefineForm::Keyword, $at)));
155 self::assertSame('%define api.prefix "yy"', $printer->define(new Define('api.prefix', 'yy', DefineForm::String, $at)));
156 self::assertSame('%define api.value.type {struct s}', $printer->define(new Define('api.value.type', 'struct s', DefineForm::Code, $at)));
157 self::assertSame('%define parse.trace', $printer->define(new Define('parse.trace', null, null, $at)));
158 }
159
160 public function testTargets(): void
161 {
162 $at = new Location(1, 1);
163
164 self::assertSame("NUM <int> <*> '+'", (new DeclarationPrinter())->targets([new Symbol(SymbolKind::Identifier, 'NUM', $at), new Tag('int', $at), new Tag(Tag::ANY, $at), new Symbol(SymbolKind::CharLiteral, '+', $at)]));
165 }
166
167 public function testEntries(): void
168 {
169 $at = new Location(1, 1);
170 $entries = [
171 new SymbolEntry(new Symbol(SymbolKind::Identifier, 'A', $at), null, null, null),
172 new SymbolEntry(new Symbol(SymbolKind::Identifier, 'B', $at), 'int', 300, new Alias('bee', true, $at)),
173 new SymbolEntry(new Symbol(SymbolKind::Identifier, 'C', $at), 'int', null, new Alias('cee', false, $at)),
174 new SymbolEntry(new Symbol(SymbolKind::String, 'dee', $at), 'str', null, null),
175 new SymbolEntry(new Symbol(SymbolKind::Identifier, 'E', $at), 'str', null, new Alias('e', false, $at, '"\\x65"')),
176 ];
177
178 self::assertSame(' A <int> B 300 _("bee") C "cee" <str> "dee" E "\\x65"', (new DeclarationPrinter())->entries($entries));
179 }
180}
181