loxxy 0.0.13 → 0.0.14

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e689f37b8b4893aff00f320707982cc1b09c17258abee50d0ae82f2d9d9d04db
4
- data.tar.gz: 57aa5e505c245cabd8f4e583a6300dbd5071b9ba580228103c4f9972c7679174
3
+ metadata.gz: 18b1d3e9f498ca8249775f55cc3950622d9f5a49cd67db912816cc60f15b3d16
4
+ data.tar.gz: 5766130089ebbe6d85a90a9f59ac1a505202c0e2646d4d7da93af025a80b0651
5
5
  SHA512:
6
- metadata.gz: 9336292545bd83f18d8f1be0a820717dcbe33cbb3b912697ea3e5245b726a5d79810ca6613c07017aef9d766670008bf49f59f6948922f18433db4a0df94f41e
7
- data.tar.gz: 149d09683e80f1d36a1939cfa13faefb1fc2bdae3b096bbde34d8824f67a305627018229e453b7b9655c78ae45b815726535a307125d238f6399df2c3d0f1aec
6
+ metadata.gz: 621832b48a5625081a0b82a8f8992d78709a1d4a053d8e4719140510242ce14d4418ca23c82d9f52186453c42d5d432203e7af496d7f92726f75622c743119e2
7
+ data.tar.gz: aa67901fae29e13d3154e274c7014fe2002fb7e054c62cf253e87e44ac421747db9f5f05ef1e108cd92c8c0124ceb93b31fa537de64f875607c096465f89aee4
data/README.md CHANGED
@@ -74,10 +74,11 @@ require 'loxxy'
74
74
 
75
75
  lox = Loxxy::Interpreter.new
76
76
 
77
- lox_program = '12.34; // A fractional number'
77
+ lox_program = '37 + 5; // THE answer'
78
78
  result = lox.evaluate(lox_program) # => Loxxy::Datatype::Number
79
79
 
80
- puts result.value # Output: 12.34
80
+ # `result` is a Ruby object, so let's use it...
81
+ puts result.value # Output: 42
81
82
  ```
82
83
 
83
84
  ## Example using RawParser class
@@ -130,11 +131,84 @@ program
130
131
  Although the interpreter should parse almost any valid Lox program,
131
132
  it currently can evaluate a tiny set of AST node (AST = Abstract Syntax Tree).
132
133
 
133
- Here are the language features supported by the interpreter:
134
- - Line comments,
135
- - All the Lox literals (booleans, numbers, strings and nil),
134
+ Here are the language features currently supported by the interpreter:
135
+
136
+ - [Comments](#comments)
137
+ - [Keywords](#keywords)
138
+ - [Operators and Special Chars](#operators-and-special-chars)
139
+ - [Datatypes](#datatypes)
140
+ - [Statements](#statements)
136
141
  - `print` statement.
137
142
 
143
+ ### Comments
144
+
145
+ Loxxy supports single line C-style comments.
146
+
147
+ ```javascript
148
+ // single line comment
149
+ ```
150
+
151
+ ### Keywords
152
+
153
+ The parser knows all the __Lox__ reserved keywords:
154
+ ```lang-none
155
+ and, class, else, false, fun, for, if, nil, or,
156
+ print, return, super, this, true, var, while
157
+ ```
158
+ Of these, the interpreter implements: `false`, `nil`, `true`
159
+
160
+ ### Operators and Special Chars
161
+ #### Operators
162
+ The parser recognizes all the __Lox__ operators, delimiters and separators:
163
+ - Arithmetic operators: `+`, `-`, `*`, `/`
164
+ - Comparison operators: `>`, `>=`, `<`, `<=`
165
+ - Equality operators: `==`, `!=`
166
+
167
+ Of these, the interpreter implements:
168
+ `+` (addition of two numbers or string concatenation)
169
+
170
+ #### Delimiters
171
+ The parser knows all the __Lox__ grouping delimiters:
172
+ (`, ), `{`, `}`
173
+
174
+ These aren't yet implemented in the interpreter.
175
+
176
+ The other characters that have a special meaning in __Lox__ are:
177
+ - `,` Used in parameter list
178
+ - `.` For the dot notation (i.e. calling a method)
179
+ - `;` The semicolon is used to terminates expressions
180
+ - `=` Assignment
181
+
182
+ The parser recognizes them all but the interpreter accepts the semicolons only.
183
+
184
+ ### Datatypes
185
+
186
+ Loxxy supports the following standard __Lox__ datatypes:
187
+ - `Boolean`: Can be `true` or `false`
188
+ - `Number`: Can be an integer or a floating-point numbers. For example: `123, 12.34, -45.67`
189
+ - `String`: Sequence of characters surrounded by `"`. For example: `"Hello!"`
190
+ - `Nil`: Used to define a null value, denoted by the `nil` keyword
191
+
192
+ All the Lox literals (booleans, numbers, strings and nil),
193
+
194
+ ## Statements
195
+ ### Implemented expressions
196
+ Loxxy implements expressions:
197
+ - Consisting of literals only; or,
198
+ - Addition of two numbers; or,
199
+ - Concatenation of two strings
200
+
201
+ ### Implemented statements
202
+ Loxxy implements the following statements:
203
+ - Expressions (see above sub-section)
204
+ - Print statement
205
+
206
+ ```javascript
207
+ // Print statement with nested concatenation
208
+ print "Hello" + ", " + "world!";
209
+ ```
210
+
211
+
138
212
 
