loxxy 0.0.16 → 0.0.21

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,7 +51,23 @@ module Loxxy
51
51
  broadcast(:after_ptree, aParseTree)
52
52
  end
53
53
 
54
- # Visit event. The visitor is about to visit a print statement expression.
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_var_stmt(aVarStmt)
57
+ broadcast(:before_var_stmt, aVarStmt)
58
+ traverse_subnodes(aVarStmt)
59
+ broadcast(:after_var_stmt, aVarStmt)
60
+ end
61
+
62
+ # Visit event. The visitor is about to visit a if statement.
63
+ # @param anIfStmt [AST::LOXIfStmt] the if statement node to visit
64
+ def visit_if_stmt(anIfStmt)
65
+ broadcast(:before_if_stmt, anIfStmt)
66
+ traverse_subnodes(anIfStmt) # The condition is visited/evaluated here...
67
+ broadcast(:after_if_stmt, anIfStmt, self)
68
+ end
69
+
70
+ # Visit event. The visitor is about to visit a print statement.
55
71
  # @param aPrintStmt [AST::LOXPrintStmt] the print statement node to visit
56
72
  def visit_print_stmt(aPrintStmt)
57
73
  broadcast(:before_print_stmt, aPrintStmt)
@@ -59,7 +75,21 @@ module Loxxy
59
75
  broadcast(:after_print_stmt, aPrintStmt)
60
76
  end
61
77
 
62
- # Visit event. The visitor is about to visit a print statement expression.
78
+ # Visit event. The visitor is about to visit a logical expression.
79
+ # Since logical expressions may take shorcuts by not evaluating all their
80
+ # sub-expressiosns, they are responsible for visiting or not their children.
81
+ # @param aBinaryExpr [AST::LOXBinaryExpr] the logical expression node to visit
82
+ def visit_logical_expr(aLogicalExpr)
83
+ broadcast(:before_logical_expr, aLogicalExpr)
84
+
85
+ # As logical connectors may take a shortcut only the first argument is visited
86
+ traverse_given_subnode(aLogicalExpr, 0)
87
+
88
+ # The second child could be visited: this action is deferred in handler
89
+ broadcast(:after_logical_expr, aLogicalExpr, self)
90
+ end
91
+
92
+ # Visit event. The visitor is about to visit a binary expression.
63
93
  # @param aBinaryExpr [AST::LOXBinaryExpr] the binary expression node to visit
64
94
  def visit_binary_expr(aBinaryExpr)
65
95
  broadcast(:before_binary_expr, aBinaryExpr)
@@ -67,6 +97,22 @@ module Loxxy
67
97
  broadcast(:after_binary_expr, aBinaryExpr)
68
98
  end
69
99
 
100
+ # Visit event. The visitor is about to visit an unary expression.
101
+ # @param anUnaryExpr [AST::anUnaryExpr] unary expression node to visit
102
+ def visit_unary_expr(anUnaryExpr)
103
+ broadcast(:before_unary_expr, anUnaryExpr)
104
+ traverse_subnodes(anUnaryExpr)
105
+ broadcast(:after_unary_expr, anUnaryExpr)
106
+ end
107
+
108
+ # Visit event. The visitor is about to visit a grouping expression.
109
+ # @param aGroupingExpr [AST::LoxGroupingExpr] grouping expression to visit
110
+ def visit_grouping_expr(aGroupingExpr)
111
+ broadcast(:before_grouping_expr, aGroupingExpr)
112
+ traverse_subnodes(aGroupingExpr)
113
+ broadcast(:after_grouping_expr, aGroupingExpr)
114
+ end
115
+
70
116
  # Visit event. The visitor is visiting the
71
117
  # given terminal node containing a datatype object.
72
118
  # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
@@ -98,6 +144,20 @@ module Loxxy
98
144
  broadcast(:after_subnodes, aParentNode, subnodes)
99
145
  end
100
146
 
147
+ # Visit event. The visitor is about to visit one given subnode of a non
148
+ # terminal node.
149
+ # @param aParentNode [Ast::LocCompoundExpr] the parent node.
150
+ # @param index [integer] index of child subnode
151
+ def traverse_given_subnode(aParentNode, index)
152
+ subnode = aParentNode.subnodes[index]
153
+ broadcast(:before_given_subnode, aParentNode, subnode)
154
+
155
+ # Now, let's proceed with the visit of that subnode
156
+ subnode.accept(self)
157
+
158
+ broadcast(:after_given_subnode, aParentNode, subnode)
159
+ end
160
+
101
161
  # Send a notification to all subscribers.
