-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathexpression.go
More file actions
108 lines (92 loc) · 2.81 KB
/
expression.go
File metadata and controls
108 lines (92 loc) · 2.81 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
// expressions are evaluated with go's built-in primitives and stuff.
// panicking makes more sense here. If there is an user error deep inside some call stack somewhere in our application
// only the top-level error catcher should report it right?]
// No returned errors. We're panicking instead. All internal tools should return their error + details (Runtime error, Syntax, etc...)
// error.js will capture all panicked errors and hand them off to the enduser correctly.
package main
type Expression interface {
evaluate() interface{}
}
type BinaryExpression struct {
left Expression
operand Token // Operator? ExpressionOperator?
right Expression
}
type UnaryExpression struct {
operand Token
right Expression
}
type LiteralExpression struct {
value interface{}
}
type VariableExpression struct {
name string
}
type GroupingExpression struct {
expression Expression
}
func (e BinaryExpression) evaluate() interface{} { // to panic or to return an error....
left := e.left.evaluate()
right := e.right.evaluate()
switch e.operand.t {
case MINUS:
return left.(float64) - right.(float64) // this ok? These will panic if not true. Should handle those errors before.
case SLASH:
return left.(float64) / right.(float64) // this ok?
case STAR:
return left.(float64) * right.(float64) // this ok?
case PLUS: // we can string concat. Typeswitch on that.
switch left.(type) {
case string:
if _, ok := right.(string); ok {
return left.(string) + right.(string)
}
case float64: // maybe others?
if _, ok := right.(float64); ok { // this is ridicoulous.
return left.(float64) + right.(float64)
}
}
case GREATER:
return left.(float64) > right.(float64)
case GREATER_EQUAL:
return left.(float64) <= right.(float64)
case LESS:
return left.(float64) < right.(float64)
case LESS_EQUAL:
return left.(float64) <= right.(float64)
case BANG_EQUAL:
return !isEqual(left, right)
case EQUAL_EQUAL:
return isEqual(left, right)
}
return nil // if you reach thhis point, then you fucked up.
}
func (e UnaryExpression) evaluate() interface{} {
right := e.right.evaluate()
switch e.operand.t {
case MINUS:
return -right.(float64) // what if the user does -"I am a dumbass user" ?? not all casts are ok. Should throw error.
case BANG:
return !right.(bool) // same here.
}
return nil // this is bad. If this happens, then you--the code writer--messed up.
}
func (e LiteralExpression) evaluate() interface{} {
return e.value
}
func (e VariableExpression) evaluate() interface{} {
value := _globalEnvironment.get(e.name) // handle runtime error: name is not defined
return value
}
func (e GroupingExpression) evaluate() interface{} {
return e.expression.evaluate()
}
func isEqual(l, r interface{}) bool {
if l == nil && r == nil {
return true
}
if l == nil {
return false
}
return l == r
}