examples: d: demonstrate location tracking
[bison.git] / examples / d / calc / calc.y
blob0aa96ee6044cf1dc095760dfa2d4b6695dea8823
1 /* Parser and scanner for calc in D. -*- D -*-
3 Copyright (C) 2018-2020 Free Software Foundation, Inc.
5 This file is part of Bison, the GNU Compiler Compiler.
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 %language "D"
22 %define api.parser.class {Calc}
23 %define parse.error verbose
25 %locations
27 %union {
28 int ival;
31 /* Bison Declarations */
32 %token EQ "="
33 PLUS "+"
34 MINUS "-"
35 STAR "*"
36 SLASH "/"
37 LPAR "("
38 RPAR ")"
39 EOL "end of line"
40 %token <ival> NUM "number"
41 %type <ival> exp
43 %left "-" "+"
44 %left "*" "/"
45 %precedence UNARY /* unary operators */
47 /* Grammar follows */
49 input:
50 line
51 | input line
54 line:
55 EOL
56 | exp EOL { writeln ($exp); }
57 | error EOL
60 exp:
61 NUM { $$ = $1; }
62 | exp "+" exp { $$ = $1 + $3; }
63 | exp "-" exp { $$ = $1 - $3; }
64 | exp "*" exp { $$ = $1 * $3; }
65 | exp "/" exp { $$ = $1 / $3; }
66 | "+" exp %prec UNARY { $$ = $2; }
67 | "-" exp %prec UNARY { $$ = -$2; }
68 | "(" exp ")" { $$ = $2; }
72 import std.range.primitives;
73 import std.stdio;
75 auto calcLexer(R)(R range)
76 if (isInputRange!R && is (ElementType!R : dchar))
78 return new CalcLexer!R(range);
81 auto calcLexer (File f)
83 import std.algorithm : map, joiner;
84 import std.utf : byDchar;
86 return f.byChunk(1024) // avoid making a syscall roundtrip per char
87 .map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
88 .joiner // combine chunks into a single virtual range of char
89 .calcLexer; // forward to other overload
92 class CalcLexer(R) : Lexer
93 if (isInputRange!R && is (ElementType!R : dchar))
95 R input;
97 this(R r) { input = r; }
99 YYPosition start;
100 YYPosition end;
102 // Should be a local in main, shared with %parse-param.
103 int exit_status = 0;
105 void yyerror(YYLocation loc, string s)
107 exit_status = 1;
108 stderr.writeln(loc.toString(), ": ", s);
111 YYSemanticType semanticVal_;
113 public final @property YYSemanticType semanticVal ()
115 return semanticVal_;
118 int yylex ()
120 import std.uni : isWhite, isNumber;
122 // Skip initial spaces
123 while (!input.empty && input.front != '\n' && isWhite (input.front))
125 start = end;
126 end.column++;
127 input.popFront;
130 if (input.empty)
131 return TokenKind.YYEOF;
133 // Numbers.
134 if (input.front.isNumber)
136 int lenChars = 0;
137 auto copy = input;
138 import std.conv : parse;
139 semanticVal_.ival = input.parse!int;
140 while (!input.empty && copy.front != input.front)
142 lenChars++;
143 copy.popFront;
145 start = end;
146 end.column += lenChars;
147 return TokenKind.NUM;
150 // Individual characters
151 auto ch = input.front;
152 input.popFront;
153 start = end;
154 end.column++;
155 switch (ch)
157 case '=': return TokenKind.EQ;
158 case '+': return TokenKind.PLUS;
159 case '-': return TokenKind.MINUS;
160 case '*': return TokenKind.STAR;
161 case '/': return TokenKind.SLASH;
162 case '(': return TokenKind.LPAR;
163 case ')': return TokenKind.RPAR;
164 case '\n':
166 end.line++;
167 end.column = 1;
168 return TokenKind.EOL;
170 default: assert(0);
174 YYPosition startPos() const
176 return start;
179 YYPosition endPos() const
181 return end;
185 int main ()
187 auto l = calcLexer (stdin);
188 auto p = new Calc (l);
189 p.parse ();
190 return l.exit_status;