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