loxxy 0.0.12 → 0.0.13

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: 7274fd2d139354b984962f9317cae890344e528540f2f157772e7395cdfd7d33
4
- data.tar.gz: c1b38a4dfcb441c00604926cbaf397ab555624270ca647448d9cc4d4f463f500
3
+ metadata.gz: e689f37b8b4893aff00f320707982cc1b09c17258abee50d0ae82f2d9d9d04db
4
+ data.tar.gz: 57aa5e505c245cabd8f4e583a6300dbd5071b9ba580228103c4f9972c7679174
5
5
  SHA512:
6
- metadata.gz: 27d5de6532213d1a995f86da84ccbd41a11b5f85c305e57a62f64637ad17852e622685e4261b1c62deb31c924bb8f8c29c4edd97e9776fabf7e0d06e5c302534
7
- data.tar.gz: dab4a9e391c120c159a63e316f90065faf1415b54c9aa4c6f349f6be8e95de1289a2478f395e6b9e19b2e6f09482ae4db49a70e0d5d857eb5475cecad0590c69
6
+ metadata.gz: 9336292545bd83f18d8f1be0a820717dcbe33cbb3b912697ea3e5245b726a5d79810ca6613c07017aef9d766670008bf49f59f6948922f18433db4a0df94f41e
7
+ data.tar.gz: 149d09683e80f1d36a1939cfa13faefb1fc2bdae3b096bbde34d8824f67a305627018229e453b7b9655c78ae45b815726535a307125d238f6399df2c3d0f1aec
@@ -1,3 +1,15 @@
1
+ ## [0.0.13] - 2021-01-10
2
+ - The interpreter can evaluate directly simple literals.
3
+
4
+ ## Changed
5
+ - Class `AST::ASTBuilder` added `reduce_exprStmt` to support the evaluation of literals.
6
+ - File `README.md` added one more example.
7
+ - File `parser_spec.rb` Updated the tests to reflect the change in the AST.
8
+ - File `interpreter_spec.rb` Added a test for literal expression.
9
+
10
+ ## Fixed
11
+ - File `loxxy.rb`: shorthand method `lox_true` referenced the ... false object (oops).
12
+
1
13
  ## [0.0.12] - 2021-01-09
2
14
  - Initial interpreter capable of evaluating a tiny subset of Lox language.
3
15
 
data/README.md CHANGED
@@ -6,17 +6,52 @@
6
6
  A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
7
7
  a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
8
8
 
9
- ## Purpose of this project:
9
+ ### Purpose of this project:
10
10
  - To deliver an open source example of a programming language fully implemented in Ruby
