-
Notifications
You must be signed in to change notification settings - Fork 6
/
tsp-compile.tcl
206 lines (178 loc) · 6.71 KB
/
tsp-compile.tcl
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
########################################
# compilation unit dictionary keys:
# file - name of file being sourced
# name - name of the proc
# args - list of args
# argTypes - list of corresponding arg types
# body - body
# returns - type
# vars - vars to type dict
# finalSpill - vars spill into interp on method end, for upvar/global/variable defined vars
# dirty - sub dict of native typed vars that need tmp obj updated before tcl invocation
# tmpvars - sub dict of temporary var types used
# tmpvarsUsed - sub dict of temporary var types used per command invocation, per type
# tmpvarsLocked - list of tmpvars locked, per type
# volatile - vars to spill/reload into tcl interp, for one command only
# frame - true/false if new var frame is required
# force - true to force compilation
# buf - code buffer, what is actually compiled
# breakable - count of nested while/for/foreach, when > 1 break/continue are allowed
# depth - count of block nesting level (if/while/for/foreach etc.)
# direct - list of other compiled procs called directly
# cmdLevel - count of nested commands (per outer word boundary)
# maxLevel - max level of nested commands
# catchLevel - count of nested catch statements
# argsPerLevel - sub dict of level number and argv lengths
# constNum - last constant value used
# constVar - sub dict of constant to const number
# lineNum - current line number
# errors - list of errors
# warnings - list of warnings
# compileType - normal = compile if able, none = don't compile, assert = Tcl error if not compilable, trace = enable Tcl tracing
# compiledReference - the Java class name or C function
proc ::tsp::init_compunit {file name procargs body} {
return [dict create \
file $file \
name $name \
args $procargs \
argTypes invalid \
body $body \
returns "" \
vars "" \
finalSpill "" \
dirty "" \
tmpvars [dict create boolean 0 int 0 double 0 string 0 var 0] \
tmpvarsUsed [dict create boolean 0 int 0 double 0 string 0 var 0] \
tmpvarsLocked "" \
volatile "" \
frame 0 \
force "" \
buf "" \
breakable 0 \
depth 0 \
direct "" \
cmdLevel 0 \
maxLevel 0 \
catchLevel 0 \
argsPerLevel [dict create] \
constNum 0 \
constVar [dict create] \
lineNum 1 \
errors "" \
warnings "" \
compileType "" \
compiledReference ""]
}
#########################################################
# compile a proc
proc ::tsp::compile_proc {file name procargs body} {
set compUnit [::tsp::init_compunit $file $name $procargs $body]
set procValid [::tsp::validProcName $name]
if {$procValid ne ""} {
::tsp::addError compUnit $procValid
::tsp::logErrorsWarnings compUnit
uplevel #0 [list ::proc $name $procargs $body]
return
}
set code ""
set errInf ""
set rc [ catch {set compileResult [::tsp::parse_body compUnit {0 end}] } errInf]
if {$rc != 0} {
error "tsp internal error: parse_body error: $errInf"
}
lassign $compileResult bodyType bodyRhs code
set errors [::tsp::getErrors compUnit]
set numErrors [llength $errors]
set returnType [dict get $compUnit returns]
set compileType [dict get $compUnit compileType]
if {$compileType eq "none"} {
::tsp::addWarning compUnit "compileType $compileType"
::tsp::logErrorsWarnings compUnit
uplevel #0 [list ::proc $name $procargs $body]
return
}
if {$returnType eq ""} {
::tsp::addError compUnit "invalid proc definition, no return type specified, likely missing #::tsp::procdef"
::tsp::logErrorsWarnings compUnit
if {$compileType eq "none" || $compileType eq "normal" || $compileType eq ""} {
uplevel #0 [list ::proc $name $procargs $body]
return
} else {
# else compileType is assert or trace, raise an error
error "invalid proc definition, no return type specified, likely missing #::tsp::procdef"
}
return
}
if {$numErrors > 0 } {
if {$compileType eq "assert" || $compileType eq "trace"} {
error "compile type: $compileType, proc $name, but resulted in errors:\n[join $errors \n]"
uplevel #0 [list ::proc $name $procargs $body]
} else {
# it's normal (or undefined), just define it
uplevel #0 [list ::proc $name $procargs $body]
}
::tsp::logErrorsWarnings compUnit
} else {
if {$compileType eq "trace"} {
# define proc with tracing commands
lassign [::tsp::init_traces compUnit $name $returnType] traces procTrace
append traces $body
uplevel #0 [list ::proc $name $procargs $traces]
uplevel #0 $procTrace
::tsp::logErrorsWarnings compUnit
} else {
# parse_body ok, let's see if we can compile it
set compilable [::tsp::lang_create_compilable compUnit $code]
::tsp::logCompilable compUnit $compilable
set rc [::tsp::lang_compile compUnit $compilable]
if {$rc == 0} {
::tsp::lang_interp_define compUnit
::tsp::addCompiledProc compUnit
} else {
# compile error, define as regular proc
uplevel #0 [list ::proc $name $procargs $body]
}
}
::tsp::logErrorsWarnings compUnit
}
}
#########################################################
# check if name is a legal identifier for compilation
# return "" if valid, other return error condition
#
proc ::tsp::validProcName {name} {
if {! [::tsp::isValidIdent $name]} {
return "invalid proc name: \"$name\" is not a valid identifier"
}
if {[lsearch [::tsp::getCompiledProcs] $name] >= 0} {
return "invalid proc name: \"$name\" has been previously defined and compiled"
}
if {[lsearch ::tsp::BUILTIN_TCL_COMMANDS $name] >= 0} {
return "invalid proc name: \"$name\" is builtin Tcl command"
}
return ""
}
#########################################################
# main tsp proc interface
#
proc ::tsp::proc {name argList body} {
set scriptfile [info script]
if {$scriptfile eq ""} {
set scriptfile _
}
::tsp::compile_proc $scriptfile $name $argList $body
return ""
}
#########################################################
# set INLINE mode, whether we should inline generated code
# or use functions where possible. value is true/false
#
proc ::tsp::setInline {value} {
if {[string is boolean -strict $value]} {
if {$value} {
set ::tsp::INLINE 1
} else {
set ::tsp::INLINE 0
}
}
}