loxxy 0.0.17 → 0.0.22
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 +61 -2
- data/README.md +164 -50
- data/lib/loxxy/ast/all_lox_nodes.rb +7 -1
- data/lib/loxxy/ast/ast_builder.rb +63 -3
- data/lib/loxxy/ast/ast_visitor.rb +68 -1
- data/lib/loxxy/ast/lox_grouping_expr.rb +23 -0
- data/lib/loxxy/ast/lox_if_stmt.rb +35 -0
- data/lib/loxxy/ast/lox_logical_expr.rb +28 -0
- data/lib/loxxy/ast/lox_seq_decl.rb +23 -0
- data/lib/loxxy/ast/lox_var_stmt.rb +28 -0
- data/lib/loxxy/ast/lox_variable_expr.rb +26 -0
- data/lib/loxxy/back_end/engine.rb +70 -1
- data/lib/loxxy/back_end/entry.rb +41 -0
- data/lib/loxxy/back_end/environment.rb +66 -0
- data/lib/loxxy/back_end/symbol_table.rb +135 -0
- data/lib/loxxy/back_end/variable.rb +25 -0
- data/lib/loxxy/datatype/builtin_datatype.rb +32 -5
- data/lib/loxxy/front_end/grammar.rb +9 -9
- data/lib/loxxy/interpreter.rb +1 -1
- data/lib/loxxy/version.rb +1 -1
- data/spec/back_end/engine_spec.rb +9 -0
- data/spec/back_end/environment_spec.rb +74 -0
- data/spec/back_end/symbol_table_spec.rb +142 -0
- data/spec/back_end/variable_spec.rb +79 -0
- data/spec/front_end/parser_spec.rb +25 -23
- data/spec/interpreter_spec.rb +110 -1
- metadata +18 -2
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'environment'
|
4
|
+
|
5
|
+
module Loxxy
|
6
|
+
module BackEnd
|
7
|
+
# A symbol table is basically a mapping from a name onto an object
|
8
|
+
# that holds information associated with that name. It is a data structure
|
9
|
+
# that keeps track of variables and their respective environment where they are
|
10
|
+
# declared. The key requirements for the symbol are:
|
11
|
+
# - To perform fast lookup operations: given a name, retrieve the corresponding
|
12
|
+
# object.
|
13
|
+
# - To allow the efficient insertion of names and related information
|
14
|
+
# - To support the nesting of environments
|
15
|
+
# - To handle the entry environment and exit environment events,
|
16
|
+
# - To cope with variable redefinition in nested environment
|
17
|
+
# The terminology 'symbol table' comes from the compiler design
|
18
|
+
# community.
|
19
|
+
class SymbolTable
|
20
|
+
# Mapping between a name and the environment(s) where it is defined
|
21
|
+
# @return [Hash{String => Array<Environment>}]
|
22
|
+
attr_reader :name2envs
|
23
|
+
|
24
|
+
# @return [Environment] The top-level environment (= root of environment tree)
|
25
|
+
attr_reader :root
|
26
|
+
|
27
|
+
# @return [Environment] The current environment.
|
28
|
+
attr_reader :current_env
|
29
|
+
|
30
|
+
# Build symbol table with given environment as root.
|
31
|
+
# @param anEnv [BackEnd::Environment,NilClass] The top-level Environment
|
32
|
+
def initialize(anEnv = nil)
|
33
|
+
@name2envs = {}
|
34
|
+
init_root(anEnv) # Set default (global) environment
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns iff there is no entry in the symbol table
|
38
|
+
# @return [Boolean]
|
39
|
+
def empty?
|
40
|
+
name2envs.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Use this method to signal the interpreter that a given environment
|
44
|
+
# to be a child of current environment and to be itself the new current environment.
|
45
|
+
# @param anEnv [BackEnd::Environment] the Environment that
|
46
|
+
def enter_environment(anEnv)
|
47
|
+
anEnv.parent = current_env
|
48
|
+
@current_env = anEnv
|
49
|
+
end
|
50
|
+
|
51
|
+
def leave_environment
|
52
|
+
current_env.defns.each_pair do |nm, _item|
|
53
|
+
environments = name2envs[nm]
|
54
|
+
if environments.size == 1
|
55
|
+
name2envs.delete(nm)
|
56
|
+
else
|
57
|
+
environments.pop
|
58
|
+
name2envs[nm] = environments
|
59
|
+
end
|
60
|
+
end
|
61
|
+
raise StandardError, 'Cannot remove root environment.' if current_env == root
|
62
|
+
|
63
|
+
@current_env = current_env.parent
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add an entry with given name to current environment.
|
67
|
+
# @param anEntry [Variable]
|
68
|
+
# @return [String] Internal name of the entry
|
69
|
+
def insert(anEntry)
|
70
|
+
current_env.insert(anEntry)
|
71
|
+
name = anEntry.name
|
72
|
+
if name2envs.include?(name)
|
73
|
+
name2envs[name] << current_env
|
74
|
+
else
|
75
|
+
name2envs[name] = [current_env]
|
76
|
+
end
|
77
|
+
|
78
|
+
anEntry.name # anEntry.i_name
|
79
|
+
end
|
80
|
+
|
81
|
+
# Search for the object with the given name
|
82
|
+
# @param aName [String]
|
83
|
+
# @return [BackEnd::Variable]
|
84
|
+
def lookup(aName)
|
85
|
+
environments = name2envs.fetch(aName, nil)
|
86
|
+
return nil if environments.nil?
|
87
|
+
|
88
|
+
sc = environments.last
|
89
|
+
sc.defns[aName]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Search for the object with the given i_name
|
93
|
+
# @param anIName [String]
|
94
|
+
# @return [BackEnd::Variable]
|
95
|
+
# def lookup_i_name(anIName)
|
96
|
+
# found = nil
|
97
|
+
# environment = current_env
|
98
|
+
|
99
|
+
# begin
|
100
|
+
# found = environment.defns.values.find { |e| e.i_name == anIName }
|
101
|
+
# break if found
|
102
|
+
|
103
|
+
# environment = environment.parent
|
104
|
+
# end while environment
|
105
|
+
|
106
|
+
# found
|
107
|
+
# end
|
108
|
+
|
109
|
+
# Return all variables defined in the current .. root chain.
|
110
|
+
# Variables are sorted top-down and left-to-right.
|
111
|
+
def all_variables
|
112
|
+
vars = []
|
113
|
+
skope = current_env
|
114
|
+
while skope
|
115
|
+
vars_of_environment = skope.defns.select { |_, item| item.kind_of?(Variable) }
|
116
|
+
vars = vars_of_environment.values.concat(vars)
|
117
|
+
skope = skope.parent
|
118
|
+
end
|
119
|
+
|
120
|
+
vars
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def init_root(anEnv)
|
126
|
+
@root = valid_environment(anEnv)
|
127
|
+
@current_env = @root
|
128
|
+
end
|
129
|
+
|
130
|
+
def valid_environment(anEnv)
|
131
|
+
anEnv.nil? ? Environment.new : anEnv
|
132
|
+
end
|
133
|
+
end # class
|
134
|
+
end # module
|
135
|
+
end # module
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'entry'
|
4
|
+
require_relative '../datatype/all_datatypes'
|
5
|
+
|
6
|
+
module Loxxy
|
7
|
+
module BackEnd
|
8
|
+
# Representation of a Lox variable.
|
9
|
+
# It is a named slot that can be associated with a value at the time.
|
10
|
+
class Variable
|
11
|
+
include Entry # Add expected behaviour for symbol table entries
|
12
|
+
|
13
|
+
# @return [Datatype::BuiltinDatatype] the value assigned to the variable
|
14
|
+
attr_accessor :value
|
15
|
+
|
16
|
+
# Create a variable with given name and initial value
|
17
|
+
# @param aName [String] The name of the variable
|
18
|
+
# @param aValue [Datatype::BuiltinDatatype] the initial assigned value
|
19
|
+
def initialize(aName, aValue = Datatype::Nil.instance)
|
20
|
+
init_name(aName)
|
21
|
+
@value = aValue
|
22
|
+
end
|
23
|
+
end # class
|
24
|
+
end # module
|
25
|
+
end # module
|
@@ -28,6 +28,13 @@ module Loxxy
|
|
28
28
|
true # Default implementation
|
29
29
|
end
|
30
30
|
|
31
|
+
# Check for inequality of this object with another Lox object
|
32
|
+
# @param other [Datatype::BuiltinDatatype, Object]
|
33
|
+
# @return [Datatype::Boolean]
|
34
|
+
def !=(other)
|
35
|
+
!(self == other)
|
36
|
+
end
|
37
|
+
|
31
38
|
# Negation ('not')
|
32
39
|
# Returns a boolean with opposite truthiness value.
|
33
40
|
# @return [Datatype::Boolean]
|
@@ -35,11 +42,18 @@ module Loxxy
|
|
35
42
|
falsey? ? True.instance : False.instance
|
36
43
|
end
|
37
44
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# @
|
41
|
-
def
|
42
|
-
|
45
|
+
# Returns the first falsey argument (if any),
|
46
|
+
# otherwise returns the last truthy argument.
|
47
|
+
# @param operand2 [Loxxy::Datatype::BuiltinDatatype, Proc]
|
48
|
+
def and(operand2)
|
49
|
+
falsey? ? self : logical_2nd_arg(operand2)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the first truthy argument (if any),
|
53
|
+
# otherwise returns the last falsey argument.
|
54
|
+
# @param operand2 [Loxxy::Datatype::BuiltinDatatype, Proc]
|
55
|
+
def or(operand2)
|
56
|
+
truthy? ? self : logical_2nd_arg(operand2)
|
43
57
|
end
|
44
58
|
|
45
59
|
# Method called from Lox to obtain the text representation of the boolean.
|
@@ -53,6 +67,19 @@ module Loxxy
|
|
53
67
|
def validated_value(aValue)
|
54
68
|
aValue
|
55
69
|
end
|
70
|
+
|
71
|
+
def logical_2nd_arg(operand2)
|
72
|
+
case operand2
|
73
|
+
when false
|
74
|
+
False.instance # Convert to Lox equivalent
|
75
|
+
when nil
|
76
|
+
Nil.instance # Convert to Lox equivalent
|
77
|
+
when true
|
78
|
+
True.instance # Convert to Lox equivalent
|
79
|
+
else
|
80
|
+
operand2
|
81
|
+
end
|
82
|
+
end
|
56
83
|
end # class
|
57
84
|
end # module
|
58
85
|
end # module
|
@@ -30,8 +30,8 @@ module Loxxy
|
|
30
30
|
rule('program' => 'declaration_plus EOF').as 'lox_program'
|
31
31
|
|
32
32
|
# Declarations: bind an identifier to something
|
33
|
-
rule('declaration_plus' => 'declaration_plus declaration')
|
34
|
-
rule('declaration_plus' => 'declaration')
|
33
|
+
rule('declaration_plus' => 'declaration_plus declaration').as 'declaration_plus_more'
|
34
|
+
rule('declaration_plus' => 'declaration').as 'declaration_plus_end'
|
35
35
|
rule('declaration' => 'classDecl')
|
36
36
|
rule('declaration' => 'funDecl')
|
37
37
|
rule('declaration' => 'varDecl')
|
@@ -46,8 +46,8 @@ module Loxxy
|
|
46
46
|
|
47
47
|
rule('funDecl' => 'FUN function')
|
48
48
|
|
49
|
-
rule('varDecl' => 'VAR IDENTIFIER SEMICOLON')
|
50
|
-
rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON')
|
49
|
+
rule('varDecl' => 'VAR IDENTIFIER SEMICOLON').as 'var_declaration'
|
50
|
+
rule('varDecl' => 'VAR IDENTIFIER EQUAL expression SEMICOLON').as 'var_initialization'
|
51
51
|
|
52
52
|
# Statements: produce side effects, but don't introduce bindings
|
53
53
|
rule('statement' => 'exprStmt')
|
@@ -68,9 +68,9 @@ module Loxxy
|
|
68
68
|
rule('forTest' => 'expression_opt SEMICOLON')
|
69
69
|
rule('forUpdate' => 'expression_opt')
|
70
70
|
|
71
|
-
rule('ifStmt' => 'IF ifCondition statement elsePart_opt')
|
72
|
-
rule('ifCondition' => 'LEFT_PAREN expression RIGHT_PAREN')
|
73
|
-
rule('elsePart_opt' => 'ELSE statement')
|
71
|
+
rule('ifStmt' => 'IF ifCondition statement elsePart_opt').as 'if_stmt'
|
72
|
+
rule('ifCondition' => 'LEFT_PAREN expression RIGHT_PAREN').as 'keep_symbol2'
|
73
|
+
rule('elsePart_opt' => 'ELSE statement').as 'keep_symbol2'
|
74
74
|
rule('elsePart_opt' => [])
|
75
75
|
|
76
76
|
rule('printStmt' => 'PRINT expression SEMICOLON').as 'print_stmt'
|
@@ -137,8 +137,8 @@ module Loxxy
|
|
137
137
|
rule('primary' => 'THIS')
|
138
138
|
rule('primary' => 'NUMBER').as 'literal_expr'
|
139
139
|
rule('primary' => 'STRING').as 'literal_expr'
|
140
|
-
rule('primary' => 'IDENTIFIER')
|
141
|
-
rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
|
140
|
+
rule('primary' => 'IDENTIFIER').as 'variable_expr'
|
141
|
+
rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN').as 'grouping_expr'
|
142
142
|
rule('primary' => 'SUPER DOT IDENTIFIER')
|
143
143
|
|
144
144
|
# Utility rules
|
data/lib/loxxy/interpreter.rb
CHANGED
@@ -31,7 +31,7 @@ module Loxxy
|
|
31
31
|
ast_tree = parser.parse(lox_input)
|
32
32
|
visitor = Ast::ASTVisitor.new(ast_tree)
|
33
33
|
|
34
|
-
# Back-end launches the tree walking &
|
34
|
+
# Back-end launches the tree walking & responds to visit events
|
35
35
|
# by executing the code determined by the visited AST node.
|
36
36
|
engine = BackEnd::Engine.new(config)
|
37
37
|
engine.execute(visitor)
|
data/lib/loxxy/version.rb
CHANGED
@@ -31,8 +31,17 @@ module Loxxy
|
|
31
31
|
context 'Listening to visitor events:' do
|
32
32
|
let(:greeting) { Datatype::LXString.new('Hello, world') }
|
33
33
|
let(:sample_pos) { double('fake-position') }
|
34
|
+
let(:var_decl) { Ast::LoxVarStmt.new(sample_pos, 'greeting', greeting) }
|
34
35
|
let(:lit_expr) { Ast::LoxLiteralExpr.new(sample_pos, greeting) }
|
35
36
|
|
37
|
+
|
38
|
+
it "should react to 'after_var_stmt' event" do
|
39
|
+
expect { subject.after_var_stmt(var_decl) }.not_to raise_error
|
40
|
+
current_env = subject.symbol_table.current_env
|
41
|
+
expect(current_env.defns['greeting']).to be_kind_of(Variable)
|
42
|
+
expect(current_env.defns['greeting'].value).to eq(greeting)
|
43
|
+
end
|
44
|
+
|
36
45
|
it "should react to 'before_literal_expr' event" do
|
37
46
|
expect { subject.before_literal_expr(lit_expr) }.not_to raise_error
|
38
47
|
expect(subject.stack.pop).to eq(greeting)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
4
|
+
|
5
|
+
# Load the class under test
|
6
|
+
require_relative '../../lib/loxxy/back_end/environment'
|
7
|
+
|
8
|
+
module Loxxy
|
9
|
+
module BackEnd
|
10
|
+
describe Environment do
|
11
|
+
let(:foo) { Datatype::LXString.new('foo') }
|
12
|
+
let(:bar) { Datatype::LXString.new('bar') }
|
13
|
+
let(:mother) { Environment.new }
|
14
|
+
subject { Environment.new(mother) }
|
15
|
+
|
16
|
+
# Shortand factory method.
|
17
|
+
def var(aName, aValue)
|
18
|
+
Variable.new(aName, aValue)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'Initialization:' do
|
22
|
+
it 'could be initialized without argument' do
|
23
|
+
expect { Environment.new }.not_to raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'could be initialized with a parent environment' do
|
27
|
+
expect { Environment.new(mother) }.not_to raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it "shouldn't have definitions by default" do
|
31
|
+
expect(subject.defns).to be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should know its parent (if any)' do
|
35
|
+
expect(subject.parent).to eq(mother)
|
36
|
+
end
|
37
|
+
end # context
|
38
|
+
|
39
|
+
context 'Provided services:' do
|
40
|
+
it 'should accept the addition of a variable' do
|
41
|
+
subject.insert(var('a', foo))
|
42
|
+
expect(subject.defns).not_to be_empty
|
43
|
+
var_a = subject.defns['a']
|
44
|
+
expect(var_a).to be_kind_of(Variable)
|
45
|
+
expect(var_a.name).to eq('a')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should accept the addition of multiple variables' do
|
49
|
+
subject.insert(var('a', foo))
|
50
|
+
expect(subject.defns).not_to be_empty
|
51
|
+
|
52
|
+
subject.insert(var('b', bar))
|
53
|
+
var_b = subject.defns['b']
|
54
|
+
expect(var_b).to be_kind_of(Variable)
|
55
|
+
expect(var_b.name).to eq('b')
|
56
|
+
end
|
57
|
+
|
58
|
+
# it 'should set the suffix of just created variable' do
|
59
|
+
# subject.insert(var('a'))
|
60
|
+
# var_a = subject.defns['a']
|
61
|
+
# expect(var_a.suffix).to eq("_#{subject.object_id.to_s(16)}")
|
62
|
+
# end
|
63
|
+
|
64
|
+
# it 'should complain when variable names collide' do
|
65
|
+
# subject.insert(var('c'))
|
66
|
+
# expect(subject.defns['c']).to be_kind_of(Datatype::Variable)
|
67
|
+
# err = StandardError
|
68
|
+
# err_msg = "Variable with name 'c' already exists."
|
69
|
+
# expect { subject.insert(var('c')) }.to raise_error(err, err_msg)
|
70
|
+
# end
|
71
|
+
end # context
|
72
|
+
end # describe
|
73
|
+
end # module
|
74
|
+
end # module
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../spec_helper' # Use the RSpec framework
|
4
|
+
require_relative '../../lib/loxxy/back_end/variable'
|
5
|
+
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../lib/loxxy/back_end/symbol_table'
|
8
|
+
|
9
|
+
module Loxxy
|
10
|
+
module BackEnd
|
11
|
+
describe SymbolTable do
|
12
|
+
subject { SymbolTable.new }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'should be initialized without argument' do
|
16
|
+
expect { SymbolTable.new }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should have a root BackEnd' do
|
20
|
+
expect(subject.root).not_to be_nil
|
21
|
+
expect(subject.current_env).to eq(subject.root)
|
22
|
+
expect(subject.root).to be_kind_of(BackEnd::Environment)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "shouldn't have names at initialization" do
|
26
|
+
expect(subject.name2envs).to be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be empty at initialization' do
|
30
|
+
expect(subject).to be_empty
|
31
|
+
end
|
32
|
+
end # context
|
33
|
+
|
34
|
+
context 'Provided services:' do
|
35
|
+
def var(aName)
|
36
|
+
Variable.new(aName)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should allow the addition of a variable' do
|
40
|
+
expect { subject.insert(var('q')) }.not_to raise_error
|
41
|
+
expect(subject).not_to be_empty
|
42
|
+
expect(subject.name2envs['q']).to be_kind_of(Array)
|
43
|
+
expect(subject.name2envs['q'].size).to eq(1)
|
44
|
+
expect(subject.name2envs['q'].first).to eq(subject.current_env)
|
45
|
+
expect(subject.current_env.defns['q']).to be_kind_of(BackEnd::Variable)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should allow the addition of several labels for same env' do
|
49
|
+
i_name = subject.insert(var('q'))
|
50
|
+
#expect(i_name).to match(/^q_[0-9a-z]*$/)
|
51
|
+
|
52
|
+
expect { subject.insert(var('x')) }.not_to raise_error
|
53
|
+
expect(subject.name2envs['x']).to be_kind_of(Array)
|
54
|
+
expect(subject.name2envs['x'].first).to eq(subject.current_env)
|
55
|
+
expect(subject.current_env.defns['x']).to be_kind_of(BackEnd::Variable)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should allow the entry into a new scope' do
|
59
|
+
subject.insert(var('q'))
|
60
|
+
new_env = BackEnd::Environment.new
|
61
|
+
expect { subject.enter_environment(new_env) }.not_to raise_error
|
62
|
+
expect(subject.current_env).to eq(new_env)
|
63
|
+
expect(subject.current_env.parent).to eq(subject.root)
|
64
|
+
expect(subject.name2envs['q']).to eq([subject.root])
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should allow the addition of same name in different scopes' do
|
68
|
+
subject.insert(var('q'))
|
69
|
+
subject.enter_environment(BackEnd::Environment.new)
|
70
|
+
subject.insert(var('q'))
|
71
|
+
expect(subject.name2envs['q']).to be_kind_of(Array)
|
72
|
+
expect(subject.name2envs['q'].size).to eq(2)
|
73
|
+
expect(subject.name2envs['q'].first).to eq(subject.root)
|
74
|
+
expect(subject.name2envs['q'].last).to eq(subject.current_env)
|
75
|
+
expect(subject.current_env.defns['q']).to be_kind_of(BackEnd::Variable)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should allow the removal of a scope' do
|
79
|
+
subject.insert(var('q'))
|
80
|
+
new_env = BackEnd::Environment.new
|
81
|
+
subject.enter_environment(new_env)
|
82
|
+
subject.insert(var('q'))
|
83
|
+
expect(subject.name2envs['q'].size).to eq(2)
|
84
|
+
|
85
|
+
expect { subject.leave_environment }.not_to raise_error
|
86
|
+
expect(subject.current_env).to eq(subject.root)
|
87
|
+
expect(subject.name2envs['q'].size).to eq(1)
|
88
|
+
expect(subject.name2envs['q']).to eq([subject.root])
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should allow the search of an entry based on its name' do
|
92
|
+
subject.insert(var('q'))
|
93
|
+
subject.insert(var('x'))
|
94
|
+
subject.enter_environment(BackEnd::Environment.new)
|
95
|
+
subject.insert(var('q'))
|
96
|
+
subject.insert(var('y'))
|
97
|
+
|
98
|
+
# Search for unknown name
|
99
|
+
expect(subject.lookup('z')).to be_nil
|
100
|
+
|
101
|
+
# Search for existing unique names
|
102
|
+
expect(subject.lookup('y')).to eq(subject.current_env.defns['y'])
|
103
|
+
expect(subject.lookup('x')).to eq(subject.root.defns['x'])
|
104
|
+
|
105
|
+
# Search for redefined name
|
106
|
+
expect(subject.lookup('q')).to eq(subject.current_env.defns['q'])
|
107
|
+
end
|
108
|
+
|
109
|
+
# it 'should allow the search of an entry based on its i_name' do
|
110
|
+
# subject.insert(var('q'))
|
111
|
+
# i_name_x = subject.insert(var('x'))
|
112
|
+
# subject.enter_environment(BackEnd::Environment.new)
|
113
|
+
# i_name_q2 = subject.insert(var('q'))
|
114
|
+
# i_name_y = subject.insert(var('y'))
|
115
|
+
|
116
|
+
# # Search for unknown i_name
|
117
|
+
# expect(subject.lookup_i_name('dummy')).to be_nil
|
118
|
+
|
119
|
+
# curr_scope = subject.current_env
|
120
|
+
# # # Search for existing unique names
|
121
|
+
# expect(subject.lookup_i_name(i_name_y)).to eq(curr_scope.defns['y'])
|
122
|
+
# expect(subject.lookup_i_name(i_name_x)).to eq(subject.root.defns['x'])
|
123
|
+
|
124
|
+
# # Search for redefined name
|
125
|
+
# expect(subject.lookup_i_name(i_name_q2)).to eq(curr_scope.defns['q'])
|
126
|
+
# end
|
127
|
+
|
128
|
+
it 'should list all the variables defined in all the szcope chain' do
|
129
|
+
subject.insert(var('q'))
|
130
|
+
subject.enter_environment(BackEnd::Environment.new)
|
131
|
+
subject.insert(var('x'))
|
132
|
+
subject.enter_environment(BackEnd::Environment.new)
|
133
|
+
subject.insert(var('y'))
|
134
|
+
subject.insert(var('x'))
|
135
|
+
|
136
|
+
vars = subject.all_variables
|
137
|
+
expect(vars.map(&:name)).to eq(%w[q x y x])
|
138
|
+
end
|
139
|
+
end # context
|
140
|
+
end # describe
|
141
|
+
end # module
|
142
|
+
end # module
|