11
- (from the scanner, parser, code generation).
11
+ (from the scanner, parser, an interpreter).
12
12
  - The implementation should be mature enough to run [LoxLox](https://github.com/benhoyt/loxlox),
13
13
  a Lox interpreter written in Lox.
14
14
 
15
- ## Current status
16
- The project is still in inception and the interpreter is being implemented...
17
- Currently it can execute a very limited subset of __Lox__ language.
15
+ ### Current status
16
+ The project is still in inception and the interpreter is being implemented...
17
+ Currently it can execute a tiny subset of __Lox__ language.
18
18
 
19
- The __loxxy__ gem also a parser class `RawPaser` that can, in principle, any valid Lox input.
19
+ The __loxxy__ gem also a parser class `RawPaser` that can parse, in principle, any valid Lox input.
20
+
21
+ ## What's the fuss about Lox?
22
+ ... Nothing...
23
+ Bob Nystrom designed a language __simple__ enough so that he could present
24
+ two implementations (an interpreter, then a compiler) in one single book.
25
+
26
+ Although __Lox__ is fairly simple, it is far from a toy language:
27
+ - Dynamically typed,
28
+ - Provides datatypes such as booleans, number, strings,
29
+ - Supports arithmetic operations (+, -, *, / ) and comparison ( >, >= , <, <=)
30
+ - Implements equality operators (==, !=) and the logical connectors `and` and `or`.
31
+ - Control flow statements `if`, `for` and `while`
32
+ - Functions and closures
33
+ - Object-orientation (classes, methods, inheritance).
34
+
35
+ In other words, __Lox__ contains interesting features expected from most general-purpose
36
+ languages.
37
+
38
+ ### What's missing in Lox?
39
+ __Lox__ was constrained by design and therefore was not aimed to be a language used in real-world applications.
40
+ Here are some missing parts to make it a _practical_ language:
41
+ - Collections (arrays, maps, ...)
42
+ - Modules (importing stuff from other packages/files)
43
+ - Error handling (e.g. exceptions)
44
+ - Support for concurrency (e.g. threads, coroutines)
45
+
46
+ Also a decent standard library for IO, networking,... is lacking.
47
+
48
+ For sure, the language has shortcomings but on the other hand, it exhibits the essential features
49
+ to cover in an introduction to language implementation.
50
+
51
+ That's already fun... and if all this gives you the inspiration for creating your own
52
+ language, that might be even funnier...
53
+
54
+ Last point: what's makes __Lox__ interesting is the fact that there are implementations in many [languages](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations)
20
55
 
21
56
  ## Hello world example
22
57
  ```ruby
@@ -28,7 +63,21 @@ lox_program = <<LOX_END
28
63
  LOX_END
29
64
 
30
65
  lox = Loxxy::Interpreter.new
31
- lox.evaluate(lox_program) # => Hello, world!
66
+ lox.evaluate(lox_program) # Output: Hello, world!
67
+ ```
68
+
69
+ ## Retrieving the result from a Lox program
70
+ The __Loxxy__ interpreter returns the value of the last evaluated expression.
71
+
72
+ ```ruby
73
+ require 'loxxy'
74
+
75
+ lox = Loxxy::Interpreter.new
76
+
77
+ lox_program = '12.34; // A fractional number'
78
+ result = lox.evaluate(lox_program) # => Loxxy::Datatype::Number
79
+
80
+ puts result.value # Output: 12.34
32
81
  ```
33
82
 
34
83
  ## Example using RawParser class
@@ -108,7 +157,6 @@ Or install it yourself as:
108
157
  TODO: Write usage instructions here
109
158
 
110
159
  ## Other Lox implementations in Ruby
111
- An impressive list of Lox implementations can be found [here](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations)
112
160
 
113
161
  For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
114
162
  There are other Ruby-based projects as well:
@@ -7,13 +7,13 @@ require_relative 'loxxy/front_end/raw_parser'
7
7
  # Namespace for all classes and constants of __loxxy__ gem.
8
8
  module Loxxy
9
9
  class Error < StandardError; end
10
-
10
+
11
11
  # Shorthand method. Returns the sole object that represents
12
12
  # a Lox false literal.
13
13
  # @return [Loxxy::Datatype::False]
14
14
  def self.lox_false
15
15
  Datatype::False.instance
16
- end
16
+ end
17
17
 
18
18
  # Shorthand method. Returns the sole object that represents
19
19
  # a Lox nil literal.
@@ -21,11 +21,11 @@ module Loxxy
21
21
  def self.lox_nil
22
22
  Datatype::Nil.instance
23
23
  end
24
-
24
+
25
25
  # Shorthand method. Returns the sole object that represents
26
26
  # a Lox true literal.
27
27
  # @return [Loxxy::Datatype::True]
28
28
  def self.lox_true
29
- Datatype::False.instance
30
- end
29
+ Datatype::True.instance
30
+ end
31
31
  end
@@ -124,13 +124,18 @@ module Loxxy
124
124
  #####################################
125
125
 
126
126
  # rule('program' => 'EOF').as 'null_program'
127
- def reduce_null_program(_production, range, _tokens, _theChildren)
127
+ def reduce_null_program(_production, _range, _tokens, _theChildren)
128
128
  Ast::LoxNoopExpr.new(tokens[0].position)
129
129
  end
130
130
 
131
131
  # rule('program' => 'declaration_plus EOF').as ''
132
132
  def reduce_lox_program(_production, range, tokens, theChildren)
133
- return_first_child(range, tokens, theChildren)
133
+ return_first_child(range, tokens, theChildren) # Discard the semicolon
134
+ end
135
+
136
+ # rule('exprStmt' => 'expression SEMICOLON')
137
+ def reduce_exprStmt(_production, range, tokens, theChildren)
138
+ return_first_child(range, tokens, theChildren) # Discard the semicolon
134
139
  end
135
140
 
136
141
  # rule('printStmt' => 'PRINT expression SEMICOLON')
@@ -25,6 +25,7 @@ module Loxxy
25
25
  # Given an abstract syntax parse tree visitor, luanch 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
@@ -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')
@@ -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.13'
5
5
  end
@@ -64,7 +64,7 @@ module Loxxy
64
64
  it 'should parse a false literal' do
65
65
  input = 'false;'
66
66
  ptree = subject.parse(input)
67
- leaf = ptree.root.subnodes[0]
67
+ leaf = ptree.root
68
68
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
69
69
  expect(leaf.literal).to be_equal(Datatype::False.instance)
70
70
  end
@@ -72,7 +72,7 @@ module Loxxy
72
72
  it 'should parse a true literal' do
73
73
  input = 'true;'
74
74
  ptree = subject.parse(input)
75
- leaf = ptree.root.subnodes[0]
75
+ leaf = ptree.root
76
76
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
77
77
  expect(leaf.literal).to be_equal(Datatype::True.instance)
78
78
  end
@@ -81,7 +81,7 @@ module Loxxy
81
81
  inputs = %w[1234; 12.34;]
82
82
  inputs.each do |source|
83
83
  ptree = subject.parse(source)
84
- leaf = ptree.root.subnodes[0]
84
+ leaf = ptree.root
85
85
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
86
86
  expect(leaf.literal).to be_kind_of(Datatype::Number)
87
87
  expect(leaf.literal.value).to eq(source.to_f)
@@ -96,7 +96,7 @@ module Loxxy
96
96
  ]