139
213
  ## Installation
140
214
 
@@ -59,6 +59,14 @@ module Loxxy
59
59
  broadcast(:after_print_stmt, aPrintStmt)
60
60
  end
61
61
 
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)
68
+ end
69
+
62
70
  # Visit event. The visitor is visiting the
63
71
  # given terminal node containing a datatype object.
64
72
  # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
@@ -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
@@ -11,7 +11,6 @@ module Loxxy
11
11
  super(aPosition, [anExpression])
12
12
  end
13
13
 
14
- # Abstract method.
15
14
  # Part of the 'visitee' role in Visitor design pattern.
16
15
  # @param visitor [Ast::ASTVisitor] the visitor
17
16
  def accept(visitor)
@@ -40,6 +40,19 @@ module Loxxy
40
40
  @ostream.print tos.to_str
41
41
  end
42
42
 
43
+ def after_binary_expr(aBinaryExpr)
44
+ op = aBinaryExpr.operator
45
+ operand2 = stack.pop
46
+ operand1 = stack.pop
47
+ implemented = [:+].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
+
43
56
  # @param literalExpr [Ast::LoxLiteralExpr]
44
57
  def before_literal_expr(literalExpr)
45
58
  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
@@ -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,35 @@ 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
+
24
41
  # Method called from Lox to obtain the text representation of the object.
25
42
  # @return [String]
26
43
  def to_str
@@ -1,11 +1,43 @@
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 numeric [Loxxy::Datatype::Number, Numeric]
28
+ # @return [Loxxy::Datatype::Number]
29
+ def +(numeric)
30
+ case numeric
31
+ when Number
32
+ self.class.new(value + numeric.value)
33
+ when Numeric
34
+ self.class.new(value + numeric)
35
+ else
36
+ err_msg = "`+': #{numeric.class} can't be coerced into #{self.class} (TypeError)"
37
+ raise TypeError, err_msg
38
+ end
39
+ end
40
+
9
41
  protected
10
42
 
11
43
  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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.14'
5
5
  end
@@ -26,12 +26,30 @@ 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
35
53
  end
36
54
  end
37
55
  end # describe
@@ -22,6 +22,22 @@ module Loxxy
22
22
  end
23
23
 
24
24
  context 'Provided services:' do
25
+ it 'should compare with other Lox numbers' do
26
+ result = subject == Number.new(sample_value)
27
+ expect(result).to be_true
28
+
29
+ result = subject == Number.new(5)
30
+ expect(result).to be_false
31
+ end
32
+
33
+ it 'should compare with Ruby numbers' do
34
+ result = subject == sample_value
35
+ expect(result).to be_true
36
+
37
+ result = subject == 5
38
+ expect(result).to be_false
39
+ end
40
+
25
41
  it 'should give its display representation' do
26
42
  expect(subject.to_str).to eq(sample_value.to_s)
27
43
  end
@@ -33,6 +33,18 @@ module Loxxy
33
33
  expect(result).to be_kind_of(Loxxy::Datatype::True)
34
34
  end
35
35
 
36
+ it 'should evaluate arithmetic operation' do
37
+ result = subject.evaluate('123 + 456; // => 579')
38
+ expect(result).to be_kind_of(Loxxy::Datatype::Number)
39
+ expect(result == 579).to be_true
40
+ end
41
+
42
+ it 'should evaluate string concatenation' do
43
+ result = subject.evaluate('"str" + "ing"; // => "string"')
44
+ expect(result).to be_kind_of(Loxxy::Datatype::LXString)
45
+ expect(result == 'string').to be_true
46
+ end
47
+
36
48
  it 'should print the hello world message' do
37
49
  expect { subject.evaluate(hello_world) }.not_to raise_error
38
50
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef