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.
@@ -51,6 +51,30 @@ module Loxxy
51
51
  broadcast(:after_ptree, aParseTree)
52
52
  end
53
53
 
54
+ # Visit event. The visitor is about to visit a variable declaration statement.
55
+ # @param aPrintStmt [AST::LOXVarStmt] the variable declaration node to visit
56
+ def visit_seq_decl(aSeqDecls)
57
+ broadcast(:before_seq_decl, aSeqDecls)
58
+ traverse_subnodes(aSeqDecls)
59
+ broadcast(:after_seq_decl, aSeqDecls)
60
+ end
61
+
62
+ # Visit event. The visitor is about to visit a variable declaration statement.
63
+ # @param aPrintStmt [AST::LOXVarStmt] the variable declaration node to visit
64
+ def visit_var_stmt(aVarStmt)
65
+ broadcast(:before_var_stmt, aVarStmt)
66
+ traverse_subnodes(aVarStmt)
67
+ broadcast(:after_var_stmt, aVarStmt)
68
+ end
69
+
70
+ # Visit event. The visitor is about to visit a if statement.
71
+ # @param anIfStmt [AST::LOXIfStmt] the if statement node to visit
72
+ def visit_if_stmt(anIfStmt)
73
+ broadcast(:before_if_stmt, anIfStmt)
74
+ traverse_subnodes(anIfStmt) # The condition is visited/evaluated here...
75
+ broadcast(:after_if_stmt, anIfStmt, self)
76
+ end
77
+
54
78
  # Visit event. The visitor is about to visit a print statement.
55
79
  # @param aPrintStmt [AST::LOXPrintStmt] the print statement node to visit
56
80
  def visit_print_stmt(aPrintStmt)
@@ -59,6 +83,20 @@ module Loxxy
59
83
  broadcast(:after_print_stmt, aPrintStmt)
60
84
  end
61
85
 
86
+ # Visit event. The visitor is about to visit a logical expression.
87
+ # Since logical expressions may take shorcuts by not evaluating all their
88
+ # sub-expressiosns, they are responsible for visiting or not their children.
89
+ # @param aBinaryExpr [AST::LOXBinaryExpr] the logical expression node to visit
90
+ def visit_logical_expr(aLogicalExpr)
91
+ broadcast(:before_logical_expr, aLogicalExpr)
92
+
93
+ # As logical connectors may take a shortcut only the first argument is visited
94
+ traverse_given_subnode(aLogicalExpr, 0)
95
+
96
+ # The second child could be visited: this action is deferred in handler
97
+ broadcast(:after_logical_expr, aLogicalExpr, self)
98
+ end
99
+
62
100
  # Visit event. The visitor is about to visit a binary expression.
63
101
  # @param aBinaryExpr [AST::LOXBinaryExpr] the binary expression node to visit
64
102
  def visit_binary_expr(aBinaryExpr)
@@ -75,6 +113,14 @@ module Loxxy
75
113
  broadcast(:after_unary_expr, anUnaryExpr)
76
114
  end
77
115
 
116
+ # Visit event. The visitor is about to visit a grouping expression.
117
+ # @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
118
+ def visit_grouping_expr(aGroupingExpr)
119
+ broadcast(:before_grouping_expr, aGroupingExpr)
120
+ traverse_subnodes(aGroupingExpr)
121
+ broadcast(:after_grouping_expr, aGroupingExpr)
122
+ end
123
+
78
124
  # Visit event. The visitor is visiting the
79
125
  # given terminal node containing a datatype object.
80
126
  # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
@@ -83,8 +129,15 @@ module Loxxy
83
129
  broadcast(:after_literal_expr, aLiteralExpr)
84
130
  end
85
131
 
132
+ # Visit event. The visitor is visiting a variable reference node
133
+ # @param aLiteralExpr [AST::LoxVariableExpr] the variable reference node to visit.
134
+ def visit_variable_expr(aVariableExpr)
135
+ broadcast(:before_variable_expr, aVariableExpr)
136
+ broadcast(:after_variable_expr, aVariableExpr, self)
137
+ end
138
+
86
139
  # Visit event. The visitor is about to visit the given non terminal node.
87
- # @param aNonTerminalNode [Rley::PTre::NonTerminalNode] the node to visit.
140
+ # @param aNonTerminalNode [Rley::PTree::NonTerminalNode] the node to visit.
88
141
  def visit_nonterminal(_non_terminal_node)
