loxxy 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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