loxxy 0.0.11 → 0.0.16

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,28 @@ 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
22
+
23
+ # Check for equality of a Lox False with another Lox object
24
+ # @param other [Datatype::BuiltinDatatype, FalseClass, Object]
25
+ # @return [Datatype::Boolean]
26
+ def ==(other)
27
+ falsey = other.kind_of?(False) || other.kind_of?(FalseClass)
28
+ falsey ? True.instance : False.instance
29
+ end
30
+
31
+ # Check for inequality of a Lox False with another Lox object
32
+ # @param other [Datatype::BuiltinDatatype, FalseClass, Object]
33
+ # @return [Datatype::Boolean]
34
+ def !=(other)
35
+ falsey = other.kind_of?(False) || other.kind_of?(FalseClass)
36
+ falsey ? False.instance : True.instance
37
+ end
16
38
  end # class
17
39
 
18
40
  False.instance.freeze # Make the sole instance immutable
@@ -1,26 +1,62 @@
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 string of characters value.
8
9
  class LXString < BuiltinDatatype
9
- # Compare a Lox String with another Lox (or genuine Ruby) String
10
- # @param other [Datatype::LxString, String]
11
- # @return [Boolean]
10
+ # Check the equality of a Lox String with another Lox object.
11
+ # @param other [Datatype::LxString, String, Object]
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
- err_msg = "Cannot compare a #{self.class} with #{other.class}"
20
- raise StandardError, err_msg
20
+ False.instance
21
21
  end
22
22
  end
23
23
 
24
+ # Check the inequality of a Lox String with another Lox object.
25
+ # @param other [Datatype::LxString, String, Object]
26
+ # @return [Datatype::Boolean]
27
+ def !=(other)
28
+ case other
29
+ when LXString
30
+ (value != other.value) ? True.instance : False.instance
31
+ when String
32
+ (value != other) ? True.instance : False.instance
33
+ else
34
+ True.instance
35
+ end
36
+ end
37
+
38
+ # Perform the concatenation of two Lox stings or
39
+ # one Lox string and a Ruby String
40
+ # @param other [Loxxy::Datatype::LXString, String]
41
+ # @return [Loxxy::Datatype::LXString]
42
+ def +(other)
43
+ case other
44
+ when LXString
45
+ self.class.new(value + other.value)
46
+ when Numeric
47
+ self.class.new(value + other)
48
+ else
49
+ err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
50
+ raise TypeError, err_msg
51
+ end
52
+ end
53
+
54
+ # Method called from Lox to obtain the text representation of the object.
55
+ # @return [String]
56
+ def to_str
57
+ value
58
+ end
59
+
24
60
  protected
25
61
 
26
62
  def validated_value(aValue)
@@ -13,6 +13,28 @@ module Loxxy
13
13
  def initialize
14
14
  super(nil)
15
15
  end
16
+
17
+ # Check the equality with another object.
18
+ # @param other [Datatype::BuiltinDatatype, NilClass, Object]
19
+ # @return [Datatype::Boolean]
20
+ def ==(other)
21
+ is_nil = other.kind_of?(Nil) || other.kind_of?(NilClass)
22
+ is_nil ? True.instance : False.instance
23
+ end
24
+
25
+ # Check the inequality with another object.
26
+ # @param other [Datatype::BuiltinDatatype, NilClass, Object]
27
+ # @return [Datatype::Boolean]
28
+ def !=(other)
29
+ is_nil = other.kind_of?(Nil) || other.kind_of?(NilClass)
30
+ is_nil ? False.instance : True.instance
31
+ end
32
+
33
+ # Method called from Lox to obtain the text representation of nil.
34
+ # @return [String]
35
+ def to_str
36
+ 'nil'
37
+ end
16
38
  end # class
17
39
 
18
40
  Nil.instance.freeze # Make the sole instance immutable
@@ -1,11 +1,104 @@
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
+ # Perform the addition of two Lox numbers or
11
+ # one Lox number and a Ruby Numeric
12
+ # @param other [Loxxy::Datatype::Number, Numeric]
13
+ # @return [Loxxy::Datatype::Number]
14
+ def +(other)
15
+ case other
16
+ when Number
17
+ self.class.new(value + other.value)
18
+ when Numeric
19
+ self.class.new(value + other)
20
+ else
21
+ err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
22
+ raise TypeError, err_msg
23
+ end
24
+ end
25
+
26
+ # Perform the subtraction of two Lox numbers or
27
+ # one Lox number and a Ruby Numeric
28
+ # @param other [Loxxy::Datatype::Number, Numeric]
29
+ # @return [Loxxy::Datatype::Number]
30
+ def -(other)
31
+ case other
32
+ when Number
33
+ self.class.new(value - other.value)
34
+ when Numeric
35
+ self.class.new(value - other)
36
+ else
37
+ err_msg = "`-': #{other.class} can't be coerced into #{self.class} (TypeError)"
38
+ raise TypeError, err_msg
39
+ end
40
+ end
41
+
42
+ # Perform the multiplication of two Lox numbers or
43
+ # one Lox number and a Ruby Numeric
44
+ # @param other [Loxxy::Datatype::Number, Numeric]
45
+ # @return [Loxxy::Datatype::Number]
46
+ def *(other)
47
+ case other
48
+ when Number
49
+ self.class.new(value * other.value)
50
+ when Numeric
51
+ self.class.new(value * other)
52
+ else
53
+ err_msg = "'*': Operands must be numbers."
54
+ raise TypeError, err_msg
55
+ end
56
+ end
57
+
58
+ # Perform the division of two Lox numbers or
59
+ # one Lox number and a Ruby Numeric
60
+ # @param other [Loxxy::Datatype::Number, Numeric]
61
+ # @return [Loxxy::Datatype::Number]
62
+ def /(other)
63
+ case other
64
+ when Number
65
+ self.class.new(value / other.value)
66
+ when Numeric
67
+ self.class.new(value / other)
68
+ else
69
+ err_msg = "'/': Operands must be numbers."
70
+ raise TypeError, err_msg
71
+ end
72
+ end
73
+
74
+ # Check the equality of a Lox number object with another object
75
+ # @param other [Datatype::BuiltinDatatype, Numeric, Object]
76
+ # @return [Datatype::Boolean]
77
+ def ==(other)
78
+ case other
79
+ when Number
80
+ (value == other.value) ? True.instance : False.instance
81
+ when Numeric
82
+ (value == other) ? True.instance : False.instance
83
+ else
84
+ False.instance
85
+ end
86
+ end
87
+
88
+ # Check the inequality of a Lox number object with another object
89
+ # @param other [Datatype::BuiltinDatatype, Numeric, Object]
90
+ # @return [Datatype::Boolean]
91
+ def !=(other)
92
+ case other
93
+ when Number
94
+ (value != other.value) ? True.instance : False.instance
95
+ when Numeric
96
+ (value != other) ? True.instance : False.instance
97
+ else
98
+ True.instance
99
+ end
100
+ end
101
+
9
102
  protected
10
103
 
11
104
  def validated_value(aValue)