loxxy 0.0.24 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +74 -0
- data/README.md +52 -12
- data/lib/loxxy/ast/all_lox_nodes.rb +5 -0
- data/lib/loxxy/ast/ast_builder.rb +88 -81
- data/lib/loxxy/ast/ast_visitor.rb +51 -9
- data/lib/loxxy/ast/lox_block_stmt.rb +27 -0
- data/lib/loxxy/ast/lox_call_expr.rb +25 -0
- data/lib/loxxy/ast/lox_for_stmt.rb +41 -0
- data/lib/loxxy/ast/lox_fun_stmt.rb +32 -0
- data/lib/loxxy/ast/lox_if_stmt.rb +3 -1
- data/lib/loxxy/ast/lox_while_stmt.rb +32 -0
- data/lib/loxxy/back_end/engine.rb +102 -1
- data/lib/loxxy/back_end/environment.rb +3 -3
- data/lib/loxxy/back_end/function.rb +45 -0
- data/lib/loxxy/back_end/symbol_table.rb +3 -3
- data/lib/loxxy/front_end/grammar.rb +34 -34
- data/lib/loxxy/front_end/literal.rb +1 -1
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/environment_spec.rb +1 -1
- data/spec/back_end/symbol_table_spec.rb +1 -1
- data/spec/datatype/lx_string_spec.rb +2 -0
- data/spec/datatype/number_spec.rb +3 -1
- data/spec/front_end/scanner_spec.rb +2 -0
- data/spec/interpreter_spec.rb +126 -2
- metadata +8 -2
@@ -9,9 +9,9 @@ module Loxxy
|
|
9
9
|
# of a relation or a relation definition.
|
10
10
|
# It contains a map of names to the objects they name (e.g. logical var)
|
11
11
|
class Environment
|
12
|
-
# The parent
|
12
|
+
# The enclosing (parent) environment.
|
13
13
|
# @return [Environment, NilClass]
|
14
|
-
attr_accessor :
|
14
|
+
attr_accessor :enclosing
|
15
15
|
|
16
16
|
# Mapping from user-defined name to related definition
|
17
17
|
# (say, a variable object)
|
@@ -21,7 +21,7 @@ module Loxxy
|
|
21
21
|
# Construct a environment instance.
|
22
22
|
# @param aParent [Environment, NilClass] Parent environment to this one.
|
23
23
|
def initialize(aParent = nil)
|
24
|
-
@
|
24
|
+
@enclosing = aParent
|
25
25
|
@defns = {}
|
26
26
|
end
|
27
27
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../datatype/all_datatypes'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module BackEnd
|
7
|
+
# Representation of a Lox function.
|
8
|
+
# It is a named slot that can be associated with a value at the time.
|
9
|
+
class Function
|
10
|
+
|
11
|
+
# @return [String]
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# @return [Array<>] the parameters
|
15
|
+
attr_reader :parameters
|
16
|
+
|
17
|
+
attr_reader :body
|
18
|
+
|
19
|
+
attr_reader :stack
|
20
|
+
|
21
|
+
# Create a variable with given name and initial value
|
22
|
+
# @param aName [String] The name of the variable
|
23
|
+
# @param aValue [Datatype::BuiltinDatatype] the initial assigned value
|
24
|
+
def initialize(aName, parameterList, aBody, aStack)
|
25
|
+
@name = aName.dup
|
26
|
+
@parameters = parameterList
|
27
|
+
@body = aBody
|
28
|
+
@stack = aStack
|
29
|
+
end
|
30
|
+
|
31
|
+
def accept(_visitor)
|
32
|
+
stack.push self
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(aVisitor)
|
36
|
+
body.empty? ? Datatype::Nil.instance : body.accept(aVisitor)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Text representation of a Lox function
|
40
|
+
def to_str
|
41
|
+
"<fn #{name}>"
|
42
|
+
end
|
43
|
+
end # class
|
44
|
+
end # module
|
45
|
+
end # module
|
@@ -44,7 +44,7 @@ module Loxxy
|
|
44
44
|
# to be a child of current environment and to be itself the new current environment.
|
45
45
|
# @param anEnv [BackEnd::Environment] the Environment that
|
46
46
|
def enter_environment(anEnv)
|
47
|
-
anEnv.
|
47
|
+
anEnv.enclosing = current_env
|
48
48
|
@current_env = anEnv
|
49
49
|
end
|
50
50
|
|
@@ -60,7 +60,7 @@ module Loxxy
|
|
60
60
|
end
|
61
61
|
raise StandardError, 'Cannot remove root environment.' if current_env == root
|
62
62
|
|
63
|
-
@current_env = current_env.
|
63
|
+
@current_env = current_env.enclosing
|
64
64
|
end
|
65
65
|
|
66
66
|
# Add an entry with given name to current environment.
|
@@ -114,7 +114,7 @@ module Loxxy
|
|
114
114
|
while skope
|
115
115
|
vars_of_environment = skope.defns.select { |_, item| item.kind_of?(Variable) }
|
116
116
|
vars = vars_of_environment.values.concat(vars)
|
117
|
-
skope = skope.
|
117
|
+
skope = skope.enclosing
|
118
118
|
end
|
119
119
|
|
120
120
|
vars
|
@@ -44,7 +44,7 @@ module Loxxy
|
|
44
44
|
rule('function_star' => 'function_star function')
|
45
45
|
rule('function_star' => [])
|
46
46
|
|
47
|
-
rule('funDecl' => 'FUN function')
|
47
|
+
rule('funDecl' => 'FUN function').as 'fun_decl'
|
48
48
|
|
49
49
|
rule('varDecl' => 'VAR IDENTIFIER SEMICOLON').as 'var_declaration'
|
50
50
|
rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON').as 'var_initialization'
|
@@ -60,12 +60,12 @@ module Loxxy
|
|
60
60
|
|
61
61
|
rule('exprStmt' => 'expression SEMICOLON').as 'exprStmt'
|
62
62
|
|
63
|
-
rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
|
64
|
-
rule('forControl' => 'forInitialization forTest forUpdate')
|
63
|
+
rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement').as 'for_stmt'
|
64
|
+
rule('forControl' => 'forInitialization forTest forUpdate').as 'for_control'
|
65
65
|
rule('forInitialization' => 'varDecl')
|
66
66
|
rule('forInitialization' => 'exprStmt')
|
67
|
-
rule('forInitialization' => 'SEMICOLON')
|
68
|
-
rule('forTest' => 'expression_opt SEMICOLON')
|
67
|
+
rule('forInitialization' => 'SEMICOLON').as 'empty_for_initialization'
|
68
|
+
rule('forTest' => 'expression_opt SEMICOLON').as 'for_test'
|
69
69
|
rule('forUpdate' => 'expression_opt')
|
70
70
|
|
71
71
|
rule('ifStmt' => 'IF ifCondition statement elsePart_opt').as 'if_stmt'
|
@@ -75,9 +75,9 @@ module Loxxy
|
|
75
75
|
|
76
76
|
rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
|
77
77
|
rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
|
78
|
-
rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement')
|
79
|
-
rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
|
80
|
-
rule('block' => 'LEFT_BRACE RIGHT_BRACE')
|
78
|
+
rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement').as 'while_stmt'
|
79
|
+
rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE').as 'block_stmt'
|
80
|
+
rule('block' => 'LEFT_BRACE RIGHT_BRACE').as 'block_empty'
|
81
81
|
|
82
82
|
# Expressions: produce values
|
83
83
|
rule('expression_opt' => 'expression')
|
@@ -88,37 +88,37 @@ module Loxxy
|
|
88
88
|
rule('owner_opt' => 'call DOT')
|
89
89
|
rule('owner_opt' => [])
|
90
90
|
rule('logic_or' => 'logic_and')
|
91
|
-
rule('logic_or' => 'logic_and disjunct_plus').as '
|
92
|
-
rule('disjunct_plus' => 'disjunct_plus OR logic_and').as '
|
93
|
-
rule('disjunct_plus' => 'OR logic_and').as '
|
91
|
+
rule('logic_or' => 'logic_and disjunct_plus').as 'logical_expr'
|
92
|
+
rule('disjunct_plus' => 'disjunct_plus OR logic_and').as 'binary_plus_more'
|
93
|
+
rule('disjunct_plus' => 'OR logic_and').as 'binary_plus_end'
|
94
94
|
rule('logic_and' => 'equality')
|
95
|
-
rule('logic_and' => 'equality conjunct_plus').as '
|
96
|
-
rule('conjunct_plus' => 'conjunct_plus AND equality').as '
|
97
|
-
rule('conjunct_plus' => 'AND equality').as '
|
95
|
+
rule('logic_and' => 'equality conjunct_plus').as 'logical_expr'
|
96
|
+
rule('conjunct_plus' => 'conjunct_plus AND equality').as 'binary_plus_more'
|
97
|
+
rule('conjunct_plus' => 'AND equality').as 'binary_plus_end'
|
98
98
|
rule('equality' => 'comparison')
|
99
|
-
rule('equality' => 'comparison equalityTest_plus').as '
|
100
|
-
rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as '
|
101
|
-
rule('equalityTest_plus' => 'equalityTest comparison').as '
|
99
|
+
rule('equality' => 'comparison equalityTest_plus').as 'binary_operator'
|
100
|
+
rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison').as 'binary_plus_more'
|
101
|
+
rule('equalityTest_plus' => 'equalityTest comparison').as 'binary_plus_end'
|
102
102
|
rule('equalityTest' => 'BANG_EQUAL')
|
103
103
|
rule('equalityTest' => 'EQUAL_EQUAL')
|
104
104
|
rule('comparison' => 'term')
|
105
|
-
rule('comparison' => 'term comparisonTest_plus').as '
|
105
|
+
rule('comparison' => 'term comparisonTest_plus').as 'binary_operator'
|
106
106
|
rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
|
107
|
-
rule('comparisonTest_plus' => 'comparisonTest term').as '
|
107
|
+
rule('comparisonTest_plus' => 'comparisonTest term').as 'binary_plus_end'
|
108
108
|
rule('comparisonTest' => 'GREATER')
|
109
109
|
rule('comparisonTest' => 'GREATER_EQUAL')
|
110
110
|
rule('comparisonTest' => 'LESS')
|
111
111
|
rule('comparisonTest' => 'LESS_EQUAL')
|
112
112
|
rule('term' => 'factor')
|
113
|
-
rule('term' => 'factor additive_plus').as '
|
114
|
-
rule('additive_plus' => 'additive_plus additionOp factor').as '
|
115
|
-
rule('additive_plus' => 'additionOp factor').as '
|
113
|
+
rule('term' => 'factor additive_plus').as 'binary_operator'
|
114
|
+
rule('additive_plus' => 'additive_plus additionOp factor').as 'binary_plus_more'
|
115
|
+
rule('additive_plus' => 'additionOp factor').as 'binary_plus_end'
|
116
116
|
rule('additionOp' => 'MINUS')
|
117
117
|
rule('additionOp' => 'PLUS')
|
118
118
|
rule('factor' => 'unary')
|
119
|
-
rule('factor' => 'unary multiplicative_plus').as '
|
120
|
-
rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as '
|
121
|
-
rule('multiplicative_plus' => 'multOp unary').as '
|
119
|
+
rule('factor' => 'unary multiplicative_plus').as 'binary_operator'
|
120
|
+
rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'binary_plus_more'
|
121
|
+
rule('multiplicative_plus' => 'multOp unary').as 'binary_plus_end'
|
122
122
|
rule('multOp' => 'SLASH')
|
123
123
|
rule('multOp' => 'STAR')
|
124
124
|
rule('unary' => 'unaryOp unary').as 'unary_expr'
|
@@ -126,10 +126,10 @@ module Loxxy
|
|
126
126
|
rule('unaryOp' => 'BANG')
|
127
127
|
rule('unaryOp' => 'MINUS')
|
128
128
|
rule('call' => 'primary')
|
129
|
-
rule('call' => 'primary refinement_plus')
|
130
|
-
rule('refinement_plus' => 'refinement_plus refinement')
|
131
|
-
rule('refinement_plus' => 'refinement')
|
132
|
-
rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
|
129
|
+
rule('call' => 'primary refinement_plus').as 'call_expr'
|
130
|
+
rule('refinement_plus' => 'refinement_plus refinement') # .as 'refinement_plus_more'
|
131
|
+
rule('refinement_plus' => 'refinement').as 'refinement_plus_end'
|
132
|
+
rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN').as 'call_arglist'
|
133
133
|
rule('refinement' => 'DOT IDENTIFIER')
|
134
134
|
rule('primary' => 'TRUE').as 'literal_expr'
|
135
135
|
rule('primary' => 'FALSE').as 'literal_expr'
|
@@ -142,15 +142,15 @@ module Loxxy
|
|
142
142
|
rule('primary' => 'SUPER DOT IDENTIFIER')
|
143
143
|
|
144
144
|
# Utility rules
|
145
|
-
rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block')
|
145
|
+
rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block').as 'function'
|
146
146
|
rule('params_opt' => 'parameters')
|
147
147
|
rule('params_opt' => [])
|
148
|
-
rule('parameters' => 'parameters COMMA IDENTIFIER')
|
149
|
-
rule('parameters' => 'IDENTIFIER')
|
148
|
+
rule('parameters' => 'parameters COMMA IDENTIFIER').as 'parameters_plus_more'
|
149
|
+
rule('parameters' => 'IDENTIFIER').as 'parameters_plus_end'
|
150
150
|
rule('arguments_opt' => 'arguments')
|
151
151
|
rule('arguments_opt' => [])
|
152
|
-
rule('arguments' => 'arguments COMMA expression')
|
153
|
-
rule('arguments' => 'expression')
|
152
|
+
rule('arguments' => 'arguments COMMA expression').as 'arguments_plus_more'
|
153
|
+
rule('arguments' => 'expression').as 'arguments_plus_end'
|
154
154
|
end
|
155
155
|
|
156
156
|
unless defined?(Grammar)
|
@@ -11,7 +11,7 @@ module Loxxy
|
|
11
11
|
|
12
12
|
# Constructor.
|
13
13
|
# @param aValue [Datatype::BuiltinDatatype] the Lox data value
|
14
|
-
# @param
|
14
|
+
# @param aLexeme [String] the lexeme (= piece of text from input)
|
15
15
|
# @param aTerminal [Rley::Syntax::Terminal, String]
|
16
16
|
# The terminal symbol corresponding to the lexeme.
|
17
17
|
# @param aPosition [Rley::Lexical::Position] The position of lexeme
|
data/lib/loxxy/version.rb
CHANGED
@@ -60,7 +60,7 @@ module Loxxy
|
|
60
60
|
new_env = BackEnd::Environment.new
|
61
61
|
expect { subject.enter_environment(new_env) }.not_to raise_error
|
62
62
|
expect(subject.current_env).to eq(new_env)
|
63
|
-
expect(subject.current_env.
|
63
|
+
expect(subject.current_env.enclosing).to eq(subject.root)
|
64
64
|
expect(subject.name2envs['q']).to eq([subject.root])
|
65
65
|
end
|
66
66
|
|
@@ -26,6 +26,7 @@ module Loxxy
|
|
26
26
|
expect(subject.to_str).to eq(sample_text)
|
27
27
|
end
|
28
28
|
|
29
|
+
# rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
29
30
|
it 'compares with another Lox string' do
|
30
31
|
result = subject == LXString.new(sample_text.dup)
|
31
32
|
expect(result).to be_true
|
@@ -40,6 +41,7 @@ module Loxxy
|
|
40
41
|
result = LXString.new('') == LXString.new('')
|
41
42
|
expect(result).to be_true
|
42
43
|
end
|
44
|
+
# rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
|
43
45
|
|
44
46
|
it 'compares with a Ruby string' do
|
45
47
|
result = subject == sample_text.dup
|
@@ -21,6 +21,7 @@ module Loxxy
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# rubocop: disable Lint/FloatComparison
|
24
25
|
context 'Provided services:' do
|
25
26
|
it 'should compare with other Lox numbers' do
|
26
27
|
result = subject == Number.new(sample_value)
|
@@ -51,7 +52,8 @@ module Loxxy
|
|
51
52
|
subtraction = subject - Number.new(10)
|
52
53
|
expect(subtraction == -22.34).to be_true
|
53
54
|
end
|
54
|
-
end
|
55
|
+
end # context
|
56
|
+
# rubocop: enable Lint/FloatComparison
|
55
57
|
end # describe
|
56
58
|
end # module
|
57
59
|
end # module
|
@@ -208,6 +208,7 @@ LOX_END
|
|
208
208
|
expect(eof_token.terminal).to eq('EOF')
|
209
209
|
end
|
210
210
|
|
211
|
+
# rubocop: disable Lint/PercentStringArray
|
211
212
|
it 'should skip end of line comments' do
|
212
213
|
input = <<-LOX_END
|
213
214
|
// first comment
|
@@ -223,6 +224,7 @@ LOX_END
|
|
223
224
|
]
|
224
225
|
match_expectations(subject, expectations)
|
225
226
|
end
|
227
|
+
# rubocop: enable Lint/PercentStringArray
|
226
228
|
|
227
229
|
it 'should cope with single slash (divide) expression' do
|
228
230
|
subject.start_with('8 / 2')
|
data/spec/interpreter_spec.rb
CHANGED
@@ -288,10 +288,10 @@ LOX_END
|
|
288
288
|
program = <<-LOX_END
|
289
289
|
var a = "before";
|
290
290
|
print a; // output: before
|
291
|
-
|
291
|
+
|
292
292
|
a = "after";
|
293
293
|
print a; // output: after
|
294
|
-
|
294
|
+
|
295
295
|
print a = "arg"; // output: arg
|
296
296
|
print a; // output: arg
|
297
297
|
LOX_END
|
@@ -299,6 +299,130 @@ LOX_END
|
|
299
299
|
expect(sample_cfg[:ostream].string).to eq('beforeafterargarg')
|
300
300
|
end
|
301
301
|
|
302
|
+
it 'should support variables local to a block' do
|
303
|
+
program = <<-LOX_END
|
304
|
+
{
|
305
|
+
var a = "first";
|
306
|
+
print a;
|
307
|
+
}
|
308
|
+
{
|
309
|
+
var a = "second";
|
310
|
+
print a;
|
311
|
+
}
|
312
|
+
LOX_END
|
313
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
314
|
+
expect(sample_cfg[:ostream].string).to eq('firstsecond')
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should support the shadowing of variables in a block' do
|
318
|
+
program = <<-LOX_END
|
319
|
+
var a = "outer";
|
320
|
+
|
321
|
+
{
|
322
|
+
var a = "inner";
|
323
|
+
print a; // output: inner
|
324
|
+
}
|
325
|
+
|
326
|
+
print a; // output: outer
|
327
|
+
LOX_END
|
328
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
329
|
+
expect(sample_cfg[:ostream].string).to eq('innerouter')
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'should implement single statement while loops' do
|
333
|
+
program = <<-LOX_END
|
334
|
+
// Single-expression body.
|
335
|
+
var c = 0;
|
336
|
+
while (c < 3) print c = c + 1;
|
337
|
+
// output: 1
|
338
|
+
// output: 2
|
339
|
+
// output: 3
|
340
|
+
LOX_END
|
341
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
342
|
+
expect(sample_cfg[:ostream].string).to eq('123')
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'should implement block body while loops' do
|
346
|
+
program = <<-LOX_END
|
347
|
+
// Block body.
|
348
|
+
var a = 0;
|
349
|
+
while (a < 3) {
|
350
|
+
print a;
|
351
|
+
a = a + 1;
|
352
|
+
}
|
353
|
+
// output: 0
|
354
|
+
// output: 1
|
355
|
+
// output: 2
|
356
|
+
LOX_END
|
357
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
358
|
+
expect(sample_cfg[:ostream].string).to eq('012')
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'should implement single statement for loops' do
|
362
|
+
program = <<-LOX_END
|
363
|
+
// Single-expression body.
|
364
|
+
for (var c = 0; c < 3;) print c = c + 1;
|
365
|
+
// output: 1
|
366
|
+
// output: 2
|
367
|
+
// output: 3
|
368
|
+
LOX_END
|
369
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
370
|
+
expect(sample_cfg[:ostream].string).to eq('123')
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'should implement for loops with block body' do
|
374
|
+
program = <<-LOX_END
|
375
|
+
// Block body.
|
376
|
+
for (var a = 0; a < 3; a = a + 1) {
|
377
|
+
print a;
|
378
|
+
}
|
379
|
+
// output: 0
|
380
|
+
// output: 1
|
381
|
+
// output: 2
|
382
|
+
LOX_END
|
383
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
384
|
+
expect(sample_cfg[:ostream].string).to eq('012')
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'should implement nullary function calls' do
|
388
|
+
program = <<-LOX_END
|
389
|
+
print clock(); // Lox expects the 'clock' predefined native function
|
390
|
+
LOX_END
|
391
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
392
|
+
tick = sample_cfg[:ostream].string
|
393
|
+
expect(Time.now.to_f - tick.to_f).to be < 0.1
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'should implement function definition' do
|
397
|
+
program = <<-LOX_END
|
398
|
+
fun printSum(a, b) {
|
399
|
+
print a + b;
|
400
|
+
}
|
401
|
+
printSum(1, 2);
|
402
|
+
LOX_END
|
403
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
404
|
+
expect(sample_cfg[:ostream].string).to eq('3')
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'should support functions with empty body' do
|
408
|
+
program = <<-LOX_END
|
409
|
+
fun f() {}
|
410
|
+
print f();
|
411
|
+
LOX_END
|
412
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
413
|
+
expect(sample_cfg[:ostream].string).to eq('nil')
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'should provide print representation of functions' do
|
417
|
+
program = <<-LOX_END
|
418
|
+
fun foo() {}
|
419
|
+
print foo; // output: <fn foo>
|
420
|
+
print clock; // output: <native fn>
|
421
|
+
LOX_END
|
422
|
+
expect { subject.evaluate(program) }.not_to raise_error
|
423
|
+
expect(sample_cfg[:ostream].string).to eq('<fn foo><native fn>')
|
424
|
+
end
|
425
|
+
|
302
426
|
it 'should print the hello world message' do
|
303
427
|
expect { subject.evaluate(hello_world) }.not_to raise_error
|
304
428
|
expect(sample_cfg[:ostream].string).to eq('Hello, world!')
|