loxxy 0.0.15 → 0.0.20

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.
@@ -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
@@ -22,7 +22,7 @@ module Loxxy
22
22
  @stack = []
23
23
  end
24
24
 
25
- # Given an abstract syntax parse tree visitor, luanch the visit
25
+ # Given an abstract syntax parse tree visitor, launch the visit
26
26
  # and execute the visit events in the output stream.
27
27
  # @param aVisitor [AST::ASTVisitor]
28
28
  # @return [Loxxy::Datatype::BuiltinDatatype]
@@ -33,19 +33,61 @@ module Loxxy
33
33
  stack.empty? ? Datatype::Nil.instance : stack.pop
34
34
  end
35
35
 
36
+ ##########################################################################
36
37
  # Visit event handling
38
+ ##########################################################################
39
+ def after_if_stmt(anIfStmt, aVisitor)
40
+ # Retrieve the result of the condition evaluation
41
+ condition = stack.pop
42
+ if condition.truthy?
43
+ result = anIfStmt.then_stmt.accept(aVisitor)
44
+ elsif anIfStmt.else_stmt
45
+ anIfStmt.else_stmt.accept(aVisitor)
46
+ end
47
+ end
37
48
 
38
49
  def after_print_stmt(_printStmt)
39
50
  tos = stack.pop
40
51
  @ostream.print tos.to_str
41
52
  end
42
53
 
54
+ def after_logical_expr(aLogicalExpr, visitor)
55
+ op = aLogicalExpr.operator
56
+ operand1 = stack.pop # only first operand was evaluated
57
+ result = nil
58
+ if ((op == :and) && operand1.falsey?) || ((op == :or) && operand1.truthy?)
59
+ result = operand1
60
+ else
61
+ raw_operand2 = aLogicalExpr.subnodes[1]
62
+ raw_operand2.accept(visitor) # Visit means operand2 is evaluated
63
+ operand2 = stack.pop
64
+ result = logical_2nd_arg(operand2)
65
+ end
66
+
67
+ stack.push result
68
+ end
69
+
70
+ def logical_2nd_arg(operand2)
71
+ case operand2
72
+ when false
73
+ False.instance # Convert to Lox equivalent
74
+ when nil
75
+ Nil.instance # Convert to Lox equivalent
76
+ when true
77
+ True.instance # Convert to Lox equivalent
78
+ when Proc
79
+ # Second operand wasn't yet evaluated...
80
+ operand2.call
81
+ else
82
+ operand2
83
+ end
84
+ end
85
+
43
86
  def after_binary_expr(aBinaryExpr)
44
87
  op = aBinaryExpr.operator
45
88
  operand2 = stack.pop
46
89
  operand1 = stack.pop
47
- implemented = %i[+ -].include?(op)
48
- if implemented && operand1.respond_to?(op)
90
+ if operand1.respond_to?(op)
49
91
  stack.push operand1.send(op, operand2)
50
92
  else
51
93
  msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
@@ -53,6 +95,21 @@ module Loxxy
53
95
  end
54
96
  end
55
97
 
98
+ def after_unary_expr(anUnaryExpr)
99
+ op = anUnaryExpr.operator
100
+ operand = stack.pop
101
+ if operand.respond_to?(op)
102
+ stack.push operand.send(op)
103
+ else
104
+ msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
105
+ raise StandardError, msg1
106
+ end
107
+ end
108
+
109
+ def after_grouping_expr(_groupingExpr)
110
+ # Do nothing: work was already done by visiting /evaluating the subexpression
111
+ end
112
+
56
113
  # @param literalExpr [Ast::LoxLiteralExpr]
57
114
  def before_literal_expr(literalExpr)
58
115
  stack.push(literalExpr.literal)
@@ -14,6 +14,48 @@ module Loxxy
14
14
  @value = validated_value(aValue)
15
15
  end
16
16
 
17
+ # Is the value considered falsey in Lox?
18
+ # Rule: false and nil are falsey and everything else is truthy.
19
+ # This test used in conditional statements (i.e. if, while)
20
+ def falsey?
21
+ false # Default implementation
22
+ end
23
+
24
+ # Is the value considered truthy in Lox?
25
+ # Rule: false and nil are falsey and everything else is truthy.
26
+ # This test used in conditional statements (i.e. if, while)
27
+ def truthy?
28
+ true # Default implementation
29
+ end
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
+
38
+ # Negation ('not')
39
+ # Returns a boolean with opposite truthiness value.
40
+ # @return [Datatype::Boolean]
41
+ def !
42
+ falsey? ? True.instance : False.instance
43
+ end
44
+
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)
57
+ end
58
+
17
59
  # Method called from Lox to obtain the text representation of the boolean.
18
60
  # @return [String]
19
61
  def to_str
@@ -25,6 +67,19 @@ module Loxxy
25
67
  def validated_value(aValue)
26
68
  aValue
27
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
28
83
  end # class
29
84
  end # module
30
85
  end # module
@@ -19,6 +19,28 @@ module Loxxy
19
19
  def false?
20
20
  true
21
21
  end