89
142
  # Loxxy interpreter encountered a CST node (Concrete Syntax Tree)
90
143
  # that it cannot handle.
@@ -106,6 +159,20 @@ module Loxxy
106
159
  broadcast(:after_subnodes, aParentNode, subnodes)
107
160
  end
108
161
 
162
+ # Visit event. The visitor is about to visit one given subnode of a non
163
+ # terminal node.
164
+ # @param aParentNode [Ast::LocCompoundExpr] the parent node.
165
+ # @param index [integer] index of child subnode
166
+ def traverse_given_subnode(aParentNode, index)
167
+ subnode = aParentNode.subnodes[index]
168
+ broadcast(:before_given_subnode, aParentNode, subnode)
169
+
170
+ # Now, let's proceed with the visit of that subnode
171
+ subnode.accept(self)
172
+
173
+ broadcast(:after_given_subnode, aParentNode, subnode)
174
+ end
175
+
109
176
  # Send a notification to all subscribers.
110
177
  # @param msg [Symbol] event to notify
111
178
  # @param args [Array] arguments of the notification.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxGroupingExpr < LoxCompoundExpr
8
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
+ # @param subExpr [Loxxy::Ast::LoxNode]
10
+ def initialize(aPosition, subExpr)
11
+ super(aPosition, [subExpr])
12
+ end
13
+
14
+ # Part of the 'visitee' role in Visitor design pattern.
15
+ # @param visitor [Ast::ASTVisitor] the visitor
16
+ def accept(visitor)
17
+ visitor.visit_grouping_expr(self)
18
+ end
19
+
20
+ alias operands subnodes
21
+ end # class
22
+ end # module
23
+ end # module
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxIfStmt < LoxCompoundExpr
8
+ # @return [LoxNode] code of then branch
9
+ attr_reader :then_stmt
10
+
11
+ # @return [LoxNode, NilClass] code of else branch
12
+ attr_reader :else_stmt
13
+
14
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
15
+ # @param subExpr [Loxxy::Ast::LoxNode]
16
+ def initialize(aPosition, condExpr, thenStmt, elseStmt)
17
+ super(aPosition, [condExpr])
18
+ @then_stmt = thenStmt
19
+ @else_stmt = elseStmt
20
+ end
21
+
22
+ # Part of the 'visitee' role in Visitor design pattern.
23
+ # @param visitor [Ast::ASTVisitor] the visitor
24
+ def accept(visitor)
25
+ visitor.visit_if_stmt(self)
26
+ end
27
+
28
+ # Accessor to the condition expression
29
+ # @return [LoxNode]
30
+ def condition
31
+ subnodes[0]
32
+ end
33
+ end # class
34
+ end # module
35
+ end # module
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxLogicalExpr < LoxCompoundExpr
8
+ # @return [Symbol] message name to be sent to receiver
9
+ attr_reader :operator
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param operand1 [Loxxy::Ast::LoxNode]
13
+ # @param operand2 [Loxxy::Ast::LoxNode]
14
+ def initialize(aPosition, anOperator, operand1, operand2)
15
+ super(aPosition, [operand1, operand2])
16
+ @operator = anOperator
17
+ end
18
+
19
+ # Part of the 'visitee' role in Visitor design pattern.
20
+ # @param visitor [Ast::ASTVisitor] the visitor
21
+ def accept(visitor)
22
+ visitor.visit_logical_expr(self)
23
+ end
24
+
25
+ alias operands subnodes
26
+ end # class
27
+ end # module
28
+ end # module
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxSeqDecl < LoxCompoundExpr
8
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
+ # @param declarations [Arrya<Loxxy::Ast::LoxNode>]
10
+ def initialize(aPosition, declarations)
11
+ super(aPosition, declarations)
12
+ end
13
+
14
+ # Part of the 'visitee' role in Visitor design pattern.
15
+ # @param visitor [Ast::ASTVisitor] the visitor
16
+ def accept(visitor)
17
+ visitor.visit_seq_decl(self)
18
+ end
19
+
20
+ alias operands subnodes
21
+ end # class
22
+ end # module
23
+ end # module
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ # This AST node represents a variable declaration
8
+ class LoxVarStmt < LoxCompoundExpr
9
+ # @return [String] variable name
10
+ attr_reader :name
11
+
12
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
13
+ # @param aName [String] name of the variable
14
+ # @param aValue [Loxxy::Ast::LoxNode, NilClass] initial value for the variable
15
+ def initialize(aPosition, aName, aValue)
16
+ initial_value = aValue ? [aValue] : []
17
+ super(aPosition, initial_value)
18
+ @name = aName
19
+ end
20
+
21
+ # Part of the 'visitee' role in Visitor design pattern.
22
+ # @param visitor [Ast::ASTVisitor] the visitor
23
+ def accept(visitor)
24
+ visitor.visit_var_stmt(self)
25
+ end
26
+ end # class
27
+ end # module
28
+ end # module
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_node'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ # This AST node represents a mention of a variable
8
+ class LoxVariableExpr < LoxNode
9
+ # @return [String] variable name
10
+ attr_reader :name
11
+
12
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
13
+ # @param aName [String] name of the variable
14
+ def initialize(aPosition, aName)
15
+ super(aPosition)
16
+ @name = aName
17
+ end
18
+
19
+ # Part of the 'visitee' role in Visitor design pattern.
20
+ # @param visitor [Ast::ASTVisitor] the visitor
21
+ def accept(visitor)
22
+ visitor.visit_variable_expr(self)
23
+ end
24
+ end # class
25
+ end # module
26
+ end # module
@@ -2,6 +2,7 @@
2
2
 
