summaryrefslogtreecommitdiff
path: root/examples/c/lexcalc/parse.y
blob: d2fcb71356139cdbaba240ee6be06082fda6eeb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* Parser for lexcalc.   -*- C -*-

   Copyright (C) 2018-2021 Free Software Foundation, Inc.

   This file is part of Bison, the GNU Compiler Compiler.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

// Prologue (directives).
%expect 0

// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
  // Tell Flex the expected prototype of yylex.
#define YY_DECL                                 \
  yytoken_kind_t yylex (YYSTYPE* yylval, YYLTYPE *yylloc)
  YY_DECL;

  void yyerror (YYLTYPE *loc, const char *msg);
}

// Emitted on top of the implementation file.
%code top
{
#include <stdio.h>  // printf.
#include <stdlib.h> // getenv.
#include <string.h> // strcmp.
}

// Include the header in the implementation rather than duplicating it.
%define api.header.include {"parse.h"}

// Don't share global variables between the scanner and the parser.
%define api.pure full

// To avoid name clashes (e.g., with C's EOF) prefix token definitions
// with TOK_ (e.g., TOK_EOF).
%define api.token.prefix {TOK_}

// %token and %type use genuine types (e.g., "%token <int>").  Let
// %bison define YYSTYPE as a union of all these types.
%define api.value.type union

// Generate detailed error messages.
%define parse.error detailed

// with locations.
%locations

// Enable debug traces (see yydebug in main).
%define parse.trace

%token
  PLUS   "+"
  MINUS  "-"
  STAR   "*"
  SLASH  "/"
  LPAREN "("
  RPAREN ")"
  EOL    "end of line"
;

%token <int> NUM "number"
%type <int> exp expression line
%printer { fprintf (yyo, "%d", $$); } <int>

%start input expression NUM

// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"

%%
// Rules.
input:
  %empty
| input line
;

line:
  exp EOL   { $$ = $exp; printf ("%d\n", $$); }
| error EOL { $$ = 0; yyerrok; }
;

expression:
  exp EOL  { $$ = $exp; }
;

exp:
  exp "+" exp   { $$ = $1 + $3; }
| exp "-" exp   { $$ = $1 - $3; }
| exp "*" exp   { $$ = $1 * $3; }
| exp "/" exp
  {
    if ($3 == 0)
      {
        yyerror (&@$, "error: division by zero");
        YYERROR;
      }
    else
      $$ = $1 / $3;
  }
| "(" exp ")"   { $$ = $2; }
| NUM           { $$ = $1; }
;
%%
// Epilogue (C code).

void yyerror (YYLTYPE *loc, const char *msg)
{
  YY_LOCATION_PRINT (stderr, *loc);
  fprintf (stderr, ": %s\n", msg);
}

int main (int argc, const char *argv[])
{
  // Possibly enable parser runtime debugging.
  yydebug = !!getenv ("YYDEBUG");
  int parse_expression_p = 0;
  int nerrs = 0;

  // Enable parse traces on option -p.
  for (int i = 1; i < argc; ++i)
    if (strcmp (argv[i], "-e") == 0)
      parse_expression_p = 1;
    else if (strcmp (argv[i], "-p") == 0)
      yydebug = 1;

  if (parse_expression_p)
    {
      yyparse_expression_t res = yyparse_expression ();
      nerrs = res.yynerrs;
      if (res.yystatus == 0)
        printf ("expression: %d\n", res.yyvalue);
      else
        printf ("expression: failure\n");
    }
  else
    nerrs = yyparse_input ().yynerrs;

  if (nerrs)
    fprintf (stderr, "errors: %d\n", nerrs);

  // Exit on failure if there were errors.
  return !!nerrs;
}