loxxy 0.4.09 → 0.4.10
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/.rubocop.yml +32 -304
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +1 -1
- data/lib/loxxy/ast/ast_builder.rb +1 -1
- data/lib/loxxy/back_end/engine.rb +2 -0
- data/lib/loxxy/back_end/environment.rb +1 -1
- data/lib/loxxy/front_end/scanner.rb +9 -9
- data/lib/loxxy/interpreter.rb +3 -3
- data/lib/loxxy/version.rb +1 -1
- data/loxxy.gemspec +8 -7
- data/spec/back_end/engine_spec.rb +21 -19
- data/spec/back_end/environment_spec.rb +20 -19
- data/spec/back_end/symbol_table_spec.rb +67 -67
- data/spec/back_end/variable_spec.rb +14 -13
- data/spec/datatype/boolean_spec.rb +9 -8
- data/spec/datatype/lx_string_spec.rb +16 -15
- data/spec/datatype/nil_spec.rb +5 -5
- data/spec/datatype/number_spec.rb +18 -17
- data/spec/front_end/parser_spec.rb +110 -110
- data/spec/front_end/raw_parser_spec.rb +25 -25
- data/spec/front_end/scanner_spec.rb +77 -76
- data/spec/interpreter_spec.rb +114 -118
- data/spec/loxxy_spec.rb +1 -1
- metadata +33 -27
@@ -10,8 +10,9 @@ module Loxxy
|
|
10
10
|
describe Environment do
|
11
11
|
let(:foo) { Datatype::LXString.new('foo') }
|
12
12
|
let(:bar) { Datatype::LXString.new('bar') }
|
13
|
-
let(:mother) {
|
14
|
-
|
13
|
+
let(:mother) { described_class.new }
|
14
|
+
|
15
|
+
subject(:an_environ) { described_class.new(mother) }
|
15
16
|
|
16
17
|
# Shortand factory method.
|
17
18
|
def var(aName, aValue)
|
@@ -20,38 +21,38 @@ module Loxxy
|
|
20
21
|
|
21
22
|
context 'Initialization:' do
|
22
23
|
it 'could be initialized without argument' do
|
23
|
-
expect {
|
24
|
+
expect { described_class.new }.not_to raise_error
|
24
25
|
end
|
25
26
|
|
26
27
|
it 'could be initialized with a parent environment' do
|
27
|
-
expect {
|
28
|
+
expect { described_class.new(mother) }.not_to raise_error
|
28
29
|
end
|
29
30
|
|
30
|
-
it "
|
31
|
-
expect(
|
31
|
+
it "doesn't have definitions by default" do
|
32
|
+
expect(an_environ.defns).to be_empty
|
32
33
|
end
|
33
34
|
|
34
|
-
it '
|
35
|
-
expect(
|
35
|
+
it 'knows its parent (if any)' do
|
36
|
+
expect(an_environ.enclosing).to eq(mother)
|
36
37
|
end
|
37
38
|
end # context
|
38
39
|
|
39
40
|
context 'Provided services:' do
|
40
|
-
it '
|
41
|
-
|
42
|
-
expect(
|
43
|
-
var_a =
|
44
|
-
expect(var_a).to
|
41
|
+
it 'accepts the addition of a variable' do
|
42
|
+
an_environ.insert(var('a', foo))
|
43
|
+
expect(an_environ.defns).not_to be_empty
|
44
|
+
var_a = an_environ.defns['a']
|
45
|
+
expect(var_a).to be_a(Variable)
|
45
46
|
expect(var_a.name).to eq('a')
|
46
47
|
end
|
47
48
|
|
48
|
-
it '
|
49
|
-
|
50
|
-
expect(
|
49
|
+
it 'accepts the addition of multiple variables' do
|
50
|
+
an_environ.insert(var('a', foo))
|
51
|
+
expect(an_environ.defns).not_to be_empty
|
51
52
|
|
52
|
-
|
53
|
-
var_b =
|
54
|
-
expect(var_b).to
|
53
|
+
an_environ.insert(var('b', bar))
|
54
|
+
var_b = an_environ.defns['b']
|
55
|
+
expect(var_b).to be_a(Variable)
|
55
56
|
expect(var_b.name).to eq('b')
|
56
57
|
end
|
57
58
|
end # context
|
@@ -9,25 +9,25 @@ require_relative '../../lib/loxxy/back_end/symbol_table'
|
|
9
9
|
module Loxxy
|
10
10
|
module BackEnd
|
11
11
|
describe SymbolTable do
|
12
|
-
subject {
|
12
|
+
subject(:symb_table) { described_class.new }
|
13
13
|
|
14
14
|
context 'Initialization:' do
|
15
|
-
it '
|
16
|
-
expect {
|
15
|
+
it 'is initialized without argument' do
|
16
|
+
expect { described_class.new }.not_to raise_error
|
17
17
|
end
|
18
18
|
|
19
|
-
it '
|
20
|
-
expect(
|
21
|
-
expect(
|
22
|
-
expect(
|
19
|
+
it 'has a root BackEnd' do
|
20
|
+
expect(symb_table.root).not_to be_nil
|
21
|
+
expect(symb_table.current_env).to eq(symb_table.root)
|
22
|
+
expect(symb_table.root).to be_a(BackEnd::Environment)
|
23
23
|
end
|
24
24
|
|
25
|
-
it "
|
26
|
-
expect(
|
25
|
+
it "doesn't have names at initialization" do
|
26
|
+
expect(symb_table.name2envs).to be_empty
|
27
27
|
end
|
28
28
|
|
29
|
-
it '
|
30
|
-
expect(
|
29
|
+
it 'is empty at initialization' do
|
30
|
+
expect(symb_table).to be_empty
|
31
31
|
end
|
32
32
|
end # context
|
33
33
|
|
@@ -36,85 +36,85 @@ module Loxxy
|
|
36
36
|
Variable.new(aName)
|
37
37
|
end
|
38
38
|
|
39
|
-
it '
|
40
|
-
expect {
|
41
|
-
expect(
|
42
|
-
expect(
|
43
|
-
expect(
|
44
|
-
expect(
|
45
|
-
expect(
|
39
|
+
it 'allows the addition of a variable' do
|
40
|
+
expect { symb_table.insert(var('q')) }.not_to raise_error
|
41
|
+
expect(symb_table).not_to be_empty
|
42
|
+
expect(symb_table.name2envs['q']).to be_a(Array)
|
43
|
+
expect(symb_table.name2envs['q'].size).to eq(1)
|
44
|
+
expect(symb_table.name2envs['q'].first).to eq(symb_table.current_env)
|
45
|
+
expect(symb_table.current_env.defns['q']).to be_a(BackEnd::Variable)
|
46
46
|
end
|
47
47
|
|
48
|
-
it '
|
49
|
-
|
48
|
+
it 'allows the addition of several labels for same env' do
|
49
|
+
symb_table.insert(var('q'))
|
50
50
|
# expect(i_name).to match(/^q_[0-9a-z]*$/)
|
51
51
|
|
52
|
-
expect {
|
53
|
-
expect(
|
54
|
-
expect(
|
55
|
-
expect(
|
52
|
+
expect { symb_table.insert(var('x')) }.not_to raise_error
|
53
|
+
expect(symb_table.name2envs['x']).to be_a(Array)
|
54
|
+
expect(symb_table.name2envs['x'].first).to eq(symb_table.current_env)
|
55
|
+
expect(symb_table.current_env.defns['x']).to be_a(BackEnd::Variable)
|
56
56
|
end
|
57
57
|
|
58
|
-
it '
|
59
|
-
|
58
|
+
it 'allows the entry into a new scope' do
|
59
|
+
symb_table.insert(var('q'))
|
60
60
|
new_env = BackEnd::Environment.new
|
61
|
-
expect {
|
62
|
-
expect(
|
63
|
-
expect(
|
64
|
-
expect(
|
61
|
+
expect { symb_table.enter_environment(new_env) }.not_to raise_error
|
62
|
+
expect(symb_table.current_env).to eq(new_env)
|
63
|
+
expect(symb_table.current_env.enclosing).to eq(symb_table.root)
|
64
|
+
expect(symb_table.name2envs['q']).to eq([symb_table.root])
|
65
65
|
end
|
66
66
|
|
67
|
-
it '
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
expect(
|
72
|
-
expect(
|
73
|
-
expect(
|
74
|
-
expect(
|
75
|
-
expect(
|
67
|
+
it 'allows the addition of same name in different scopes' do
|
68
|
+
symb_table.insert(var('q'))
|
69
|
+
symb_table.enter_environment(BackEnd::Environment.new)
|
70
|
+
symb_table.insert(var('q'))
|
71
|
+
expect(symb_table.name2envs['q']).to be_a(Array)
|
72
|
+
expect(symb_table.name2envs['q'].size).to eq(2)
|
73
|
+
expect(symb_table.name2envs['q'].first).to eq(symb_table.root)
|
74
|
+
expect(symb_table.name2envs['q'].last).to eq(symb_table.current_env)
|
75
|
+
expect(symb_table.current_env.defns['q']).to be_a(BackEnd::Variable)
|
76
76
|
end
|
77
77
|
|
78
|
-
it '
|
79
|
-
|
78
|
+
it 'allows the removal of a scope' do
|
79
|
+
symb_table.insert(var('q'))
|
80
80
|
new_env = BackEnd::Environment.new
|
81
|
-
|
82
|
-
|
83
|
-
expect(
|
84
|
-
|
85
|
-
expect {
|
86
|
-
expect(
|
87
|
-
expect(
|
88
|
-
expect(
|
81
|
+
symb_table.enter_environment(new_env)
|
82
|
+
symb_table.insert(var('q'))
|
83
|
+
expect(symb_table.name2envs['q'].size).to eq(2)
|
84
|
+
|
85
|
+
expect { symb_table.leave_environment }.not_to raise_error
|
86
|
+
expect(symb_table.current_env).to eq(symb_table.root)
|
87
|
+
expect(symb_table.name2envs['q'].size).to eq(1)
|
88
|
+
expect(symb_table.name2envs['q']).to eq([symb_table.root])
|
89
89
|
end
|
90
90
|
|
91
|
-
it '
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
91
|
+
it 'allows the search of an entry based on its name' do
|
92
|
+
symb_table.insert(var('q'))
|
93
|
+
symb_table.insert(var('x'))
|
94
|
+
symb_table.enter_environment(BackEnd::Environment.new)
|
95
|
+
symb_table.insert(var('q'))
|
96
|
+
symb_table.insert(var('y'))
|
97
97
|
|
98
98
|
# Search for unknown name
|
99
|
-
expect(
|
99
|
+
expect(symb_table.lookup('z')).to be_nil
|
100
100
|
|
101
101
|
# Search for existing unique names
|
102
|
-
expect(
|
103
|
-
expect(
|
102
|
+
expect(symb_table.lookup('y')).to eq(symb_table.current_env.defns['y'])
|
103
|
+
expect(symb_table.lookup('x')).to eq(symb_table.root.defns['x'])
|
104
104
|
|
105
105
|
# Search for redefined name
|
106
|
-
expect(
|
106
|
+
expect(symb_table.lookup('q')).to eq(symb_table.current_env.defns['q'])
|
107
107
|
end
|
108
108
|
|
109
|
-
it '
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
109
|
+
it 'lists all the variables defined in all the szcope chain' do
|
110
|
+
symb_table.insert(var('q'))
|
111
|
+
symb_table.enter_environment(BackEnd::Environment.new)
|
112
|
+
symb_table.insert(var('x'))
|
113
|
+
symb_table.enter_environment(BackEnd::Environment.new)
|
114
|
+
symb_table.insert(var('y'))
|
115
|
+
symb_table.insert(var('x'))
|
116
116
|
|
117
|
-
vars =
|
117
|
+
vars = symb_table.all_variables
|
118
118
|
expect(vars.map(&:name)).to eq(%w[q x y x])
|
119
119
|
end
|
120
120
|
end # context
|
@@ -11,31 +11,32 @@ module Loxxy
|
|
11
11
|
describe Variable do
|
12
12
|
let(:sample_name) { 'iAmAVariable' }
|
13
13
|
let(:sample_value) { 'here is my value' }
|
14
|
-
|
14
|
+
|
15
|
+
subject(:a_var) { described_class.new(sample_name, sample_value) }
|
15
16
|
|
16
17
|
context 'Initialization:' do
|
17
|
-
it '
|
18
|
-
expect {
|
18
|
+
it 'is initialized with a name and a value, or...' do
|
19
|
+
expect { described_class.new(sample_name, sample_value) }.not_to raise_error
|
19
20
|
end
|
20
21
|
|
21
|
-
it '
|
22
|
-
expect {
|
22
|
+
it 'is initialized with just a name' do
|
23
|
+
expect { described_class.new(sample_name) }.not_to raise_error
|
23
24
|
end
|
24
25
|
|
25
|
-
it '
|
26
|
-
expect(
|
26
|
+
it 'knows its name' do
|
27
|
+
expect(a_var.name).to eq(sample_name)
|
27
28
|
end
|
28
29
|
|
29
|
-
it '
|
30
|
-
expect(
|
30
|
+
it 'has a frozen name' do
|
31
|
+
expect(a_var.name).to be_frozen
|
31
32
|
end
|
32
33
|
|
33
|
-
it '
|
34
|
-
expect(
|
34
|
+
it 'knows its value (if provided)' do
|
35
|
+
expect(a_var.value).to eq(sample_value)
|
35
36
|
end
|
36
37
|
|
37
|
-
it '
|
38
|
-
instance =
|
38
|
+
it 'has a nil value otherwise' do
|
39
|
+
instance = described_class.new(sample_name)
|
39
40
|
expect(instance.value).to eq(Datatype::Nil.instance)
|
40
41
|
end
|
41
42
|
end # context
|
@@ -8,22 +8,23 @@ require_relative '../../lib/loxxy/datatype/boolean'
|
|
8
8
|
module Loxxy
|
9
9
|
module Datatype
|
10
10
|
describe Boolean do
|
11
|
-
let(:
|
12
|
-
|
11
|
+
let(:truth_value) { false }
|
12
|
+
|
13
|
+
subject(:a_boolean) { described_class.new(truth_value) }
|
13
14
|
|
14
15
|
context 'Initialization:' do
|
15
|
-
it '
|
16
|
-
expect {
|
16
|
+
it 'accepts a boolean value at initialization' do
|
17
|
+
expect { described_class.new(truth_value) }.not_to raise_error
|
17
18
|
end
|
18
19
|
|
19
|
-
it '
|
20
|
-
expect(
|
20
|
+
it 'knows its value' do
|
21
|
+
expect(a_boolean.value).to eq(truth_value)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
context 'Provided services:' do
|
25
|
-
it '
|
26
|
-
expect(
|
26
|
+
it 'gives its display representation' do
|
27
|
+
expect(a_boolean.to_str).to eq('false')
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end # describe
|
@@ -9,53 +9,54 @@ module Loxxy
|
|
9
9
|
module Datatype
|
10
10
|
describe LXString do
|
11
11
|
let(:sample_text) { 'some_text' }
|
12
|
-
|
12
|
+
|
13
|
+
subject(:a_string) { described_class.new(sample_text) }
|
13
14
|
|
14
15
|
context 'Initialization:' do
|
15
|
-
it '
|
16
|
-
expect {
|
16
|
+
it 'accepts a String value at initialization' do
|
17
|
+
expect { described_class.new(sample_text) }.not_to raise_error
|
17
18
|
end
|
18
19
|
|
19
|
-
it '
|
20
|
-
expect(
|
20
|
+
it 'knows its value' do
|
21
|
+
expect(a_string.value).to eq(sample_text)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
context 'Provided services:' do
|
25
|
-
it '
|
26
|
-
expect(
|
26
|
+
it 'gives its display representation' do
|
27
|
+
expect(a_string.to_str).to eq(sample_text)
|
27
28
|
end
|
28
29
|
|
29
30
|
# rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
30
31
|
it 'compares with another Lox string' do
|
31
|
-
result =
|
32
|
+
result = a_string == described_class.new(sample_text.dup)
|
32
33
|
expect(result).to be_true
|
33
34
|
|
34
|
-
result =
|
35
|
+
result = a_string == described_class.new('other-text')
|
35
36
|
expect(result).to be_false
|
36
37
|
|
37
|
-
result =
|
38
|
+
result = a_string == described_class.new('')
|
38
39
|
expect(result).to be_false
|
39
40
|
|
40
41
|
# Two empty strings are equal
|
41
|
-
result =
|
42
|
+
result = described_class.new('') == described_class.new('')
|
42
43
|
expect(result).to be_true
|
43
44
|
end
|
44
45
|
# rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
|
45
46
|
|
46
47
|
it 'compares with a Ruby string' do
|
47
|
-
result =
|
48
|
+
result = a_string == sample_text.dup
|
48
49
|
expect(result).to be_true
|
49
50
|
|
50
|
-
result =
|
51
|
+
result = a_string == 'other-text'
|
51
52
|
expect(result).to be_false
|
52
53
|
|
53
|
-
result =
|
54
|
+
result = a_string == ''
|
54
55
|
expect(result).to be_false
|
55
56
|
end
|
56
57
|
|
57
58
|
it 'performs the concatenation with another string' do
|
58
|
-
concatenation =
|
59
|
+
concatenation = described_class.new('str') + described_class.new('ing')
|
59
60
|
expect(concatenation == 'string').to be_true
|
60
61
|
end
|
61
62
|
end
|
data/spec/datatype/nil_spec.rb
CHANGED
@@ -8,17 +8,17 @@ require_relative '../../lib/loxxy/datatype/boolean'
|
|
8
8
|
module Loxxy
|
9
9
|
module Datatype
|
10
10
|
describe Nil do
|
11
|
-
subject {
|
11
|
+
subject(:nil_literal) { described_class.instance }
|
12
12
|
|
13
13
|
context 'Initialization:' do
|
14
|
-
it '
|
15
|
-
expect(
|
14
|
+
it 'knows its value' do
|
15
|
+
expect(nil_literal.value).to be_nil
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
context 'Provided services:' do
|
20
|
-
it '
|
21
|
-
expect(
|
20
|
+
it 'gives its display representation' do
|
21
|
+
expect(nil_literal.to_str).to eq('nil')
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end # describe
|
@@ -9,47 +9,48 @@ module Loxxy
|
|
9
9
|
module Datatype
|
10
10
|
describe Number do
|
11
11
|
let(:sample_value) { -12.34 }
|
12
|
-
|
12
|
+
|
13
|
+
subject(:num_val) { described_class.new(sample_value) }
|
13
14
|
|
14
15
|
context 'Initialization:' do
|
15
|
-
it '
|
16
|
-
expect {
|
16
|
+
it 'accepts a Numeric value at initialization' do
|
17
|
+
expect { described_class.new(sample_value) }.not_to raise_error
|
17
18
|
end
|
18
19
|
|
19
|
-
it '
|
20
|
-
expect(
|
20
|
+
it 'knows its value' do
|
21
|
+
expect(num_val.value).to eq(sample_value)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
# rubocop: disable Lint/FloatComparison
|
25
26
|
context 'Provided services:' do
|
26
|
-
it '
|
27
|
-
result =
|
27
|
+
it 'compares with other Lox numbers' do
|
28
|
+
result = num_val == described_class.new(sample_value)
|
28
29
|
expect(result).to be_true
|
29
30
|
|
30
|
-
result =
|
31
|
+
result = num_val == described_class.new(5)
|
31
32
|
expect(result).to be_false
|
32
33
|
end
|
33
34
|
|
34
|
-
it '
|
35
|
-
result =
|
35
|
+
it 'compares with Ruby numbers' do
|
36
|
+
result = num_val == sample_value
|
36
37
|
expect(result).to be_true
|
37
38
|
|
38
|
-
result =
|
39
|
+
result = num_val == 5
|
39
40
|
expect(result).to be_false
|
40
41
|
end
|
41
42
|
|
42
|
-
it '
|
43
|
-
expect(
|
43
|
+
it 'gives its display representation' do
|
44
|
+
expect(num_val.to_str).to eq(sample_value.to_s)
|
44
45
|
end
|
45
46
|
|
46
|
-
it '
|
47
|
-
addition =
|
47
|
+
it 'computes the addition with another number' do
|
48
|
+
addition = num_val + described_class.new(10)
|
48
49
|
expect(addition == -2.34).to be_true
|
49
50
|
end
|
50
51
|
|
51
|
-
it '
|
52
|
-
subtraction =
|
52
|
+
it 'computes the subtraction with another number' do
|
53
|
+
subtraction = num_val - described_class.new(10)
|
53
54
|
expect(subtraction == -22.34).to be_true
|
54
55
|
end
|
55
56
|
end # context
|