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)79auto 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()150int main () 151 { 152 auto l = calcLexer (stdin); 153 auto p = new Calc (l); 154 p.parse (); 155 return l.exit_status; 156 } 157