loxxy 0.0.12 → 0.0.17

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.
@@ -22,9 +22,10 @@ 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
+ # @return [Loxxy::Datatype::BuiltinDatatype]
28
29
  def execute(aVisitor)
29
30
  aVisitor.subscribe(self)
30
31
  aVisitor.start
@@ -39,6 +40,29 @@ module Loxxy
39
40
  @ostream.print tos.to_str
40
41
  end
41
42
 
43
+ def after_binary_expr(aBinaryExpr)
44
+ op = aBinaryExpr.operator
45
+ operand2 = stack.pop
46
+ operand1 = stack.pop
47
+ if operand1.respond_to?(op)
48
+ stack.push operand1.send(op, operand2)
49
+ else
50
+ msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
51
+ raise StandardError, msg1
52
+ end
53
+ end
54
+
55
+ def after_unary_expr(anUnaryExpr)
56
+ op = anUnaryExpr.operator
57
+ operand = stack.pop
58
+ if operand.respond_to?(op)
59
+ stack.push operand.send(op)
60
+ else
61
+ msg1 = "`#{op}': Unimplemented operator for a #{operand1.class}."
62
+ raise StandardError, msg1
63
+ end
64
+ end
65
+
42
66
  # @param literalExpr [Ast::LoxLiteralExpr]
43
67
  def before_literal_expr(literalExpr)
44
68
  stack.push(literalExpr.literal)
@@ -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,34 @@ 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
+ # Negation ('not')
32
+ # Returns a boolean with opposite truthiness value.
33
+ # @return [Datatype::Boolean]
34
+ def !
35
+ falsey? ? True.instance : False.instance
36
+ end
37
+
38
+ # Check for inequality of this object with another Lox object
39
+ # @param other [Datatype::BuiltinDatatype, Object]
40
+ # @return [Datatype::Boolean]
41
+ def !=(other)
42
+ !(self == other)
43
+ end
44
+
17
45
  # Method called from Lox to obtain the text representation of the boolean.
18
46
  # @return [String]
19
47
  def to_str
@@ -13,6 +13,34 @@ 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
+ # 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
16
44
  end # class
17
45
 
18
46
  False.instance.freeze # Make the sole instance immutable
@@ -1,23 +1,39 @@
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
+ end
22
+ end
23
+
24
+ # Perform the concatenation of two Lox stings or
25
+ # one Lox string and a Ruby String
26
+ # @param other [Loxxy::Datatype::LXString, String]
27
+ # @return [Loxxy::Datatype::LXString]
28
+ def +(other)
29
+ case other
30
+ when LXString
31
+ self.class.new(value + other.value)
32
+ when Numeric
33
+ self.class.new(value + other)
34
+ else
35
+ err_msg = "`+': #{other.class} can't be coerced into #{self.class} (TypeError)"
36
+ raise TypeError, err_msg
21
37
  end
22
38
  end
23
39
 
@@ -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
@@ -1,11 +1,142 @@
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
+ # 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
+
9
140
  protected
10
141
 
11
142
  def validated_value(aValue)
@@ -13,6 +13,20 @@ 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
22
+
23
+ # Check for equality of a Lox True with another Lox object / Ruby true
24
+ # @param other [Datatype::True, TrueClass, Object]
25
+ # @return [Datatype::Boolean]
26
+ def ==(other)
27
+ thruthy = other.kind_of?(True) || other.kind_of?(TrueClass)
28
+ thruthy ? True.instance : False.instance
29
+ end
16
30
  end # class
17
31
 
18
32
  True.instance.freeze # Make the sole instance immutable
@@ -58,7 +58,7 @@ module Loxxy
58
58
  rule('statement' => 'whileStmt')
59
59
  rule('statement' => 'block')
60
60
 
61
- rule('exprStmt' => 'expression SEMICOLON')
61
+ rule('exprStmt' => 'expression SEMICOLON').as 'exprStmt'
62
62
 
63
63
  rule('forStmt' => 'FOR LEFT_PAREN forControl RIGHT_PAREN statement')
64
64
  rule('forControl' => 'forInitialization forTest forUpdate')
@@ -121,7 +121,7 @@ module Loxxy
121
121
  rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
122
122
  rule('multOp' => 'SLASH')
123
123
  rule('multOp' => 'STAR')
124
- rule('unary' => 'unaryOp unary')
124
+ rule('unary' => 'unaryOp unary').as 'unary_expr'
125
125
  rule('unary' => 'call')
126
126
  rule('unaryOp' => 'BANG')
127
127
  rule('unaryOp' => 'MINUS')
@@ -19,8 +19,10 @@ module Loxxy
19
19
  @config = theOptions
20
20
  end
21
21
 
22
- # Evaluate the given Lox program
22
+ # Evaluate the given Lox program.
23
+ # Return the result of the last executed expression (if any)
23
24
  # @param lox_input [String] Lox program to evaluate
25
+ # @return [Loxxy::Datatype::BuiltinDatatype]
24
26
  def evaluate(lox_input)
25
27
  # Front-end scans, parses the input and blurps an AST...
26
28
  parser = FrontEnd::Parser.new
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.12'
4
+ VERSION = '0.0.17'
5
5
  end
@@ -26,12 +26,35 @@ module Loxxy
26
26
  expect(subject.to_str).to eq(sample_text)
27
27
  end
28
28
 
29
- it 'compares with another string' do
30
- expect(subject).to eq(sample_text)
31
- expect(subject).to eq(LXString.new(sample_text.dup))
29
+ it 'compares with another Lox string' do
30
+ result = subject == LXString.new(sample_text.dup)
31
+ expect(result).to be_true
32
32
 
33
- expect(subject).not_to eq('')
34
- expect(subject).not_to eq('other-text')
33
+ result = subject == LXString.new('other-text')
34
+ expect(result).to be_false
35
+
36
+ result = subject == LXString.new('')
37
+ expect(result).to be_false
38
+
39
+ # Two empty strings are equal
40
+ result = LXString.new('') == LXString.new('')
41
+ expect(result).to be_true
42
+ end
43
+
44
+ it 'compares with a Ruby string' do
45
+ result = subject == sample_text.dup
46
+ expect(result).to be_true
47
+
48
+ result = subject == 'other-text'
49
+ expect(result).to be_false
50
+
51
+ result = subject == ''
52
+ expect(result).to be_false
53
+ end
54
+
55
+ it 'performs the concatenation with another string' do
56
+ concatenation = LXString.new('str') + LXString.new('ing')
57
+ expect(concatenation == 'string').to be_true
35
58
  end
36
59
  end
37
60
  end # describe