22
+
23
+ # Is the value considered falsey in Lox?
24
+ # Rule: false and nil are falsey and everything else is truthy.
25
+ # This test used in conditional statements (i.e. if, while)
26
+ def falsey?
27
+ true
28
+ end
29
+
30
+ # Is the value considered truthy in Lox?
31
+ # Rule: false and nil are falsey and everything else is truthy.
32
+ # This test used in conditional statements (i.e. if, while)
33
+ def truthy?
34
+ false
35
+ end
36
+
37
+ # Check for equality of a Lox False with another Lox object
38
+ # @param other [Datatype::BuiltinDatatype, FalseClass, Object]
39
+ # @return [Datatype::Boolean]
40
+ def ==(other)
41
+ falsey = other.kind_of?(False) || other.kind_of?(FalseClass)
42
+ falsey ? True.instance : False.instance
43
+ end
22
44
  end # class
23
45
 
24
46
  False.instance.freeze # Make the sole instance immutable
@@ -7,8 +7,8 @@ module Loxxy
7
7
  module Datatype
8
8
  # Class for representing a Lox string of characters value.
9
9
  class LXString < BuiltinDatatype
10
- # Compare a Lox String with another Lox (or genuine Ruby) String
11
- # @param other [Datatype::LxString, String]
10
+ # Check the equality of a Lox String with another Lox object.
11
+ # @param other [Datatype::LxString, String, Object]
12
12
  # @return [Datatype::Boolean]
13
13
  def ==(other)
14
14
  case other
@@ -17,8 +17,7 @@ module Loxxy
17
17
  when String
18
18
  (value == other) ? True.instance : False.instance
19
19
  else
20
- err_msg = "Cannot compare a #{self.class} with #{other.class}"
21
- raise StandardError, err_msg
20
+ False.instance
22
21
  end
23
22
  end
24
23
 
@@ -14,6 +14,28 @@ module Loxxy
14
14
  super(nil)
15
15
  end
16
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
+ # Is the value considered falsey in Lox?
26
+ # Rule: false and nil are falsey and everything else is truthy.
27
+ # This test used in conditional statements (i.e. if, while)
28
+ def falsey?
29
+ true
30
+ end
31
+
32
+ # Is the value considered truthy in Lox?
33
+ # Rule: false and nil are falsey and everything else is truthy.
34
+ # This test used in conditional statements (i.e. if, while)
35
+ def truthy?
36
+ false
37
+ end
38
+
17
39
  # Method called from Lox to obtain the text representation of nil.
18
40
  # @return [String]
19
41
  def to_str
@@ -7,21 +7,6 @@ module Loxxy
7
7
  module Datatype
8
8
  # Class for representing a Lox numeric value.
9
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
10
  # Perform the addition of two Lox numbers or
26
11
  # one Lox number and a Ruby Numeric
27
12
  # @param other [Loxxy::Datatype::Number, Numeric]
@@ -54,6 +39,104 @@ module Loxxy
54
39
  end
55
40
  end
56
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
+ # Unary minus (return value with changed sign)
75
+ # @return [Loxxy::Datatype::Number]
76
+ def -@
77
+ self.class.new(-value)
78
+ end
79
+
80
+ # Check the equality of a Lox number object with another object
81
+ # @param other [Datatype::BuiltinDatatype, Numeric, Object]
82
+ # @return [Datatype::Boolean]
83
+ def ==(other)
84
+ case other
85
+ when Number
86
+ (value == other.value) ? True.instance : False.instance
87
+ when Numeric
88
+ (value == other) ? True.instance : False.instance
89
+ else
90
+ False.instance
91
+ end
92
+ end
93
+
94
+ # Check whether this Lox number has a greater value than given argument.
95
+ # @param other [Datatype::Number, Numeric]
96
+ # @return [Datatype::Boolean]
97
+ def >(other)
98
+ case other
99
+ when Number
100
+ (value > other.value) ? True.instance : False.instance
101
+ when Numeric
102
+ (value > other) ? True.instance : False.instance
103
+ else
104
+ msg = "'>': Operands must be numbers."
105
+ raise StandardError, msg
106
+ end
107
+ end
108
+
109
+ # Check whether this Lox number has a greater or equal value
110
+ # than given argument.
111
+ # @param other [Datatype::Number, Numeric]
112
+ # @return [Datatype::Boolean]
113
+ def >=(other)
114
+ case other
115
+ when Number
116
+ (value >= other.value) ? True.instance : False.instance
117
+ when Numeric
118
+ (value >= other) ? True.instance : False.instance
119
+ else
120
+ msg = "'>': Operands must be numbers."
121
+ raise StandardError, msg
122
+ end
123
+ end
124
+
125
+ # Check whether this Lox number has a lesser value than given argument.
126
+ # @param other [Datatype::Number, Numeric]
127
+ # @return [Datatype::Boolean]
128
+ def <(other)
129
+ !(self >= other)
130
+ end
131
+
132
+ # Check whether this Lox number has a lesser or equal value
133
+ # than given argument.
134
+ # @param other [Datatype::Number, Numeric]
135
+ # @return [Datatype::Boolean]
136
+ def <=(other)
137
+ !(self > other)
138
+ end
139
+
57
140
  protected
58
141
 
59
142
  def validated_value(aValue)