3
3
  # Load all the classes implementing AST nodes
4
4
  require_relative '../ast/all_lox_nodes'
5
+ require_relative 'symbol_table'
5
6
 
6
7
  module Loxxy
7
8
  module BackEnd
@@ -12,13 +13,17 @@ module Loxxy
12
13
  # @return [Hash] A set of configuration options
13
14
  attr_reader :config
14
15
 
15
- # @return [Array] Data stack used for passing data between statements
16
+ # @return [BackEnd::SymbolTable]
17
+ attr_reader :symbol_table
18
+
19
+ # @return [Array<Datatype::BuiltinDatatyp>] Stack for the values of expr
16
20
  attr_reader :stack
17
21
 
18
22
  # @param theOptions [Hash]
19
23
  def initialize(theOptions)
20
24
  @config = theOptions
21
25
  @ostream = config.include?(:ostream) ? config[:ostream] : $stdout
26
+ @symbol_table = SymbolTable.new
22
27
  @stack = []
23
28
  end
24
29
 
@@ -33,13 +38,66 @@ module Loxxy
33
38
  stack.empty? ? Datatype::Nil.instance : stack.pop
34
39
  end
35
40
 
41
+ ##########################################################################
36
42
  # Visit event handling
43
+ ##########################################################################
44
+
45
+ def after_seq_decl(aSeqDecls)
46
+ # Do nothing, subnodes were already evaluated
47
+ end
48
+
49
+ def after_var_stmt(aVarStmt)
50
+ new_var = Variable.new(aVarStmt.name, aVarStmt.subnodes[0])
51
+ symbol_table.insert(new_var)
52
+ end
53
+
54
+ def after_if_stmt(anIfStmt, aVisitor)
55
+ # Retrieve the result of the condition evaluation
56
+ condition = stack.pop
57
+ if condition.truthy?
58
+ result = anIfStmt.then_stmt.accept(aVisitor)
59
+ elsif anIfStmt.else_stmt
60
+ anIfStmt.else_stmt.accept(aVisitor)
61
+ end
62
+ end
37
63
 
38
64
  def after_print_stmt(_printStmt)
39
65
  tos = stack.pop
40
66
  @ostream.print tos.to_str
41
67
  end
42
68
 
69
+ def after_logical_expr(aLogicalExpr, visitor)
70
+ op = aLogicalExpr.operator
71
+ operand1 = stack.pop # only first operand was evaluated
72
+ result = nil
73
+ if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
74
+ result = operand1
75
+ else
76
+ raw_operand2 = aLogicalExpr.subnodes[1]
77
+ raw_operand2.accept(visitor) # Visit means operand2 is evaluated
78
+ operand2 = stack.pop
79
+ result = logical_2nd_arg(operand2)
80
+ end
81
+
82
+ stack.push result
83
+ end
84
+
85
+ def logical_2nd_arg(operand2)
86
+ case operand2
87
+ when false
88
+ False.instance # Convert to Lox equivalent
89
+ when nil
90
+ Nil.instance # Convert to Lox equivalent
91
+ when true
92
+ True.instance # Convert to Lox equivalent
93
+ when Proc
94
+ # Second operand wasn't yet evaluated...
95
+ operand2.call
96
+ else
97
+ operand2
98
+ end
99
+ end
100
+
43
101
  def after_binary_expr(aBinaryExpr)
