-
Notifications
You must be signed in to change notification settings - Fork 17
/
interpreter.cr
166 lines (133 loc) · 4.4 KB
/
interpreter.cr
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
require "./interpreter/*"
require "./interpreter/nodes/*"
require "./interpreter/native_lib"
module Myst
class Interpreter
# Value stack the the interpreter uses to pass around arguments and values.
property stack : Array(MTValue)
# Value stack representing the current receiver value for operations.
property self_stack : Array(MTValue)
# Stack of scopes representing the current lexical scope.
property scope_stack : Array(Scope)
# Current stack of method calls/code blocks at any point in the interpreter.
property callstack : Callstack
# The Kernel module for this instance of the interpreter.
property kernel : TModule
# The true base type of every object in Myst.
property base_object : TType
# The base Type for all type objects.
property base_type : TType
# The number of warnings generated by execution of programs.
property warnings : Int32
# The pool of open file descriptors used to avoid duplicating entries.
getter fd_pool = {} of Int32 => IO
# A mapping of values to documentation about that value. Mostly applied
# to modules, types, and methods.
property doc_table : Hash(MTValue, DocComment)
def initialize(input : IO = STDIN, output : IO = STDOUT, errput : IO = STDERR)
fd_pool.merge!({
0 => input,
1 => output,
2 => errput
})
@stack = [] of MTValue
@scope_stack = [] of Scope
@callstack = Callstack.new
@kernel = TModule.new("Kernel")
@base_object = __make_type("Object", @kernel.scope, parent_type: nil)
init_base_object
@base_type = __make_type("Type", @kernel.scope, parent_type: @base_object)
init_base_type
@kernel = create_kernel
@self_stack = [@kernel] of MTValue
@warnings = 0
@doc_table = Hash(MTValue, DocComment).new
end
# input, output, and errput properties. These delegate to the entries in
# `fd_pool`, allowing them to be overridden either from the language
# itself, or from Crystal-land (e.g., for specs or through `Myst::VM`).
{% for stream, fd in {input: 0, output: 1, errput: 2} %}
def {{stream.id}}
fd_pool[{{fd}}]
end
def {{stream.id}}=(other)
fd_pool[{{fd}}] = other
end
{% end %}
def current_scope
scope_override || __scopeof(current_self)
end
def scope_override
@scope_stack.last?
end
def push_scope_override(scope : Scope = Scope.new)
scope.parent ||= current_scope
@scope_stack.push(scope)
end
def pop_scope_override
@scope_stack.pop
end
def pop_scope_override(to_size : Int)
return unless to_size >= 0
count_to_pop = @scope_stack.size - to_size
if count_to_pop > 0
@scope_stack.pop(count_to_pop)
end
end
def pop_callstack(to_size : Int)
return unless to_size >= 0
count_to_pop = @callstack.size - to_size
if count_to_pop > 0
@callstack.pop(count_to_pop)
end
end
def current_self
self_stack.last
end
def push_self(new_self : MTValue)
self_stack.push(new_self)
end
def pop_self
self_stack.pop
end
def pop_self(to_size : Int)
return unless to_size >= 0
count_to_pop = self_stack.size - to_size
if count_to_pop > 0
self_stack.pop(count_to_pop)
end
end
def visit(node : Node)
raise "Interpreter bug: #{node.class.name} nodes are not yet supported."
end
def warn(message : String, node : Node)
@warnings += 1
errput.puts("WARNING: #{message}")
errput.puts(" from `#{node.name}` at #{node.location.to_s}")
end
def put_error(error : RuntimeError)
value_to_s = recursive_lookup(error.value, "to_s").as(TFunctor)
result = Invocation.new(self, value_to_s, error.value, [] of MTValue, nil).invoke
errput.puts("Uncaught Exception: " + result.as(String))
errput.puts(error.trace)
end
def run(program, capture_errors=true)
visit(program)
rescue err : RuntimeError
if capture_errors
put_error(err)
else
raise err
end
rescue ex
raise ex unless capture_errors
errput.puts("Interpreter Error: #{ex.message}")
errput.puts
errput.puts("Myst backtrace: ")
errput.puts(callstack)
errput.puts
errput.puts("Native backtrace: ")
errput.puts(ex.inspect_with_backtrace)
end
end
end