loxxy 0.0.10 → 0.0.15

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.
@@ -39,75 +39,63 @@ module Loxxy
39
39
  top.accept(self)
40
40
  end
41
41
 
42
- # Visit event. The visitor is visiting the
43
- # given terminal node containing a datatype object.
44
- # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
45
- def visit_literal_expr(aLiteralExpr)
46
- broadcast(:before_literal_expr, aLiteralExpr)
47
- broadcast(:after_literal_expr, aLiteralExpr)
42
+ # Visit event. The visitor is about to visit the ptree.
43
+ # @param aParseTree [Rley::PTre::ParseTree] the ptree to visit.
44
+ def start_visit_ptree(aParseTree)
45
+ broadcast(:before_ptree, aParseTree)
48
46
  end
49
47
 
50
- =begin
48
+ # Visit event. The visitor has completed the visit of the ptree.
49
+ # @param aParseTree [Rley::PTre::ParseTree] the visited ptree.
50
+ def end_visit_ptree(aParseTree)
51
+ broadcast(:after_ptree, aParseTree)
52
+ end
51
53
 
54
+ # Visit event. The visitor is about to visit a print statement expression.
55
+ # @param aPrintStmt [AST::LOXPrintStmt] the print statement node to visit
56
+ def visit_print_stmt(aPrintStmt)
57
+ broadcast(:before_print_stmt, aPrintStmt)
58
+ traverse_subnodes(aPrintStmt)
59
+ broadcast(:after_print_stmt, aPrintStmt)
60
+ end
52
61
 
53
- def visit_compound_datum(aCompoundDatum)
54
- broadcast(:before_compound_datum, aCompoundDatum)
55
- traverse_children(aCompoundDatum)
56
- broadcast(:after_compound_datum, aCompoundDatum)
62
+ # Visit event. The visitor is about to visit a print statement expression.
63
+ # @param aBinaryExpr [AST::LOXBinaryExpr] the binary expression node to visit
64
+ def visit_binary_expr(aBinaryExpr)
65
+ broadcast(:before_binary_expr, aBinaryExpr)
66
+ traverse_subnodes(aBinaryExpr)
67
+ broadcast(:after_binary_expr, aBinaryExpr)
57
68
  end
58
69
 
59
70
  # Visit event. The visitor is visiting the
60
- # given empty list object.
61
- # @param anEmptyList [SkmEmptyList] the empty list object to visit.
62
- def visit_empty_list(anEmptyList)
63
- broadcast(:before_empty_list, anEmptyList)
64
- broadcast(:after_empty_list, anEmptyList)
71
+ # given terminal node containing a datatype object.
72
+ # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
73
+ def visit_literal_expr(aLiteralExpr)
74
+ broadcast(:before_literal_expr, aLiteralExpr)
75
+ broadcast(:after_literal_expr, aLiteralExpr)
65
76
  end
66
77
 
67
- def visit_pair(aPair)
68
- broadcast(:before_pair, aPair)
69
- traverse_car_cdr(aPair)
70
- broadcast(:after_pair, aPair)
71
- end
72
- =end
73
- =begin
74
- # Visit event. The visitor is about to visit the given non terminal node.
75
- # @param aNonTerminalNode [NonTerminalNode] the node to visit.
76
- def visit_nonterminal(aNonTerminalNode)
77
- if @traversal == :post_order
78
- broadcast(:before_non_terminal, aNonTerminalNode)
79
- traverse_subnodes(aNonTerminalNode)
80
- else
81
- traverse_subnodes(aNonTerminalNode)
82
- broadcast(:before_non_terminal, aNonTerminalNode)
78
+ # Visit event. The visitor is about to visit the given non terminal node.
79
+ # @param aNonTerminalNode [Rley::PTre::NonTerminalNode] the node to visit.
80
+ def visit_nonterminal(_non_terminal_node)
81
+ # Loxxy interpreter encountered a CST node (Concrete Syntax Tree)
82
+ # that it cannot handle.
83
+ raise NotImplementedError, 'Loxxy cannot execute this code yet.'
83
84
  end
