forked from tpoindex/tsp
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtsp-generate-list.tcl
More file actions
307 lines (254 loc) · 12 KB
/
tsp-generate-list.tcl
File metadata and controls
307 lines (254 loc) · 12 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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# list commands
# lappend, lindex, llength, lrange, lset, list, lreplace, linsert
#########################################################
# generate code for "lset" command (assumed to be first parse word)
# varName must be a var type; string, int, boolean, double cause compile error
# return list of: type rhsVarName code
# note: no need to set rhsVarName as dirty, since we don't shadow var types
# and since we only "lightly" compile this command, gen_direct_tcl will
# handle shadow vars
#
proc ::tsp::gen_command_lset {compUnitDict tree} {
upvar $compUnitDict compUnit
if {[llength $tree] < 4} {
::tsp::addError compUnit "wrong # args: should be \"lset listVar index ?index...? value\""
return [list void "" ""]
}
set code "\n/***** ::tsp::gen_command_lset */\n"
set varname [::tsp::nodeText compUnit [lindex $tree 1]]
if {$varname eq ""} {
::tsp::addError compUnit "lset varName not a text word"
return [list void "" ""]
}
set type [::tsp::getVarType compUnit $varname]
if {$type eq "array" || $type eq "string" || $type eq "boolean" || $type eq "int" || $type eq "double"} {
::tsp::addError compUnit "lset varName must be type var, defined as : $type"
return [list void "" ""]
}
if {$type eq "undefined"} {
if {[::tsp::isProcArg compUnit $varname]} {
::tsp::addError compUnit "proc argument variable \"$varname\" not previously defined"
return [list void "" ""]
} elseif {[::tsp::isValidIdent $varname]} {
::tsp::addWarning compUnit "variable \"${varname}\" implicitly defined as type: \"var\" (lset)"
::tsp::setVarType compUnit $varname var
set type var
} else {
::tsp::addError compUnit "invalid identifier: \"$varname\""
return [list void "" ""]
}
set pre [::tsp::var_prefix $varname]
append code [::tsp::lang_assign_empty_zero $pre$varname var]
append code [::tsp::lang_preserve $pre$varname]
} else {
if {$type ne "var"} {
error "unexpected var type: $type\n[::tsp::currentLine compUnit]\n[::tsp::error_stacktrace]"
}
# varname exists
}
# if varname was not previously included as volatile, spill variable here and add to volatile list
if {[lsearch [dict get $compUnit volatile] $varname] == -1} {
append code [::tsp::lang_spill_vars compUnit $varname] \n
::tsp::append_volatile_list compUnit $varname
}
# generate the code to call the command, and append to existing code
set directResult [::tsp::gen_direct_tcl compUnit $tree]
lassign $directResult type rhsvar directCode
append code $directCode
return [list $type $rhsvar $code]
}
#########################################################
# generate code for "lappend" command (assumed to be first parse word)
# varName must be a var type; string, int, boolean, double cause compile error
# return list of: type rhsVarName code
# note: no need to set rhsVarName as dirty, since we don't shadow var types
# we still will use shadow var/dirty checking for args
#
proc ::tsp::gen_command_lappend {compUnitDict tree} {
upvar $compUnitDict compUnit
if {[llength $tree] < 2} {
::tsp::addError compUnit "wrong # args: should be \"lappend varName ?value value ...?\""
return [list void "" ""]
}
set varname [::tsp::nodeText compUnit [lindex $tree 1]]
if {$varname eq ""} {
::tsp::addError compUnit "lappend varName not a text word"
return [list void "" ""]
}
set code "\n/***** ::tsp::gen_command_lappend */\n"
set type [::tsp::getVarType compUnit $varname]
set pre [::tsp::var_prefix $varname]
if {$type eq "array" || $type eq "string" || $type eq "boolean" || $type eq "int" || $type eq "double"} {
::tsp::addError compUnit "lappend varName must be type var, defined as : $type"
return [list void "" ""]
}
if {$type eq "undefined"} {
if {[::tsp::isProcArg compUnit $varname]} {
::tsp::addError compUnit "proc argument variable \"$varname\" not previously defined"
return [list void "" ""]
} elseif {[::tsp::isValidIdent $varname]} {
::tsp::addWarning compUnit "variable \"${varname}\" implicitly defined as type: \"var\" (lappend)"
::tsp::setVarType compUnit $varname var
set type var
} else {
::tsp::addError compUnit "invalid identifier: \"$varname\""
return [list void "" ""]
}
append code [::tsp::lang_assign_empty_zero $pre$varname var]
append code [::tsp::lang_preserve $pre$varname]
} else {
if {$type ne "var"} {
error "unexpected var type: $type\n[::tsp::currentLine compUnit]\n[::tsp::error_stacktrace]"
}
# varname exists
if {[lsearch [dict get $compUnit volatile] $varname] != -1} {
# var was defined as volatile, we have to let interp run this as a direct command at this point
# generate the code to call the command, and append to existing code
set directResult [::tsp::gen_direct_tcl compUnit $tree]
lassign $directResult type rhsvar directCode
append code $directCode
return [list $type $rhsvar $code]
}
}
# dup if shared obj, or assign empty var if null
append code [::tsp::lang_dup_var_if_shared $pre$varname]
#FIXME: use shadow var and dirty checking
# append to var
set argVar [::tsp::get_tmpvar compUnit var]
set argVarComponents [list [list text $argVar $argVar]]
foreach node [lrange $tree 2 end] {
# assign arg into a tmp var type
set appendNodeComponents [::tsp::parse_word compUnit $node]
set appendNodeType [lindex [lindex $appendNodeComponents 0] 0]
if {$appendNodeType eq "invalid"} {
::tsp::addError compUnit "lappend argument parsed as \"$appendNodeType\""
return [list void "" ""]
}
set setTree ""
append code [lindex [::tsp::produce_set compUnit $setTree $argVarComponents $appendNodeComponents] 2]
# note - TclList.append with preserve() the argVar
append code [::tsp::lang_lappend_var $pre$varname $argVar]
}
# return the value
set pre [::tsp::var_prefix $varname]
return [list var $pre$varname $code]
}
#########################################################
# generate code for "llength" command (assumed to be first parse word)
# varName must be a var type; string, int, boolean, double cause compile error
# return list of: type rhsVarName code
#
proc ::tsp::gen_command_llength {compUnitDict tree} {
upvar $compUnitDict compUnit
if {[llength $tree] != 2} {
::tsp::addError compUnit "wrong # args: should be \"llength list\""
return [list void "" ""]
}
set code "\n/***** ::tsp::gen_command_llength */\n"
set argComponents [::tsp::parse_word compUnit [lindex $tree 1]]
set argComponentType [lindex [lindex $argComponents 0] 0]
if {$argComponentType eq "invalid"} {
::tsp::addError compUnit "invalid argument for llength, parsed as: $argComponentType"
return [list void "" ""]
}
if {$argComponentType eq "scalar"} {
set argVar [lindex [lindex $argComponents 0] 1]
set argType [::tsp::getVarType compUnit $argVar]
set pre [::tsp::var_prefix $argVar]
if {$argType eq "boolean" || $argType eq "int" || $argType eq "double"} {
# these have a length of 1 :-)
append code "/* llength of $argType : 1*/\n"
return [list int 1 $code]
} elseif {$argType eq "array"} {
::tsp::addError compUnit "llength argument must be type var, defined as : $argType"
return [list void "" ""]
} elseif {$argType eq "undefined"} {
::tsp::addWarning compUnit "llength argument \"$argVar\" is undefined"
return [list void "" ""]
} elseif {$argType eq "string"} {
# convert the string into a tmp var
#FIXME: use shadow var and dirty checking
set argTmpVar [::tsp::get_tmpvar compUnit var]
append code [::tsp::lang_assign_var_string $argTmpVar $pre$argVar]
set argVar $argTmpVar
} elseif {$argType eq "var"} {
set argVar $pre$argVar
} else {
error "llength: unexpected type: $argType \n[::tsp::currentLine compUnit]\n[::tsp::error_stacktrace]"
}
} else {
# it's text, command, or an array reference, convert into a var
set argTmpVar [::tsp::get_tmpvar compUnit var]
set argTmpComponents [list [list text $argTmpVar $argTmpVar]]
set setTree ""
append code [lindex [::tsp::produce_set compUnit $setTree $argTmpComponents $argComponents] 2]
set argVar $argTmpVar
}
set returnVar [::tsp::get_tmpvar compUnit int]
append code [::tsp::lang_llength $returnVar $argVar \
[::tsp::lang_quote_string [::tsp::gen_runtime_error compUnit "llength: can't convert argument to a list"]]]
return [list int $returnVar $code]
}
#########################################################
# generate code for "list" command (assumed to be first parse word)
# return list of: type rhsVarName code
#
proc ::tsp::gen_command_list {compUnitDict tree} {
upvar $compUnitDict compUnit
set code "\n/***** ::tsp::gen_command_list */\n"
set varName [::tsp::get_tmpvar compUnit var]
append code [::tsp::lang_safe_release $varName]
append code [::tsp::gen_objv_list compUnit [lrange $tree 1 end] $varName]
return [list var $varName $code]
}
#########################################################
# generate code for "lindex" command (assumed to be first parse word)
# varName must be a var type
# only compile simple case of one index, and only where index is an int or integer constant
# return list of: type rhsVarName code
#
proc ::tsp::gen_command_lindex {compUnitDict tree} {
upvar $compUnitDict compUnit
if {[llength $tree] < 2} {
::tsp::addError compUnit "wrong # args: should be \"lindex list ?index...?\""
return [list void "" ""]
}
set code "\n/***** ::tsp::gen_command_lindex */\n"
if {[llength $tree] != 3} {
# no index or multiple indexes, pass this to the lindex command
# generate the code to call the command, and append to existing code
set directResult [::tsp::gen_direct_tcl compUnit $tree]
lassign $directResult type rhsvar directCode
append code $directCode
return [list $type $rhsvar $code]
}
# list argument, make sure it is a list
# we'll assign it to another var if already a var :-(
#FIXME: use shadow var and dirty checking
set argComponents [::tsp::parse_word compUnit [lindex $tree 1]]
set argTmpVar [::tsp::get_tmpvar compUnit var]
set argTmpComponents [list [list text $argTmpVar $argTmpVar]]
set setTree ""
append code [lindex [::tsp::produce_set compUnit $setTree $argTmpComponents $argComponents] 2]
# index component, can either be an int type or an integer constant, anything else,
# let lindex have at it.
set idxResult [::tsp::get_index compUnit [lindex $tree 2]]
lassign $idxResult idxValid idxRef idxIsFromEnd convertCode
if {! $idxValid} {
set directResult [::tsp::gen_direct_tcl compUnit $tree]
lassign [lindex $directResult] type rhsvar directCode
append code $directCode
return [list $type $rhsvar $code]
} else {
if {[::tsp::literalExprTypes $idxRef] eq "stringliteral"} {
# not a int literal, so it must be a scalar
set pre [::tsp::var_prefix $idxRef]
set idxRef $pre$idxRef
}
append code $convertCode
}
set returnVar [::tsp::get_tmpvar compUnit var]
append code [::tsp::lang_lindex $returnVar $argTmpVar $idxRef $idxIsFromEnd \
[::tsp::lang_quote_string [::tsp::gen_runtime_error compUnit "lindex: can't convert argument to a list or index out of bounds"]]]
return [list var $returnVar $code]
}