97
97
  inputs.each do |source|
98
98
  ptree = subject.parse(source)
99
- leaf = ptree.root.subnodes[0]
99
+ leaf = ptree.root
100
100
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
101
101
  expect(leaf.literal).to be_kind_of(Datatype::LXString)
102
102
  expect(leaf.literal.value).to eq(source.gsub(/(^")|(";$)/, ''))
@@ -106,7 +106,7 @@ module Loxxy
106
106
  it 'should parse a nil literal' do
107
107
  input = 'nil;'
108
108
  ptree = subject.parse(input)
109
- leaf = ptree.root.subnodes[0]
109
+ leaf = ptree.root
110
110
  expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
111
111
  expect(leaf.literal).to be_equal(Datatype::Nil.instance)
112
112
  end
@@ -131,10 +131,7 @@ LOX_END
131
131
  it 'should parse the addition of two number literals' do
132
132
  input = '123 + 456;'
133
133
  ptree = subject.parse(input)
134
- parent = ptree.root
135
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
136
- expect(parent.symbol.name).to eq('exprStmt')
137
- expr = parent.subnodes[0]
134
+ expr = ptree.root
138
135
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
139
136
  expect(expr.operator).to eq(:+)
140
137
  expect(expr.operands[0].literal.value).to eq(123)
@@ -144,10 +141,7 @@ LOX_END
144
141
  it 'should parse the subtraction of two number literals' do
145
142
  input = '4 - 3;'
146
143
  ptree = subject.parse(input)
147
- parent = ptree.root
148
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
149
- expect(parent.symbol.name).to eq('exprStmt')
150
- expr = parent.subnodes[0]
144
+ expr = ptree.root
151
145
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
152
146
  expect(expr.operator).to eq(:-)
153
147
  expect(expr.operands[0].literal.value).to eq(4)
@@ -157,10 +151,7 @@ LOX_END
157
151
  it 'should parse multiple additive operations' do
158
152
  input = '5 + 2 - 3;'
159
153
  ptree = subject.parse(input)
160
- parent = ptree.root
161
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
162
- expect(parent.symbol.name).to eq('exprStmt')
163
- expr = parent.subnodes[0]
154
+ expr = ptree.root
164
155
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
165
156
  expect(expr.operator).to eq(:-)
166
157
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -173,10 +164,7 @@ LOX_END
173
164
  it 'should parse the division of two number literals' do
174
165
  input = '8 / 2;'
175
166
  ptree = subject.parse(input)
176
- parent = ptree.root
177
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
178
- expect(parent.symbol.name).to eq('exprStmt')
179
- expr = parent.subnodes[0]
167
+ expr = ptree.root
180
168
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
181
169
  expect(expr.operator).to eq(:/)
182
170
  expect(expr.operands[0].literal.value).to eq(8)
@@ -186,10 +174,7 @@ LOX_END
186
174
  it 'should parse the product of two number literals' do
187
175
  input = '12.34 * 0.3;'
188
176
  ptree = subject.parse(input)
189
- parent = ptree.root
190
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
191
- expect(parent.symbol.name).to eq('exprStmt')
192
- expr = parent.subnodes[0]
177
+ expr = ptree.root
193
178
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
194
179
  expect(expr.operator).to eq(:*)
195
180
  expect(expr.operands[0].literal.value).to eq(12.34)
@@ -199,10 +184,7 @@ LOX_END
199
184
  it 'should parse multiple multiplicative operations' do
200
185
  input = '5 * 2 / 3;'
201
186
  ptree = subject.parse(input)
202
- parent = ptree.root
203
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
204
- expect(parent.symbol.name).to eq('exprStmt')
205
- expr = parent.subnodes[0]
187
+ expr = ptree.root
206
188
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
207
189
  expect(expr.operator).to eq(:/)
208
190
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -215,10 +197,7 @@ LOX_END
215
197
  it 'should parse combination of terms and factors' do
216
198
  input = '5 + 2 / 3;'
217
199
  ptree = subject.parse(input)
218
- parent = ptree.root
219
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
220
- expect(parent.symbol.name).to eq('exprStmt')
221
- expr = parent.subnodes[0]
200
+ expr = ptree.root
222
201
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
223
202
  expect(expr.operator).to eq(:+)
224
203
  expect(expr.operands[0].literal.value).to eq(5)
@@ -233,10 +212,7 @@ LOX_END
233
212
  it 'should parse the concatenation of two string literals' do
234
213
  input = '"Lo" + "ve";'
235
214
  ptree = subject.parse(input)
236
- parent = ptree.root
237
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
238
- expect(parent.symbol.name).to eq('exprStmt')
239
- expr = parent.subnodes[0]
215
+ expr = ptree.root
240
216
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
241
217
  expect(expr.operator).to eq(:+)
242
218
  expect(expr.operands[0].literal.value).to eq('Lo')
@@ -249,10 +225,7 @@ LOX_END
249
225
  %w[> >= < <=].each do |predicate|
250
226
  input = "3 #{predicate} 2;"
251
227
  ptree = subject.parse(input)
252
- parent = ptree.root
253
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
254
- expect(parent.symbol.name).to eq('exprStmt')
255
- expr = parent.subnodes[0]
228
+ expr = ptree.root
256
229
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
257
230
  expect(expr.operator).to eq(predicate.to_sym)
258
231
  expect(expr.operands[0].literal.value).to eq(3)
@@ -266,10 +239,7 @@ LOX_END
266
239
  %w[!= ==].each do |predicate|
267
240
  input = "3 #{predicate} 2;"
268
241
  ptree = subject.parse(input)
269
- parent = ptree.root
270
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
271
- expect(parent.symbol.name).to eq('exprStmt')
272
- expr = parent.subnodes[0]
242
+ expr = ptree.root
273
243
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
274
244
  expect(expr.operator).to eq(predicate.to_sym)
275
245
  expect(expr.operands[0].literal.value).to eq(3)
@@ -280,10 +250,7 @@ LOX_END
280
250
  it 'should parse combination of equality expressions' do
281
251
  input = '5 != 2 == false; // A bit contrived example'
282
252
  ptree = subject.parse(input)
283
- parent = ptree.root
284
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
285
- expect(parent.symbol.name).to eq('exprStmt')
286
- expr = parent.subnodes[0]
253
+ expr = ptree.root
287
254
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
288
255
  expect(expr.operator).to eq(:==)
289
256
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -299,10 +266,7 @@ LOX_END
299
266
  %w[or and].each do |connector|
300
267
  input = "5 > 2 #{connector} 3 <= 4;"
301
268
  ptree = subject.parse(input)
302
- parent = ptree.root
303
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
304
- expect(parent.symbol.name).to eq('exprStmt')
305
- expr = parent.subnodes[0]
269
+ expr = ptree.root
306
270
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
307
271
  expect(expr.operator).to eq(connector.to_sym)
308
272
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -319,10 +283,7 @@ LOX_END
319
283
  it 'should parse a combinations of logical expressions' do
320
284
  input = '4 > 3 and 1 < 2 or 4 >= 5;'
321
285
  ptree = subject.parse(input)
322
- parent = ptree.root
323
- expect(parent).to be_kind_of(Rley::PTree::NonTerminalNode)
324
- expect(parent.symbol.name).to eq('exprStmt')
325
- expr = parent.subnodes[0]
286
+ expr = ptree.root
326
287
  expect(expr).to be_kind_of(Ast::LoxBinaryExpr)
327
288
  expect(expr.operator).to eq(:or) # or has lower precedence than and
328
289
  expect(expr.operands[0]).to be_kind_of(Ast::LoxBinaryExpr)
@@ -28,6 +28,11 @@ module Loxxy
28
28
  context 'Evaluating Lox code:' do
29
29
  let(:hello_world) { 'print "Hello, world!";' }
30
30
 
31
+ it 'should evaluate core data types' do
32
+ result = subject.evaluate('true; // Not false')
33
+ expect(result).to be_kind_of(Loxxy::Datatype::True)
34
+ end
35
+
31
36
  it 'should print the hello world message' do
32
37
  expect { subject.evaluate(hello_world) }.not_to raise_error
33
38
  expect(sample_cfg[:ostream].string).to eq('Hello, world!')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-09 00:00:00.000000000 Z
11
+ date: 2021-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley