loxxy 0.3.01 → 0.4.01
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -0
- data/CHANGELOG.md +42 -0
- data/README.md +63 -5
- data/lib/loxxy/ast/ast_builder.rb +55 -91
- data/lib/loxxy/ast/ast_visitor.rb +0 -2
- data/lib/loxxy/ast/lox_class_stmt.rb +8 -5
- data/lib/loxxy/ast/lox_fun_stmt.rb +12 -5
- data/lib/loxxy/ast/lox_node.rb +1 -1
- data/lib/loxxy/ast/lox_set_expr.rb +5 -2
- data/lib/loxxy/back_end/engine.rb +147 -50
- data/lib/loxxy/back_end/environment.rb +4 -0
- data/lib/loxxy/back_end/lox_class.rb +6 -8
- data/lib/loxxy/back_end/lox_function.rb +13 -7
- data/lib/loxxy/back_end/lox_instance.rb +14 -2
- data/lib/loxxy/back_end/resolver.rb +6 -0
- data/lib/loxxy/datatype/lx_string.rb +2 -3
- data/lib/loxxy/front_end/grammar.rb +16 -39
- data/lib/loxxy/front_end/scanner.rb +54 -28
- data/lib/loxxy/version.rb +1 -1
- data/loxxy.gemspec +2 -2
- data/spec/back_end/engine_spec.rb +13 -3
- data/spec/front_end/parser_spec.rb +4 -4
- data/spec/front_end/raw_parser_spec.rb +10 -3
- data/spec/front_end/scanner_spec.rb +13 -10
- data/spec/interpreter_spec.rb +44 -0
- data/spec/spec_helper.rb +1 -1
- metadata +5 -5
@@ -71,7 +71,6 @@ module Loxxy
|
|
71
71
|
# @param aClassStmt [AST::LOXClassStmt] the for statement node to visit
|
72
72
|
def visit_class_stmt(aClassStmt)
|
73
73
|
broadcast(:before_class_stmt, aClassStmt)
|
74
|
-
traverse_subnodes(aClassStmt) # The methods are visited here...
|
75
74
|
broadcast(:after_class_stmt, aClassStmt, self)
|
76
75
|
end
|
77
76
|
|
@@ -126,7 +125,6 @@ module Loxxy
|
|
126
125
|
# @param aSetExpr [AST::LOXGetExpr] the get expression node to visit
|
127
126
|
def visit_set_expr(aSetExpr)
|
128
127
|
broadcast(:before_set_expr, aSetExpr, self)
|
129
|
-
traverse_subnodes(aSetExpr)
|
130
128
|
broadcast(:after_set_expr, aSetExpr, self)
|
131
129
|
end
|
132
130
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'lox_node'
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
|
-
|
7
|
+
# A parse tree node that represents a Lox class declaration.
|
8
|
+
class LoxClassStmt < LoxNode
|
8
9
|
# @return [String] the class name
|
9
10
|
attr_reader :name
|
10
11
|
|
@@ -14,11 +15,13 @@ module Loxxy
|
|
14
15
|
# @return [Array<Ast::LoxFunStmt>] the methods
|
15
16
|
attr_reader :body
|
16
17
|
|
18
|
+
# Constructor for a parse node that represents a Lox function declaration
|
17
19
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
18
|
-
# @param
|
19
|
-
# @param
|
20
|
+
# @param aName [String] the class name
|
21
|
+
# @param aSuperclassName [String] the super class name
|
22
|
+
# @param theMethods [Array<Loxxy::Ast::LoxFunStmt>] the methods
|
20
23
|
def initialize(aPosition, aName, aSuperclassName, theMethods)
|
21
|
-
super(aPosition
|
24
|
+
super(aPosition)
|
22
25
|
@name = aName.dup
|
23
26
|
@superclass = aSuperclassName
|
24
27
|
@body = theMethods
|
@@ -4,17 +4,25 @@ require_relative 'lox_node'
|
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
|
-
#
|
7
|
+
# A parse tree node that represents a Lox function declaration.
|
8
8
|
class LoxFunStmt < LoxNode
|
9
|
+
# @return [String] the function name
|
9
10
|
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Array<String>] the parameter names
|
10
13
|
attr_reader :params
|
14
|
+
|
15
|
+
# @return [Ast::LoxBlockStmt] the parse tree representing the function's body
|
11
16
|
attr_reader :body
|
17
|
+
|
18
|
+
# @return [Boolean] true if the function is a method
|
12
19
|
attr_accessor :is_method
|
13
20
|
|
21
|
+
# Constructor for a parse node that represents a Lox function declaration
|
14
22
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
|
-
# @param aName [String]
|
16
|
-
# @param
|
17
|
-
# @param
|
23
|
+
# @param aName [String] the function name
|
24
|
+
# @param paramList [Array<String>] the parameter names
|
25
|
+
# @param aBody [Ast::LoxBlockStmt] the parse tree representing the function's body
|
18
26
|
def initialize(aPosition, aName, paramList, aBody)
|
19
27
|
super(aPosition)
|
20
28
|
@name = aName.dup
|
@@ -25,6 +33,5 @@ module Loxxy
|
|
25
33
|
|
26
34
|
define_accept # Add `accept` method as found in Visitor design pattern
|
27
35
|
end # class
|
28
|
-
# rubocop: enable Style/AccessorGrouping
|
29
36
|
end # module
|
30
37
|
end # module
|
data/lib/loxxy/ast/lox_node.rb
CHANGED
@@ -11,7 +11,7 @@ module Loxxy
|
|
11
11
|
# Let nodes take `visitee` role as defined in the Visitor design pattern
|
12
12
|
extend ASTVisitee
|
13
13
|
|
14
|
-
# return [Rley::Lexical::Position] Position of the entry in the input stream.
|
14
|
+
# @return [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
15
|
attr_reader :position
|
16
16
|
|
17
17
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
@@ -4,17 +4,20 @@ require_relative 'lox_compound_expr'
|
|
4
4
|
|
5
5
|
module Loxxy
|
6
6
|
module Ast
|
7
|
-
class LoxSetExpr <
|
7
|
+
class LoxSetExpr < LoxNode
|
8
8
|
# @return [Ast::LoxNode] the object to which the property belongs to
|
9
9
|
attr_reader :object
|
10
10
|
|
11
11
|
# @return [String] Name of an object property
|
12
12
|
attr_accessor :property
|
13
13
|
|
14
|
+
# @return [LoxNode, Datatype] value to assign
|
15
|
+
attr_accessor :value
|
16
|
+
|
14
17
|
# @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
|
15
18
|
# @param anObject [Ast::LoxNode] The object which the given property is being set
|
16
19
|
def initialize(aPosition, anObject)
|
17
|
-
super(aPosition
|
20
|
+
super(aPosition)
|
18
21
|
@object = anObject
|
19
22
|
end
|
20
23
|
|
@@ -20,7 +20,7 @@ module Loxxy
|
|
20
20
|
# @return [BackEnd::SymbolTable]
|
21
21
|
attr_reader :symbol_table
|
22
22
|
|
23
|
-
# @return [Array<Datatype::BuiltinDatatype>] Data stack for
|
23
|
+
# @return [Array<Datatype::BuiltinDatatype>] Data stack for arguments and return results
|
24
24
|
attr_reader :stack
|
25
25
|
|
26
26
|
# @return [Hash { Symbol => UnaryOperator}]
|
@@ -35,17 +35,31 @@ module Loxxy
|
|
35
35
|
# @param theOptions [Hash]
|
36
36
|
def initialize(theOptions)
|
37
37
|
@config = theOptions
|
38
|
+
@istream = config.include?(:istream) ? config[:istream] : $stdin
|
38
39
|
@ostream = config.include?(:ostream) ? config[:ostream] : $stdout
|
39
40
|
@symbol_table = SymbolTable.new
|
40
41
|
@stack = []
|
41
|
-
@unary_operators = {}
|
42
|
-
@binary_operators = {}
|
43
42
|
|
43
|
+
reset_expr_stack
|
44
44
|
init_unary_operators
|
45
45
|
init_binary_operators
|
46
46
|
init_globals
|
47
47
|
end
|
48
48
|
|
49
|
+
# Returns the current environment
|
50
|
+
# @return [Loxxy::BackEnd::Environment]
|
51
|
+
def current_env
|
52
|
+
symbol_table.current_env
|
53
|
+
end
|
54
|
+
|
55
|
+
def expr_stack
|
56
|
+
current_env.expr_stack
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset_expr_stack
|
60
|
+
current_env.expr_stack.clear
|
61
|
+
end
|
62
|
+
|
49
63
|
# Given an abstract syntax parse tree visitor, launch the visit
|
50
64
|
# and execute the visit events in the output stream.
|
51
65
|
# @param aVisitor [AST::ASTVisitor]
|
@@ -58,7 +72,7 @@ module Loxxy
|
|
58
72
|
aVisitor.subscribe(self)
|
59
73
|
aVisitor.start
|
60
74
|
aVisitor.unsubscribe(self)
|
61
|
-
|
75
|
+
expr_stack.empty? ? Datatype::Nil.instance : expr_stack.pop
|
62
76
|
end
|
63
77
|
|
64
78
|
##########################################################################
|
@@ -69,10 +83,14 @@ module Loxxy
|
|
69
83
|
# Do nothing, subnodes were already evaluated
|
70
84
|
end
|
71
85
|
|
86
|
+
def before_class_stmt(_class_stmt)
|
87
|
+
reset_expr_stack
|
88
|
+
end
|
89
|
+
|
72
90
|
def after_class_stmt(aClassStmt, aVisitor)
|
73
91
|
if aClassStmt.superclass
|
74
92
|
aClassStmt.superclass.accept(aVisitor)
|
75
|
-
parent =
|
93
|
+
parent = expr_stack.pop
|
76
94
|
unless parent.kind_of?(LoxClass)
|
77
95
|
raise Loxxy::RuntimeError, 'Superclass must be a class.'
|
78
96
|
end
|
@@ -89,7 +107,7 @@ module Loxxy
|
|
89
107
|
meths = aClassStmt.body.map do |func_node|
|
90
108
|
func_node.is_method = true
|
91
109
|
func_node.accept(aVisitor)
|
92
|
-
mth =
|
110
|
+
mth = expr_stack.pop
|
93
111
|
mth.is_initializer = true if mth.name == 'init'
|
94
112
|
mth
|
95
113
|
end
|
@@ -104,11 +122,15 @@ module Loxxy
|
|
104
122
|
symbol_table.insert(new_var)
|
105
123
|
end
|
106
124
|
|
125
|
+
def before_var_stmt(_var_stmt)
|
126
|
+
reset_expr_stack
|
127
|
+
end
|
128
|
+
|
107
129
|
def after_var_stmt(aVarStmt)
|
108
130
|
new_var = Variable.new(aVarStmt.name, Datatype::Nil.instance)
|
109
131
|
symbol_table.insert(new_var)
|
110
132
|
|
111
|
-
value =
|
133
|
+
value = expr_stack.pop
|
112
134
|
new_var.assign(value)
|
113
135
|
end
|
114
136
|
|
@@ -116,9 +138,14 @@ module Loxxy
|
|
116
138
|
before_block_stmt(aForStmt)
|
117
139
|
end
|
118
140
|
|
141
|
+
def before_if_stmt(_if_stmt)
|
142
|
+
reset_expr_stack
|
143
|
+
end
|
144
|
+
|
119
145
|
def after_if_stmt(anIfStmt, aVisitor)
|
120
146
|
# Retrieve the result of the condition evaluation
|
121
|
-
condition = stack.pop
|
147
|
+
# condition = stack.pop
|
148
|
+
condition = expr_stack.pop
|
122
149
|
if condition.truthy?
|
123
150
|
anIfStmt.then_stmt.accept(aVisitor)
|
124
151
|
elsif anIfStmt.else_stmt
|
@@ -126,18 +153,31 @@ module Loxxy
|
|
126
153
|
end
|
127
154
|
end
|
128
155
|
|
156
|
+
def before_print_stmt(_print_stmt)
|
157
|
+
reset_expr_stack
|
158
|
+
end
|
159
|
+
|
129
160
|
def after_print_stmt(_printStmt)
|
130
|
-
tos =
|
161
|
+
tos = expr_stack.pop
|
131
162
|
@ostream.print tos ? tos.to_str : 'nil'
|
132
163
|
end
|
133
164
|
|
165
|
+
def before_return_stmt(_return_stmt)
|
166
|
+
reset_expr_stack
|
167
|
+
end
|
168
|
+
|
134
169
|
def after_return_stmt(_returnStmt, _aVisitor)
|
170
|
+
stack.push(expr_stack.pop)
|
135
171
|
throw(:return)
|
136
172
|
end
|
137
173
|
|
174
|
+
def before_while_stmt(_while_stmt)
|
175
|
+
reset_expr_stack
|
176
|
+
end
|
177
|
+
|
138
178
|
def after_while_stmt(aWhileStmt, aVisitor)
|
139
179
|
loop do
|
140
|
-
condition =
|
180
|
+
condition = expr_stack.pop
|
141
181
|
break unless condition.truthy?
|
142
182
|
|
143
183
|
aWhileStmt.body.accept(aVisitor)
|
@@ -146,6 +186,7 @@ module Loxxy
|
|
146
186
|
end
|
147
187
|
|
148
188
|
def before_block_stmt(_aBlockStmt)
|
189
|
+
reset_expr_stack
|
149
190
|
new_env = Environment.new
|
150
191
|
symbol_table.enter_environment(new_env)
|
151
192
|
end
|
@@ -159,40 +200,42 @@ module Loxxy
|
|
159
200
|
variable = variable_lookup(anAssignExpr)
|
160
201
|
raise Loxxy::RuntimeError, "Undefined variable '#{var_name}'." unless variable
|
161
202
|
|
162
|
-
value =
|
203
|
+
value = expr_stack.last # ToS remains since an assignment produces a value
|
163
204
|
variable.assign(value)
|
164
205
|
end
|
165
206
|
|
166
|
-
def before_set_expr(
|
167
|
-
|
168
|
-
aSetExpr.object.accept(aVisitor)
|
207
|
+
def before_set_expr(_set_expr, _visitor)
|
208
|
+
reset_expr_stack
|
169
209
|
end
|
170
210
|
|
171
|
-
def after_set_expr(aSetExpr,
|
172
|
-
|
173
|
-
|
211
|
+
def after_set_expr(aSetExpr, aVisitor)
|
212
|
+
# Evaluate receiver object part (i.e. 'this')
|
213
|
+
aSetExpr.object.accept(aVisitor)
|
214
|
+
assignee = expr_stack.pop
|
174
215
|
unless assignee.kind_of?(LoxInstance)
|
175
216
|
raise Loxxy::RuntimeError, 'Only instances have fields.'
|
176
217
|
end
|
177
218
|
|
219
|
+
aSetExpr.value.accept(aVisitor)
|
220
|
+
value = expr_stack.pop
|
221
|
+
|
178
222
|
assignee.set(aSetExpr.property, value)
|
179
|
-
|
223
|
+
expr_stack.push value
|
180
224
|
end
|
181
225
|
|
182
226
|
def after_logical_expr(aLogicalExpr, visitor)
|
183
227
|
op = aLogicalExpr.operator
|
184
|
-
operand1 =
|
228
|
+
operand1 = expr_stack.pop # only first operand was evaluated
|
185
229
|
result = nil
|
186
230
|
if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
|
187
231
|
result = operand1
|
188
232
|
else
|
189
233
|
raw_operand2 = aLogicalExpr.subnodes[1]
|
190
234
|
raw_operand2.accept(visitor) # Visit means operand2 is evaluated
|
191
|
-
operand2 =
|
235
|
+
operand2 = expr_stack.pop
|
192
236
|
result = logical_2nd_arg(operand2)
|
193
237
|
end
|
194
|
-
|
195
|
-
stack.push result
|
238
|
+
expr_stack.push result
|
196
239
|
end
|
197
240
|
|
198
241
|
def logical_2nd_arg(operand2)
|
@@ -212,14 +255,14 @@ module Loxxy
|
|
212
255
|
end
|
213
256
|
|
214
257
|
def after_binary_expr(aBinaryExpr)
|
215
|
-
operand2 =
|
216
|
-
operand1 =
|
258
|
+
operand2 = expr_stack.pop
|
259
|
+
operand1 = expr_stack.pop
|
217
260
|
op = aBinaryExpr.operator
|
218
261
|
operator = binary_operators[op]
|
219
262
|
operator.validate_operands(operand1, operand2)
|
220
263
|
if operand1.respond_to?(op)
|
221
264
|
result = operand1.send(op, operand2)
|
222
|
-
|
265
|
+
expr_stack.push convert2lox_datatype(result)
|
223
266
|
else
|
224
267
|
msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
|
225
268
|
raise StandardError, msg1
|
@@ -227,13 +270,13 @@ module Loxxy
|
|
227
270
|
end
|
228
271
|
|
229
272
|
def after_unary_expr(anUnaryExpr)
|
230
|
-
operand =
|
273
|
+
operand = expr_stack.pop
|
231
274
|
op = anUnaryExpr.operator
|
232
275
|
operator = unary_operators[op]
|
233
276
|
operator.validate_operand(operand)
|
234
277
|
if operand.respond_to?(op)
|
235
278
|
result = operand.send(op)
|
236
|
-
|
279
|
+
expr_stack.push convert2lox_datatype(result)
|
237
280
|
else
|
238
281
|
msg1 = "`#{op}': Unimplemented operator for a #{operand.class}."
|
239
282
|
raise StandardError, msg1
|
@@ -243,12 +286,17 @@ module Loxxy
|
|
243
286
|
def after_call_expr(aCallExpr, aVisitor)
|
244
287
|
# Evaluate callee part
|
245
288
|
aCallExpr.callee.accept(aVisitor)
|
246
|
-
callee =
|
289
|
+
callee = expr_stack.pop
|
290
|
+
before_size = expr_stack.size
|
247
291
|
aCallExpr.arguments.reverse_each { |arg| arg.accept(aVisitor) }
|
292
|
+
after_size = expr_stack.size
|
293
|
+
if after_size > before_size
|
294
|
+
stack.concat(expr_stack.pop(after_size - before_size))
|
295
|
+
end
|
248
296
|
|
249
297
|
case callee
|
250
298
|
when NativeFunction
|
251
|
-
|
299
|
+
expr_stack.push callee.call # Pass arguments
|
252
300
|
when LoxFunction, LoxClass
|
253
301
|
arg_count = aCallExpr.arguments.size
|
254
302
|
if arg_count != callee.arity
|
@@ -263,12 +311,12 @@ module Loxxy
|
|
263
311
|
|
264
312
|
def after_get_expr(aGetExpr, aVisitor)
|
265
313
|
aGetExpr.object.accept(aVisitor)
|
266
|
-
instance =
|
314
|
+
instance = expr_stack.pop
|
267
315
|
unless instance.kind_of?(LoxInstance)
|
268
316
|
raise Loxxy::RuntimeError, 'Only instances have properties.'
|
269
317
|
end
|
270
318
|
|
271
|
-
|
319
|
+
expr_stack.push instance.get(aGetExpr.property)
|
272
320
|
end
|
273
321
|
|
274
322
|
def after_grouping_expr(_groupingExpr)
|
@@ -278,14 +326,17 @@ module Loxxy
|
|
278
326
|
def after_variable_expr(aVarExpr, aVisitor)
|
279
327
|
var_name = aVarExpr.name
|
280
328
|
var = variable_lookup(aVarExpr)
|
281
|
-
|
329
|
+
unless var
|
330
|
+
pos = "line #{aVarExpr.position.line}:#{aVarExpr.position.column}"
|
331
|
+
raise Loxxy::RuntimeError, "[#{pos}] Undefined variable '#{var_name}'."
|
332
|
+
end
|
282
333
|
|
283
334
|
var.value.accept(aVisitor) # Evaluate variable value then push on stack
|
284
335
|
end
|
285
336
|
|
286
337
|
# @param literalExpr [Ast::LoxLiteralExpr]
|
287
338
|
def before_literal_expr(literalExpr)
|
288
|
-
|
339
|
+
expr_stack.push(literalExpr.literal)
|
289
340
|
end
|
290
341
|
|
291
342
|
def after_this_expr(aThisExpr, aVisitor)
|
@@ -304,18 +355,22 @@ module Loxxy
|
|
304
355
|
raise Loxxy::RuntimeError, "Undefined property '#{aSuperExpr.property}'."
|
305
356
|
end
|
306
357
|
|
307
|
-
|
358
|
+
expr_stack.push method.bind(instance)
|
308
359
|
end
|
309
360
|
|
310
361
|
# @param aValue [Ast::BuiltinDattype] the built-in datatype value
|
311
362
|
def before_visit_builtin(aValue)
|
312
|
-
|
363
|
+
expr_stack.push(aValue)
|
364
|
+
end
|
365
|
+
|
366
|
+
def before_fun_stmt(_fun_stmt, _visitor)
|
367
|
+
reset_expr_stack
|
313
368
|
end
|
314
369
|
|
315
370
|
def after_fun_stmt(aFunStmt, _visitor)
|
316
371
|
function = LoxFunction.new(aFunStmt.name, aFunStmt.params, aFunStmt.body, self)
|
317
372
|
if aFunStmt.is_method
|
318
|
-
|
373
|
+
expr_stack.push function
|
319
374
|
else
|
320
375
|
new_var = Variable.new(aFunStmt.name, function)
|
321
376
|
symbol_table.insert(new_var)
|
@@ -337,21 +392,9 @@ module Loxxy
|
|
337
392
|
env.defns[aVarNode.name]
|
338
393
|
end
|
339
394
|
|
340
|
-
NativeFunction = Struct.new(:callable, :interp) do
|
341
|
-
def accept(_visitor)
|
342
|
-
interp.stack.push self
|
343
|
-
end
|
344
|
-
|
345
|
-
def call
|
346
|
-
callable.call
|
347
|
-
end
|
348
|
-
|
349
|
-
def to_str
|
350
|
-
'<native fn>'
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
395
|
def init_unary_operators
|
396
|
+
@unary_operators = {}
|
397
|
+
|
355
398
|
negate_op = UnaryOperator.new('-', [Datatype::Number])
|
356
399
|
unary_operators[:-@] = negate_op
|
357
400
|
|
@@ -361,6 +404,8 @@ module Loxxy
|
|
361
404
|
end
|
362
405
|
|
363
406
|
def init_binary_operators
|
407
|
+
@binary_operators = {}
|
408
|
+
|
364
409
|
plus_op = BinaryOperator.new('+', [[Datatype::Number, :idem],
|
365
410
|
[Datatype::LXString, :idem]])
|
366
411
|
binary_operators[:+] = plus_op
|
@@ -395,6 +440,24 @@ module Loxxy
|
|
395
440
|
|
396
441
|
def init_globals
|
397
442
|
add_native_fun('clock', native_clock)
|
443
|
+
add_native_fun('getc', native_getc)
|
444
|
+
add_native_fun('chr', native_chr)
|
445
|
+
add_native_fun('exit', native_exit)
|
446
|
+
add_native_fun('print_error', native_print_error)
|
447
|
+
end
|
448
|
+
|
449
|
+
NativeFunction = Struct.new(:callable, :interp) do
|
450
|
+
def accept(_visitor)
|
451
|
+
interp.expr_stack.push self
|
452
|
+
end
|
453
|
+
|
454
|
+
def call
|
455
|
+
callable.call
|
456
|
+
end
|
457
|
+
|
458
|
+
def to_str
|
459
|
+
'<native fn>'
|
460
|
+
end
|
398
461
|
end
|
399
462
|
|
400
463
|
def add_native_fun(aName, aProc)
|
@@ -411,6 +474,40 @@ module Loxxy
|
|
411
474
|
end
|
412
475
|
end
|
413
476
|
|
477
|
+
# Read a single character and return the character code as an integer.
|
478
|
+
# LoxLox requires the end of input to be a negative number
|
479
|
+
def native_getc
|
480
|
+
proc do
|
481
|
+
ch = @istream.getc
|
482
|
+
val = ch ? ch.codepoints[0] : -1
|
483
|
+
Datatype::Number.new(val)
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
# chr(ch): Convert given character code number to a single-character string.
|
488
|
+
def native_chr
|
489
|
+
proc do
|
490
|
+
codepoint = stack.pop
|
491
|
+
Datatype::LXString.new(codepoint.value.chr)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# exit(status): Exit with given status code.
|
496
|
+
def native_exit
|
497
|
+
proc do
|
498
|
+
status = stack.pop
|
499
|
+
exit(status.value)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# print_error(message): Print message string on stderr.
|
504
|
+
def native_print_error
|
505
|
+
proc do
|
506
|
+
message = stack.pop
|
507
|
+
$stderr.print message.value
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
414
511
|
def convert2lox_datatype(item)
|
415
512
|
case item
|
416
513
|
when TrueClass then Datatype::True.instance
|