44
102
  op = aBinaryExpr.operator
45
103
  operand2 = stack.pop
@@ -63,6 +121,17 @@ module Loxxy
63
121
  end
64
122
  end
65
123
 
124
+ def after_grouping_expr(_groupingExpr)
125
+ # Do nothing: work was already done by visiting /evaluating the subexpression
126
+ end
127
+
128
+ def after_variable_expr(aVarExpr, aVisitor)
129
+ var_name = aVarExpr.name
130
+ var = symbol_table.lookup(var_name)
131
+ raise StandardError, "Unknown variable #{var_name}" unless var
132
+ var.value.accept(aVisitor) # Evaluate the variable value
133
+ end
134
+
66
135
  # @param literalExpr [Ast::LoxLiteralExpr]
67
136
  def before_literal_expr(literalExpr)
68
137
  stack.push(literalExpr.literal)
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loxxy
4
+ module BackEnd
5
+ # Mix-in module that implements the expected common behaviour of entries
6
+ # placed in the symbol table.
7
+ module Entry
8
+ # @return [String] User-defined name of the entry.
9
+ attr_reader :name
10
+
11
+ =begin
12
+ # @return [String] Suffix for building the internal name of the entry.
13
+ attr_accessor :suffix
14
+ =end
15
+
16
+ # Initialize the entry with given name
17
+ # @param aName [String] The name of the entry
18
+ def init_name(aName)
19
+ @name = aName.dup
20
+ @name.freeze
21
+ end
22
+ =begin
23
+ # Return the internal name of the entry
24
+ # Internal names used to disambiguate entry names.
25
+ # There might be homonyns between variable because:
26
+ # - A child Scope may have a entry with same name as one of its
27
+ # ancestor(s).
28
+ # - Multiple calls to same defrel or procedure may imply multiple creation
29
+ # of a entry given name...
30
+ # @return [String] internal name
31
+ def i_name
32
+ if suffix =~ /^_/
33
+ label + suffix
34
+ else
35
+ (suffix.nil? || suffix.empty?) ? label : suffix
36
+ end
37
+ end
38
+ =end
39
+ end # module
40
+ end # module
41
+ end # module
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'variable'
4
+
5
+ module Loxxy
6
+ module BackEnd
7
+ # A environment is a name space that corresponds either to a specific
8
+ # delimited region in Loxxy source code or to an activation record
9
+ # of a relation or a relation definition.
10
+ # It contains a map of names to the objects they name (e.g. logical var)
11
+ class Environment
12
+ # The parent (enclosing) environment.
13
+ # @return [Environment, NilClass]
14
+ attr_accessor :parent
15
+
16
+ # Mapping from user-defined name to related definition
17
+ # (say, a variable object)
18
+ # @return [Hash{String => Variable}] Pairs of the kind
19
+ attr_reader :defns
20
+
21
+ # Construct a environment instance.
22
+ # @param aParent [Environment, NilClass] Parent environment to this one.
23
+ def initialize(aParent = nil)
24
+ @parent = aParent
25
+ @defns = {}
26
+ end
27
+
28
+ # Add a new variable to the environment.
29
+ # @param anEntry [BackEnd::Variable]
30
+ # @return [BackEnd::Variable] the variable
31
+ def insert(anEntry)
32
+ e = validated_entry(anEntry)
33
+ # e.suffix = default_suffix if e.kind_of?(BackEnd::Variable)
34
+ defns[e.name] = e
35
+
36
+ e
37
+ end
38
+
39
+ # Returns a string with a human-readable representation of the object.
40
+ # @return [String]
41
+ def inspect
42
+ +"#<#{self.class}:#{object_id.to_s(16)}>"
43
+ end
44
+
45
+ private
46
+
47
+ def validated_entry(anEntry)
48
+ name = anEntry.name
49
+ unless name.kind_of?(String) && !name.empty?
50
+ err_msg = 'Invalid variable name argument.'
51
+ raise StandardError, err_msg
52
+ end
53
+ if defns.include?(name) && !anEntry.kind_of?(Variable)
54
+ err_msg = "Variable with name '#{name}' already exists."
55
+ raise StandardError, err_msg
56
+ end
57
+
58
+ anEntry
59
+ end
60
+
61
+ # def default_suffix
62
+ # @default_suffix ||= "_#{object_id.to_s(16)}"
63
+ # end
64
+ end # class
65
+ end # module
66
+ end # module