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
|
#
# Sample Ibpag2 grammar file.
#
#
# The code between %{ and %} gets copied directly. Note the Iconish
# comment syntax.
#
%{
# Note: If IIDEBUG is defined in the output file, debugging messages
# about the stacks and actions get displayed.
#
$define IIDEBUG 1
%}
#
# Here we declare the tokens returned by the lexical analyzer.
# Precedences increase as we go on. Note how (unlike YACC), tokens
# are separated by commas. Note also how UMINUS is used only for its
# %prec later.
#
%token NUMBER
%left '+', '-'
%left '*', '/'
%right UMINUS
%%
#
# After this point, and up to the next %%, we have the grammar itself.
# By default, the start symbol is the left-hand side of the first
# rule.
#
lines : lines, expr, '\n' { write($2) }
| lines, '\n'
| epsilon # Note use of epsilon/error tokens.
| error, '\n' {
write("syntax error; try again:")
# like YACC's yyerrok macro
iierrok
}
;
expr : expr, '+', expr { return $1 + $3 }
| expr, '-', expr { return $1 - $3 }
| expr, '*', expr { return $1 * $3 }
| expr, '/', expr { return $1 / $3 }
| '(', expr, ')' { return $2 }
| '-', expr %prec UMINUS { return -$2 }
| NUMBER { return $1 }
;
%%
#
# From here on, code gets copied directly to the output file. We are
# no longer in the grammar proper.
#
#
# The lexical analyzer must be called iilex, with the module name
# appended (if there is one). It must take one argument, infile (an
# input stream). It must be a generator, and fail on EOF (not return
# something <= 0, as is the case for YACC + Lex). Iilval holds the
# literal string value of the token just suspended by iilex().
#
procedure iilex(infile)
local nextchar, c, num
initial {
# Here's where you'd initialize any %{ globals %} declared
# above.
}
nextchar := create !(!infile || "\n" || "\n")
c := @nextchar | fail
repeat {
if any(&digits, c) then {
if not (\num ||:= c) then
num := c
} else {
if iilval := \num then {
suspend NUMBER
num := &null
}
if any('+-*/()\n', c) then {
iilval := c
suspend ord(c)
} else {
if not any(' \t', c) then {
# deliberate error - will be handled later
suspend &null
}
}
}
c := @nextchar | break
}
if iilval := \num then {
return NUMBER
num := &null
}
end
procedure main()
return iiparse(&input, 1)
end
|