84
- broadcast(:after_non_terminal, aNonTerminalNode)
85
- end
86
- =end
87
85
 
88
86
  private
89
87
 
90
- def traverse_children(aParent)
91
- children = aParent.children
92
- broadcast(:before_children, aParent, children)
88
+ # Visit event. The visitor is about to visit the subnodes of a non
89
+ # terminal node.
90
+ # @param aParentNode [Ast::LocCompoundExpr] the parent node.
91
+ def traverse_subnodes(aParentNode)
92
+ subnodes = aParentNode.subnodes
93
+ broadcast(:before_subnodes, aParentNode, subnodes)
93
94
 
94
- # Let's proceed with the visit of children
95
- children.each { |a_child| a_child.accept(self) }
95
+ # Let's proceed with the visit of subnodes
96
+ subnodes.each { |a_node| a_node.accept(self) }
96
97
 
97
- broadcast(:after_children, aParent, children)
98
- end
99
-
100
- def traverse_car_cdr(aPair)
101
- if aPair.car
102
- broadcast(:before_car, aPair, aPair.car)
103
- aPair.car.accept(self)
104
- broadcast(:after_car, aPair, aPair.car)
105
- end
106
- if aPair.cdr
107
- broadcast(:before_cdr, aPair, aPair.cdr)
108
- aPair.cdr.accept(self)
109
- broadcast(:after_cdr, aPair, aPair.cdr)
110
- end
98
+ broadcast(:after_subnodes, aParentNode, subnodes)
111
99
  end
112
100
 
113
101
  # Send a notification to all subscribers.
@@ -117,7 +105,7 @@ module Loxxy
117
105
  subscribers.each do |subscr|
118
106
  next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
119
107
 
120
- subscr.send(msg, runtime, *args)
108
+ subscr.send(msg, *args)
121
109
  end
122
110
  end
123
111
  end # class
@@ -16,6 +16,12 @@ module Loxxy
16
16
  @operator = anOperator
17
17
  end
18
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_binary_expr(self)
23
+ end
24
+
19
25
  alias operands subnodes
20
26
  end # class
21
27
  end # module
@@ -5,7 +5,7 @@ require_relative 'lox_node'
5
5
  module Loxxy
6
6
  module Ast
7
7
  class LoxCompoundExpr < LoxNode
8
- # @return [Array<Ast::LoxNode>]
8
+ # @return [Array<Ast::LoxNode>] the children nodes (sub-expressions)
9
9
  attr_reader :subnodes
10
10
 
11
11
  # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
@@ -11,6 +11,11 @@ module Loxxy
11
11
  @position = aPosition
12
12
  end
13
13
 
14
+ # Notification that the parsing was successfully completed
15
+ def done!
16
+ # Default: do nothing ...
17
+ end
18
+
14
19
  # Abstract method.
15
20
  # Part of the 'visitee' role in Visitor design pattern.
16
21
  # @param _visitor [LoxxyTreeVisitor] the visitor
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_node'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxNoopExpr < LoxNode
8
+ # Abstract method.
9
+ # Part of the 'visitee' role in Visitor design pattern.
10
+ # @param _visitor [LoxxyTreeVisitor] the visitor
11
+ def accept(_visitor)
12
+ # Do nothing...
13
+ end
14
+ end # class
15
+ end # module
16
+ end # module
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxPrintStmt < LoxCompoundExpr
8
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
9
+ # @param anExpression [Ast::LoxNode] expression to output
10
+ def initialize(aPosition, anExpression)
11
+ super(aPosition, [anExpression])
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_print_stmt(self)
18
+ end
19
+ end # class
20
+ end # module
21
+ end # module
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load all the classes implementing AST nodes
4
+ require_relative '../ast/all_lox_nodes'
5
+
6
+ module Loxxy
7
+ module BackEnd
8
+ # An instance of this class executes the statements as when they
9
+ # occur during the abstract syntax tree walking.
10
+ # @note WIP: very crude implementation.
11
+ class Engine
12
+ # @return [Hash] A set of configuration options
13
+ attr_reader :config
14
+
15
+ # @return [Array] Data stack used for passing data between statements
16
+ attr_reader :stack
17
+
18
+ # @param theOptions [Hash]
19
+ def initialize(theOptions)
20
+ @config = theOptions
21
+ @ostream = config.include?(:ostream) ? config[:ostream] : $stdout
22
+ @stack = []
23
+ end
24
+
25
+ # Given an abstract syntax parse tree visitor, luanch the visit
26
+ # and execute the visit events in the output stream.
27
+ # @param aVisitor [AST::ASTVisitor]
28
+ # @return [Loxxy::Datatype::BuiltinDatatype]
29
+ def execute(aVisitor)
30
+ aVisitor.subscribe(self)
31
+ aVisitor.start
32
+ aVisitor.unsubscribe(self)
33
+ stack.empty? ? Datatype::Nil.instance : stack.pop
34
+ end
35
+
36
+ # Visit event handling
37
+
38
+ def after_print_stmt(_printStmt)
39
+ tos = stack.pop
40
+ @ostream.print tos.to_str
41
+ end
42
+
43
+ def after_binary_expr(aBinaryExpr)
44
+ op = aBinaryExpr.operator
45
+ operand2 = stack.pop
46
+ operand1 = stack.pop
47
+ implemented = %i[+ -].include?(op)
48
+ if implemented && operand1.respond_to?(op)
49
+ stack.push operand1.send(op, operand2)
50
+ else
51
+ msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
52
+ raise StandardError, msg1
53
+ end
54
+ end
55
+
56
+ # @param literalExpr [Ast::LoxLiteralExpr]
57
+ def before_literal_expr(literalExpr)
58
+ stack.push(literalExpr.literal)
59
+ end
60
+ end # class
61
+ end # module
62
+ end # module
@@ -8,6 +8,17 @@ module Loxxy
8
8
  # An instance acts merely as a wrapper around a Ruby representation
9
9
  # of the value.
10
10
  class Boolean < BuiltinDatatype
11
+ # Is this object representing a false value in Lox?
12
+ # @return [FalseClass, TrueClass]
13
+ def false?
14
+ false
15
+ end
16
+
17
+ # Is this object representing the true value in Lox?
18
+ # @return [FalseClass, TrueClass]
19
+ def true?
20
+ false
21
+ end
11
22
  end # class
12
23
  end # module
13
24
  end # module
@@ -14,6 +14,12 @@ module Loxxy
14
14
  @value = validated_value(aValue)
15
15
  end
16
16
 
17
+ # Method called from Lox to obtain the text representation of the boolean.
18
+ # @return [String]
19
+ def to_str
20
+ value.to_s # Default implementation...
21
+ end
22
+
17
23
  protected
18
24
 
19
25
  def validated_value(aValue)
@@ -13,6 +13,12 @@ module Loxxy
13
13
  def initialize
14
14
  super(false)
15
15
  end
16
+
17
+ # Is this object representing a false value in Lox?
18
+ # @return [TrueClass]
19
+ def false?
20
+ true
21
+ end
16
22
  end # class
17
23
 
18
24
  False.instance.freeze # Make the sole instance immutable
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'builtin_datatype'
3
+ require_relative 'false'
4
+ require_relative 'true'
4
5
 
5
6
  module Loxxy
6
7
  module Datatype
@@ -8,19 +9,41 @@ module Loxxy
8
9
  class LXString < BuiltinDatatype
9
10
  # Compare a Lox String with another Lox (or genuine Ruby) String
10
11
  # @param other [Datatype::LxString, String]
11
- # @return [Boolean]
12
+ # @return [Datatype::Boolean]
12
13
  def ==(other)
13
14
  case other
14
- when LXString
15
- value == other.value
16
- when String
17
- value == other
15
+ when LXString
16
+ (value == other.value) ? True.instance : False.instance
17
+ when String
18
+ (value == other) ? True.instance : False.instance
18
19
  else
19
20
  err_msg = "Cannot compare a #{self.class} with #{other.class}"
20
21
  raise StandardError, err_msg
21
22
  end
22
23
  end
23
24
 
25
+ # Perform the concatenation of two Lox stings or
26
+ # one Lox string and a Ruby String
27
+ # @param other [Loxxy::Datatype::LXString, String]
28
+ # @return [Loxxy::Datatype::LXString]
29
+ def +(other)
30
+ case other
31
+ when LXString
32
+ self.class.new(value + other.value)
33
+ when Numeric
34
+ self.class.new(value + other)
35
+ else
36
+ err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
37
+ raise TypeError, err_msg
38
+ end
39
+ end
40
+
41
+ # Method called from Lox to obtain the text representation of the object.
42
+ # @return [String]
43
+ def to_str
44
+ value
45
+ end
46
+
24
47
  protected
25
48
 
26
49
  def validated_value(aValue)
@@ -13,6 +13,12 @@ module Loxxy
13
13
  def initialize
14
14
  super(nil)
15
15
  end
16
+
17
+ # Method called from Lox to obtain the text representation of nil.
18
+ # @return [String]
19
+ def to_str
20
+ 'nil'
21
+ end
16
22
  end # class
17
23
 
18
24
  Nil.instance.freeze # Make the sole instance immutable
@@ -1,11 +1,59 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'builtin_datatype'
3
+ require_relative 'false'
4
+ require_relative 'true'
4
5
 
5
6
  module Loxxy
6
7
  module Datatype
7
8
  # Class for representing a Lox numeric value.
8
9
  class Number < BuiltinDatatype
10
+ # Compare a Lox Number with another Lox (or genuine Ruby) Number
11
+ # @param other [Datatype::Number, Numeric]
12
+ # @return [Datatype::Boolean]
13
+ def ==(other)
14
+ case other
15
+ when Number
16
+ (value == other.value) ? True.instance : False.instance
17
+ when Numeric
18
+ (value == other) ? True.instance : False.instance
19
+ else
20
+ err_msg = "Cannot compare a #{self.class} with #{other.class}"
21
+ raise StandardError, err_msg
22
+ end
23
+ end
24
+
25
+ # Perform the addition of two Lox numbers or
26
+ # one Lox number and a Ruby Numeric
27
+ # @param other [Loxxy::Datatype::Number, Numeric]
28
+ # @return [Loxxy::Datatype::Number]
29
+ def +(other)
30
+ case other
31
+ when Number
32
+ self.class.new(value + other.value)
33
+ when Numeric
34
+ self.class.new(value + other)
35
+ else
36
+ err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
37
+ raise TypeError, err_msg
38
+ end
39
+ end
40
+
41
+ # Perform the subtraction of two Lox numbers or
42
+ # one Lox number and a Ruby Numeric
43
+ # @param other [Loxxy::Datatype::Number, Numeric]
44
+ # @return [Loxxy::Datatype::Number]
45
+ def -(other)
46
+ case other
47
+ when Number
48
+ self.class.new(value - other.value)
49
+ when Numeric
50
+ self.class.new(value - other)
51
+ else
52
+ err_msg = "`-': #{other.class} can't be coerced into #{self.class} (TypeError)"
53
+ raise TypeError, err_msg
54
+ end
55
+ end
56
+
9
57
  protected
10
58
 
11
59
  def validated_value(aValue)
@@ -13,6 +13,12 @@ module Loxxy
13
13
  def initialize
14
14
  super(true)
15
15
  end
16
+
17
+ # Is this object representing the true value in Lox?
18
+ # @return [TrueClass]
19
+ def true?
20
+ true
21
+ end
16
22
  end # class
17
23
 
18
24
  True.instance.freeze # Make the sole instance immutable