1 /* Parser and scanner for calc in D.   -*- D -*-
2 
3    Copyright (C) 2018-2021 Free Software Foundation, Inc.
4 
5    This file is part of Bison, the GNU Compiler Compiler.
6 
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.
11 
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.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
19 
20 %language "D"
21 
22 %define api.parser.class {Calc}
23 %define parse.error verbose
24 
25 %union {
26   int ival;
27 }
28 
29 /* Bison Declarations */
30 %token EQ     "="
31        PLUS   "+"
32        MINUS  "-"
33        STAR   "*"
34        SLASH  "/"
35        LPAR   "("
36        RPAR   ")"
37        EOL    "end of line"
38 %token <ival> NUM "number"
39 %type  <ival> exp
40 
41 %left "-" "+"
42 %left "*" "/"
43 %precedence UNARY   /* unary operators */
44 
45 /* Grammar follows */
46 %%
47 input:
48   line
49 | input line
50 ;
51 
52 line:
53   EOL
54 | exp EOL           { writeln ($exp); }
55 | error EOL
56 ;
57 
58 exp:
59   NUM                  { $$ = $1; }
60 | exp "+" exp          { $$ = $1 + $3; }
61 | exp "-" exp          { $$ = $1 - $3; }
62 | exp "*" exp          { $$ = $1 * $3; }
63 | exp "/" exp          { $$ = $1 / $3; }
64 | "+" exp  %prec UNARY { $$ = -$2; }
65 | "-" exp  %prec UNARY { $$ = -$2; }
66 | "(" exp ")"          { $$ = $2; }
67 ;
68 
69 %%
70 import std.range.primitives;
71 import std.stdio;
72 
73 auto calcLexer(R)(R range)
74   if (isInputRange!R && is (ElementType!R : dchar))
75 {
76   return new CalcLexer!R(range);
77 }
78 
calcLexer(File f)79 auto calcLexer (File f)
80 {
81   import std.algorithm : map, joiner;
82   import std.utf : byDchar;
83 
84   return f.byChunk(1024)        // avoid making a syscall roundtrip per char
85           .map!(chunk => cast(char[]) chunk) // because byChunk returns ubyte[]
86           .joiner               // combine chunks into a single virtual range of char
87           .calcLexer;           // forward to other overload
88 }
89 
90 class CalcLexer(R) : Lexer
91   if (isInputRange!R && is (ElementType!R : dchar))
92 {
93   R input;
94 
this(R r)95   this(R r) { input = r; }
96 
97   // Should be a local in main, shared with %parse-param.
98   int exit_status = 0;
99 
yyerror(string s)100   public void yyerror (string s)
101   {
102     exit_status = 1;
103     stderr.writeln (s);
104   }
105 
106   YYSemanticType semanticVal_;
107 
semanticVal()108   public final @property YYSemanticType semanticVal ()
109   {
110     return semanticVal_;
111   }
112 
yylex()113   int yylex ()
114   {
115     import std.uni : isWhite, isNumber;
116 
117     // Skip initial spaces
118     while (!input.empty && input.front != '\n' && isWhite (input.front))
119       input.popFront;
120 
121     if (input.empty)
122       return TokenKind.YYEOF;
123 
124     // Numbers.
125     if (input.front.isNumber)
126       {
127         import std.conv : parse;
128         semanticVal_.ival = input.parse!int;
129         return TokenKind.NUM;
130       }
131 
132     // Individual characters
133     auto ch = input.front;
134     input.popFront;
135     switch (ch)
136       {
137       case '=':  return TokenKind.EQ;
138       case '+':  return TokenKind.PLUS;
139       case '-':  return TokenKind.MINUS;
140       case '*':  return TokenKind.STAR;
141       case '/':  return TokenKind.SLASH;
142       case '(':  return TokenKind.LPAR;
143       case ')':  return TokenKind.RPAR;
144       case '\n': return TokenKind.EOL;
145       default: assert(0);
146       }
147   }
148 }
149 
main()150 int main ()
151 {
152   auto l = calcLexer (stdin);
153   auto p = new Calc (l);
154   p.parse ();
155   return l.exit_status;
156 }
157