-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.y.rb
146 lines (126 loc) · 4.27 KB
/
parser.y.rb
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
# Usage racc datatypes.y.rb && ruby datatypes.tab.rb
#
# Simple ML like language using Scott encoding
class Parser
token WORD INT BOOL STRING
options no_result_var
start main
rule
main : stmts { [val[0]].flatten }
stmts : stmts ";" stmt { [val[0], val[2]] } | stmt
stmt : data | val | expr_0
data : "data" WORD args "=" ctrs
{ DataType.new(val[1], [val[2]].flatten, [val[4]].flatten) }
| "data" WORD "=" ctrs
{ DataType.new(val[1], nil, [val[3]].flatten) }
args : args WORD { [val[0], val[1]] } | WORD
ctrs : ctr "|" ctrs { [val[0], val[2]] }
| ctr
ctr : ctr WORD { Ctr.new(val[0].name, [*val[0].args, val[1]].flatten) }
| WORD { Ctr.new(val[0]) }
val : "val" WORD "=" expr_0 { Val.new(val[1], val[3]) }
expr_0 : lamb | let | if_ | match | expr_1
expr_1 : app | expr_2
expr_2 : atom
lamb : "fun" WORD "=>" expr_0 { Lamb.new(val[1], nil, val[3]) }
| "fun" WORD ":" typ_scheme "=>" expr_0 { Lamb.new(val[1], val[3], val[5]) }
app : expr_1 expr_2 { App.new(val[0], val[1]) }
atom : WORD | const | "(" expr_0 ")" { val[1] }
const : INT | BOOL | STRING
let : "let" WORD "=" expr_0 "in" expr_0
{ Let.new(val[1], val[3], val[5]) }
match : "match" expr_0 "with" patterns "end"
{ Match.new(val[1], [val[3]].flatten) }
if_ : "if" expr_0 "then" expr_0 "else" expr_0
{ If.new(val[1], val[3], val[5]) }
patterns : pattern patterns { [val[0], val[1]] }
| pattern
pattern : "|" pat "=>" expr_0 { MatchPattern.new(val[1], val[3]) }
pat : WORD pat { [val[0], val[1]].flatten } | WORD
typ_scheme : "forall" typ_args "." typ_expr { TypeScheme.new(val[1], val[3]) }
| typ_expr
typ_args : WORD typ_args { [UVar.new(val[0].to_s), val[1]].flatten }
| WORD { [UVar.new(val[0])] }
typ_expr : typ_arrow | typ_base
typ_arrow : typ_base "->" typ_expr { UFun.new(:arrow, [val[0], val[2]]) }
typ_base : WORD { typ_var_of_sym(val[0]) } | "(" typ_expr ")" { val[1] }
end
---- inner
KEYWORDS = %w(data match with end let in fun val if then else true false forall)
SYMBOLS = %w(=> -> . | ; = ( ) :).map { |x| Regexp.quote(x) }
def typ_var_of_sym(val)
if val.size == 1
UVar.new(val.to_sym)
else
UFun.new(val.to_sym, [])
end
end
def readstring(s)
acc = []
loop do
x = s.scan_until(/"|\"/)
fail "unterminated string \"#{str}" if x.nil?
if x.end_with? '\"'
acc << x
else
acc << x[..-2]
break
end
end
acc.join("")
end
def make_tokens str
require 'strscan'
result = []
s = StringScanner.new str
until s.empty?
case
when s.skip(/\s+/)
when s.scan(/#/)
s.skip_until(/$/)
when tok = s.scan(/(#{SYMBOLS.join("|")})/)
result << [tok, nil]
when tok = s.scan(/\b(#{KEYWORDS.join("|")})\b/)
case tok
when "true"
result << [:BOOL, true]
when "false"
result << [:BOOL, false]
else
result << [tok, nil]
end
when tok = s.scan(/"/)
result << [:STRING, readstring(s)]
when tok = s.scan(/\d+/)
result << [:INT, tok.to_i]
when tok = s.scan(/\w+/)
result << [:WORD, tok.to_sym]
else
raise "can't recognize <#{s.peek(5)}>"
end
end
result << [false, false]
return result
end
attr_accessor :result
def parse(str)
@result = []
@tokens = make_tokens str
do_parse
end
def next_token
@tokens.shift
end
---- header
class DataType < Struct.new :name, :args, :ctrs; def to_s; Unparser.new.unparse([self]); end; end
class Ctr < Struct.new :name, :args; def to_s; Unparser.new.unparse([self]); end; end
class App < Struct.new :f, :arg; def to_s; Unparser.new.unparse([self]); end; end
class Let < Struct.new :x, :e1, :e2; def to_s; Unparser.new.unparse([self]); end; end
class Lamb < Struct.new :arg, :typ, :body; def to_s; Unparser.new.unparse([self]); end; end
class Val < Struct.new :name, :value; def to_s; Unparser.new.unparse([self]); end; end
class Match < Struct.new :scrutinee, :patterns; def to_s; Unparser.new.unparse([self]); end; end
class MatchPattern < Struct.new :pat, :body; def to_s; Unparser.new.unparse([self]); end; end
class If < Struct.new :cond, :then_, :else_; def to_s; Unparser.new.unparse([self]); end; end
class TypeScheme < Struct.new :args, :typ; end
class UVar < Struct.new :letter; end
class UFun < Struct.new :name, :args; end