/
statement.go
224 lines (202 loc) · 5.4 KB
/
statement.go
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
219
220
221
222
223
224
/*
* gomacro - A Go interpreter with Lisp-like macros
*
* Copyright (C) 2017-2019 Massimiliano Ghilardi
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*
* statement.go
*
* Created on: Feb 13, 2017
* Author: Massimiliano Ghilardi
*/
package classic
import (
"go/ast"
"go/token"
r "reflect"
. "github.com/cosmos72/gomacro/base"
"github.com/cosmos72/gomacro/base/reflect"
)
type eBreak struct {
label string
}
type eContinue struct {
label string
}
func (_ eBreak) Error() string {
return "break outside for or switch"
}
func (_ eContinue) Error() string {
return "continue outside for"
}
type eReturn struct {
results []r.Value
}
func (_ eReturn) Error() string {
return "return outside function"
}
func (env *Env) evalBlock(block *ast.BlockStmt) (r.Value, []r.Value) {
if block == nil || len(block.List) == 0 {
return NoneR, nil
}
env = NewEnv(env, "{}")
return env.evalStatements(block.List)
}
func (env *Env) evalStatements(list []ast.Stmt) (r.Value, []r.Value) {
ret := NoneR
var rets []r.Value
for i := range list {
ret, rets = env.evalStatement(list[i])
}
return ret, rets
}
func (env *Env) evalStatement(stmt ast.Stmt) (r.Value, []r.Value) {
again:
if stmt != nil {
env.Pos = stmt.Pos()
}
switch node := stmt.(type) {
case *ast.AssignStmt:
return env.evalAssignments(node)
case *ast.BlockStmt:
return env.evalBlock(node)
case *ast.BranchStmt:
return env.evalBranch(node)
case *ast.CaseClause, *ast.CommClause:
return env.Errorf("misplaced case: not inside switch or select: %v <%v>", node, r.TypeOf(node))
case *ast.DeclStmt:
return env.evalDecl(node.Decl)
case *ast.DeferStmt:
return env.evalDefer(node.Call)
case *ast.EmptyStmt:
return NoneR, nil
case *ast.ExprStmt:
return env.evalExpr(node.X)
case *ast.ForStmt:
return env.evalFor(node)
case *ast.GoStmt:
return env.evalGo(node)
case *ast.IfStmt:
return env.evalIf(node)
case *ast.IncDecStmt:
return env.evalIncDec(node)
case *ast.LabeledStmt:
stmt = node
goto again
case *ast.RangeStmt:
return env.evalForRange(node)
case *ast.ReturnStmt:
return env.evalReturn(node)
case *ast.SelectStmt:
return env.evalSelect(node)
case *ast.SendStmt:
return env.evalSend(node)
case *ast.SwitchStmt:
return env.evalSwitch(node)
case *ast.TypeSwitchStmt:
return env.evalTypeSwitch(node)
default:
return env.Errorf("unimplemented statement: %v <%v>", node, r.TypeOf(node))
}
}
func (env *Env) evalBranch(node *ast.BranchStmt) (r.Value, []r.Value) {
var label string
if node.Label != nil {
label = node.Label.Name
}
switch node.Tok {
case token.BREAK:
panic(eBreak{label})
case token.CONTINUE:
panic(eContinue{label})
case token.GOTO:
return env.Errorf("unimplemented: goto")
case token.FALLTHROUGH:
return env.Errorf("invalid fallthrough: not the last statement in a case")
default:
return env.Errorf("unimplemented branch: %v <%v>", node, r.TypeOf(node))
}
}
func (env *Env) evalGo(stmt *ast.GoStmt) (r.Value, []r.Value) {
if !MultiThread {
env.Errorf("cannot create goroutine: %v\n\treason: this copy of gomacro was compiled with build tag 'gomacro_singlethread'", stmt)
}
node := stmt.Call
fun := env.evalExpr1(node.Fun)
switch fun.Kind() {
case r.Struct:
switch fun := fun.Interface().(type) {
case Constructor:
// evaluate args in the caller's goroutine
t, args := env.evalConstructorArgs(fun, node)
go fun.exec(env, t, args)
case Function:
// evaluate args in the caller's goroutine
args := env.evalFunctionArgs(fun, node)
go fun.exec(env, args)
}
case r.Func:
// evaluate args in the caller's goroutine
args := env.evalFuncArgs(fun, node)
if node.Ellipsis == token.NoPos {
go fun.Call(args)
} else {
go fun.CallSlice(args)
}
}
return NoneR, nil
}
func (env *Env) evalIf(node *ast.IfStmt) (r.Value, []r.Value) {
if node.Init != nil {
env = NewEnv(env, "if {}")
_, _ = env.evalStatement(node.Init)
}
cond, _ := env.EvalNode(node.Cond)
if cond.Kind() != r.Bool {
cf := cond.Interface()
return env.Errorf("if: invalid condition type <%T> %#v, expecting <bool>", cf, cf)
}
if cond.Bool() {
return env.evalBlock(node.Body)
} else if node.Else != nil {
return env.evalStatement(node.Else)
} else {
return NilR, nil
}
}
func (env *Env) evalIncDec(node *ast.IncDecStmt) (r.Value, []r.Value) {
var op token.Token
switch node.Tok {
case token.INC:
op = token.ADD_ASSIGN
case token.DEC:
op = token.SUB_ASSIGN
default:
return env.Errorf("unsupported *ast.IncDecStmt operation, expecting ++ or -- : %v <%v>", node, r.TypeOf(node))
}
place := env.evalPlace(node.X)
return env.assignPlace(place, op, One), nil
}
func (env *Env) evalSend(node *ast.SendStmt) (r.Value, []r.Value) {
channel := env.evalExpr1(node.Chan)
if channel.Kind() != r.Chan {
return env.Errorf("<- invoked on non-channel: %v evaluated to %v <%v>", node.Chan, channel, typeOf(channel))
}
value := env.evalExpr1(node.Value)
channel.Send(value)
return NoneR, nil
}
func (env *Env) evalReturn(node *ast.ReturnStmt) (r.Value, []r.Value) {
var rets []r.Value
if len(node.Results) == 1 {
// return foo() returns *all* the values returned by foo, not just the first one
rets = reflect.PackValuesR(env.evalExpr(node.Results[0]))
} else {
rets = env.evalExprs(node.Results)
}
panic(eReturn{rets})
}