102
162
  # @param msg [Symbol] event to notify
103
163
  # @param args [Array] arguments of the notification.
@@ -5,7 +5,7 @@ require_relative 'lox_compound_expr'
5
5
  module Loxxy
6
6
  module Ast
7
7
  class LoxBinaryExpr < LoxCompoundExpr
8
- # @return [Symbol]
8
+ # @return [Symbol] message name to be sent to receiver
9
9
  attr_reader :operator
10
10
 
11
11
  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
@@ -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,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxUnaryExpr < 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 operand [Loxxy::Ast::LoxNode]
13
+ def initialize(aPosition, anOperator, operand)
14
+ super(aPosition, [operand])
15
+ @operator = anOperator
16
+ end
17
+
18
+ # Part of the 'visitee' role in Visitor design pattern.
19
+ # @param visitor [Ast::ASTVisitor] the visitor
20
+ def accept(visitor)
21
+ visitor.visit_unary_expr(self)
22
+ end
23
+
24
+ alias operands subnodes
25
+ end # class
26
+ end # module
27
+ 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
@@ -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,17 +13,21 @@ 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
 
25
- # Given an abstract syntax parse tree visitor, luanch the visit
30
+ # Given an abstract syntax parse tree visitor, launch the visit
26
31
  # and execute the visit events in the output stream.
27
32
  # @param aVisitor [AST::ASTVisitor]
28
33
  # @return [Loxxy::Datatype::BuiltinDatatype]
@@ -33,19 +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
+ def after_var_stmt(aVarStmt)
45
+ new_var = Variable.new(aVarStmt.name, aVarStmt.subnodes[0])
46
+ symbol_table.insert(new_var)
47
+ end
48
+
49
+ def after_if_stmt(anIfStmt, aVisitor)
50
+ # Retrieve the result of the condition evaluation
51
+ condition = stack.pop
52
+ if condition.truthy?
53
+ result = anIfStmt.then_stmt.accept(aVisitor)
54
+ elsif anIfStmt.else_stmt
55
+ anIfStmt.else_stmt.accept(aVisitor)
56
+ end
57
+ end
37
58
 
38
59
  def after_print_stmt(_printStmt)
39
60
  tos = stack.pop
40
61
  @ostream.print tos.to_str
41
62
  end
42
63
 
64
+ def after_logical_expr(aLogicalExpr, visitor)
65
+ op = aLogicalExpr.operator
66
+ operand1 = stack.pop # only first operand was evaluated
67
+ result = nil
68
+ if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
69
+ result = operand1
70
+ else
71
+ raw_operand2 = aLogicalExpr.subnodes[1]
72
+ raw_operand2.accept(visitor) # Visit means operand2 is evaluated
73
+ operand2 = stack.pop
74
+ result = logical_2nd_arg(operand2)
75
+ end
76
+
77
+ stack.push result
78
+ end
79
+
80
+ def logical_2nd_arg(operand2)
81
+ case operand2
82
+ when false
83
+ False.instance # Convert to Lox equivalent
84
+ when nil
85
+ Nil.instance # Convert to Lox equivalent
86
+ when true
87
+ True.instance # Convert to Lox equivalent
88
+ when Proc
89
+ # Second operand wasn't yet evaluated...
90
+ operand2.call
91
+ else
92
+ operand2
93
+ end
94
+ end
95
+
43
96
  def after_binary_expr(aBinaryExpr)
44
97
  op = aBinaryExpr.operator
45
98
  operand2 = stack.pop
46
99
  operand1 = stack.pop
47
- implemented = %i[+ - * / == !=].include?(op)
48
- if implemented && operand1.respond_to?(op)
100
+ if operand1.respond_to?(op)
49
101
  stack.push operand1.send(op, operand2)
50
102
  else
51
103
  msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
@@ -53,6 +105,21 @@ module Loxxy
53
105
  end
54
106
  end
55
107
 
108
+ def after_unary_expr(anUnaryExpr)
109
+ op = anUnaryExpr.operator
110
+ operand = stack.pop
111
+ if operand.respond_to?(op)
112
+ stack.push operand.send(op)
113
+ else
114
+ msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
115
+ raise StandardError, msg1
116
+ end
117
+ end
118
+
119
+ def after_grouping_expr(_groupingExpr)
120
+ # Do nothing: work was already done by visiting /evaluating the subexpression
121
+ end
122
+
56
123
  # @param literalExpr [Ast::LoxLiteralExpr]
57
124
  def before_literal_expr(literalExpr)
58
125
  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