-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsyntax.swift
More file actions
218 lines (199 loc) · 6.44 KB
/
syntax.swift
File metadata and controls
218 lines (199 loc) · 6.44 KB
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// type ::= `int` | `bool`
enum LangType {
case IntType
case BoolType
}
// op ::= `+` | `&&` | `<`
enum Op {
case PlusOp
case AndOp
case LessThanOp
}
// intVariable ::= `i0` | `i1`
// boolVariable ::= `b0` | `b1`
// intExp ::= INTEGER | intExp + intExp | if boolExp intExp intExp |
// let intVariable : int = intExp in intExp |
// let boolVariable : bool = boolExp in intExp |
// intVariable
// boolExp ::= `true` | `false` | boolExp && boolExp |
// intExp < intExp |
// if boolExp boolExp boolExp |
// let intVariable : int = intExp in boolExp |
// let boolVariable : bool = boolExp in boolExp |
// boolVariable
// exp ::= intExp | boolExp
// exp ::= INTEGER | VARIABLE |
// `true` | `false` |
// exp op exp |
// `if` exp exp exp |
// `let` VARIABLE `:` type `=` exp `in` exp
indirect enum Exp {
case IntegerExp(Int)
case VariableExp(String)
case TrueExp
case FalseExp
case BinopExp(Exp, Op, Exp)
case IfExp(Exp, Exp, Exp)
case LetExp(String, LangType, Exp, Exp)
}
func generateVarName() -> String {
if Int.random(in: 0..<2) == 0 {
return "x"
} else {
return "y"
}
}
func generateOp() -> Op {
switch Int.random(in: 0..<3) {
case 0: return Op.PlusOp
case 1: return Op.AndOp
case _: return Op.LessThanOp
}
}
func generateType() -> LangType {
if Int.random(in: 0..<2) == 0 {
return LangType.IntType
} else {
return LangType.BoolType
}
}
func generateExp() -> Exp {
switch Int.random(in: 0..<7) {
case 0: return Exp.IntegerExp(Int.random(in: 0..<100))
case 1: return Exp.VariableExp(generateVarName())
case 2: return Exp.TrueExp
case 3: return Exp.FalseExp
case 4: return Exp.BinopExp(generateExp(), generateOp(), generateExp())
case 5: return Exp.IfExp(generateExp(), generateExp(), generateExp())
case _: return Exp.LetExp(generateVarName(), generateType(), generateExp(), generateExp())
}
}
enum LangValue {
case IntValue(Int)
case BoolValue(Bool)
}
extension Exp {
func execute() {
if let _ = self.typecheck([:]) {
print("\(self): \(self.evaluate([:]))");
} else {
print("FAILED TO TYPECHECK: \(self)");
}
}
// Variable -> Type
// String -> Type
func typecheck(_ env: [String: LangType]) -> LangType? {
switch self {
case .IntegerExp(_): return LangType.IntType
case .VariableExp(let name):
if let varType = env[name] {
return varType
} else {
return nil
}
case .TrueExp: return LangType.BoolType
case .FalseExp: return LangType.BoolType
case let .BinopExp(leftExp, op, rightExp):
if let leftType = leftExp.typecheck(env) {
if let rightType = rightExp.typecheck(env) {
switch (leftType, op, rightType) {
case (.IntType, .PlusOp, .IntType): return LangType.IntType
case (.BoolType, .AndOp, .BoolType): return LangType.BoolType
case (.IntType, .LessThanOp, .IntType): return LangType.BoolType
case _: return nil
}
}
}
return nil;
case let .IfExp(guardExp, ifTrue, ifFalse):
if let guardType = guardExp.typecheck(env) {
if guardType == LangType.BoolType {
if let ifTrueType = ifTrue.typecheck(env) {
if let ifFalseType = ifFalse.typecheck(env) {
if ifTrueType == ifFalseType {
return ifTrueType
}
}
}
}
}
return nil;
case let .LetExp(varName, expectedVarType, initExp, bodyExp):
if let actualVarType = initExp.typecheck(env) {
if actualVarType == expectedVarType {
var newEnv = env;
newEnv[varName] = expectedVarType;
return bodyExp.typecheck(newEnv);
}
}
return nil;
}
} // typecheck
func evaluate(_ env: [String : LangValue]) -> LangValue {
switch self {
case .IntegerExp(let value): return LangValue.IntValue(value)
case .VariableExp(let name): return env[name]!
case .TrueExp: return LangValue.BoolValue(true)
case .FalseExp: return LangValue.BoolValue(false)
case let .BinopExp(leftExp, op, rightExp):
switch (leftExp.evaluate(env), op, rightExp.evaluate(env)) {
case let (.IntValue(l), .PlusOp, .IntValue(r)): return LangValue.IntValue(l + r)
case let (.BoolValue(l), .AndOp, .BoolValue(r)): return LangValue.BoolValue(l && r)
case let (.IntValue(l), .LessThanOp, .IntValue(r)): return LangValue.BoolValue(l < r)
case _:
assert(false);
return LangValue.IntValue(0);
}
case let .IfExp(guardExp, ifTrue, ifFalse):
if case let LangValue.BoolValue(guardValue) = guardExp.evaluate(env) {
if guardValue {
return ifTrue.evaluate(env)
} else {
return ifFalse.evaluate(env)
}
} else {
assert(false);
return LangValue.IntValue(0);
}
case let .LetExp(varName, _, initialValue, body):
var newEnv = env
newEnv[varName] = initialValue.evaluate(env)
return body.evaluate(newEnv)
}
}
}
// 3 + 2
let add = Exp.BinopExp(
Exp.IntegerExp(3),
Op.PlusOp,
Exp.IntegerExp(2));
// if (2 < 3) 5 else 7
let ifExample = Exp.IfExp(
Exp.BinopExp(
Exp.IntegerExp(2),
Op.LessThanOp,
Exp.IntegerExp(3)),
Exp.IntegerExp(5),
Exp.IntegerExp(7));
// let x: int = 7 in
// x + 5
let letExample = Exp.LetExp(
"x",
LangType.IntType,
Exp.IntegerExp(7),
Exp.BinopExp(
Exp.VariableExp("x"),
Op.PlusOp,
Exp.IntegerExp(5)));
// let x: int = true in x
let illTypedExample = Exp.LetExp(
"x",
LangType.IntType,
Exp.TrueExp,
Exp.VariableExp("x"));
// 1.) Typechecker of this language
// 2.) Evaluator of this language
// 3.) Fuzzer for this language (automatically generate ASTs)
while true {
generateExp().execute();
}