loxxy 0.0.8 → 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: ed8bad2906a9ffd76c81a1fe14d89088f913f84996f0a6d98e7f104e0eccfdc9
4
- data.tar.gz: 40ee48dc0f4d7e0ce1028820b9c5cf56d0c78d9853916f3bf9e5f32a7106887a
3
+ metadata.gz: e689f37b8b4893aff00f320707982cc1b09c17258abee50d0ae82f2d9d9d04db
4
+ data.tar.gz: 57aa5e505c245cabd8f4e583a6300dbd5071b9ba580228103c4f9972c7679174
5
5
  SHA512:
6
- metadata.gz: 69c3c6e6b2a60c3f27284be820ac8f2687de0073c28719bd9414578ab37ac59de267dc1e69897966fef33d4ba725597587fe991b8e20ce6d40ec1587f3aa53c4
7
- data.tar.gz: c296dcedcf97fe7426a59cfb2df2144685f2809c64becb18483e62a5aea536c2ca3ae2ab697b865c0945456e191a0a011f01123d34160acfa554761b8a2f917e
6
+ metadata.gz: 9336292545bd83f18d8f1be0a820717dcbe33cbb3b912697ea3e5245b726a5d79810ca6613c07017aef9d766670008bf49f59f6948922f18433db4a0df94f41e
7
+ data.tar.gz: 149d09683e80f1d36a1939cfa13faefb1fc2bdae3b096bbde34d8824f67a305627018229e453b7b9655c78ae45b815726535a307125d238f6399df2c3d0f1aec
@@ -1,8 +1,64 @@
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
+
13
+ ## [0.0.12] - 2021-01-09
14
+ - Initial interpreter capable of evaluating a tiny subset of Lox language.
15
+
16
+ ## Added
17
+ - Class `AST::LoxNoopExpr`
18
+ - Class `AST::LoxPrintStmt`
19
+ - Class `BackEnd::Engine` implementation of the print statement logic
20
+ - Class `Interpreter`
21
+
22
+ ## Changed
23
+ - Class `Ast::ASTVisitor` Added visit method
24
+ - File `README.md` added Hello world example.
25
+
26
+ ## [0.0.11] - 2021-01-08
27
+ - AST node generation for logical expression (and, or).
28
+
29
+ ## Changed
30
+ - Class `AST::ASTBuilder` added `reduce_` methods for logical operations.
31
+ - File `grammar.rb`added name to logical expression rules
32
+ - File `README.md` added gem version and license badges, expanded roadmap section.
33
+
34
+ ## Fixed
35
+ - File `grammar.rb`: a rule had incomplete non-terminal name `conjunct_` in its lhs.
36
+
37
+
38
+ ## [0.0.10] - 2021-01-08
39
+ - AST node generation for equality expression.
40
+
41
+ ## Changed
42
+ - Class `AST::ASTBuilder` refactoring and added `reduce_` methods for equality operations.
43
+ - File `grammar.rb`added name to equality rules
44
+ - File `README.md` added gem version and license badges, expanded roadmap section.
45
+
46
+ ## Fixed
47
+ - File `grammar.rb`: a rule had still the discarded non-terminal `equalityTest_star` in its lhs.
48
+
49
+ ## [0.0.9] - 2021-01-07
50
+ - AST node generation for comparison expression.
51
+
52
+ ## Changed
53
+ - Class `AST::ASTBuilder` added `reduce_` methods for comparison operations.
54
+ - File `grammar.rb`added name to comparison rules
55
+
1
56
  ## [0.0.8] - 2021-01-07
2
57
  - AST node generation for arithmetic operations of number literals.
3
58
 
4
59
  ## Changed
5
60
  - Class `AST::ASTBuilder` added `reduce_` methods for arithmetic operations.
61
+ - File `grammar.rb`added name to arithmetic rules
6
62
 
7
63
  ## Fixed
8
64
  - File `grammar.rb`: second rule for `factor` had a missing member in rhs.
data/README.md CHANGED
@@ -1,31 +1,87 @@
1
- # Loxxy
1
+ # loxxy
2
+ [![Gem Version](https://badge.fury.io/rb/loxxy.svg)](https://badge.fury.io/rb/loxxy)
3
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
4
+
2
5
 
3
6
  A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
4
7
  a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
5
8
 
6
- ## Purpose of this project:
9
+ ### Purpose of this project:
7
10
  - To deliver an open source example of a programming language fully implemented in Ruby
8
- (from the scanner, parser, code generation).
9
- - The implementation should be mature enough to run (LoxLox)[https://github.com/benhoyt/loxlox],
11
+ (from the scanner, parser, an interpreter).
12
+ - The implementation should be mature enough to run [LoxLox](https://github.com/benhoyt/loxlox),
10
13
  a Lox interpreter written in Lox.
11
14
 
12
- ## Current status
13
- The __loxxy__ gem hosts two distinct parsers classes (`RawParser` and `Parser`).
14
- - A `RawParser` instance is able to recognize valid Lox input and to generate
15
- a concrete parse tree from it.
16
- - A `Parser` instance can also parse Lox source code but will generate an AST
17
- (Abstract Syntax Tree) that will be used by the future tree-walking interpreter.
18
- Currently it generates AST for arithmetic expressions with literal numbers only.
19
-
20
- ## Roadmap
21
- - [DONE] Scanner (tokenizer)
22
- - [DONE] Raw parser. It parses Lox programs and generates a parse tree.
23
- - [DONE] Tailored parser for generating AST (Abstract Syntax Tree)
24
- - [STARTED] Custom AST builder class
25
- - [STARTED] Hierarchy classes for representing Lox expressions in AST
26
- - [TODO] Interpreter or transpiler
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
+
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)
55
+
56
+ ## Hello world example
57
+ ```ruby
58
+ require 'loxxy'
59
+
60
+ lox_program = <<LOX_END
61
+ // Your first Lox program!
62
+ print "Hello, world!";
63
+ LOX_END
64
+
65
+ lox = Loxxy::Interpreter.new
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
81
+ ```
27
82
 
28
83
  ## Example using RawParser class
84
+
29
85
  ```ruby
30
86
  require 'loxxy'
31
87
 
@@ -70,6 +126,16 @@ program
70
126
  +-- EOF: ''
71
127
  ```
72
128
 
129
+ ## Suppported Lox language features
130
+ Although the interpreter should parse almost any valid Lox program,
131
+ it currently can evaluate a tiny set of AST node (AST = Abstract Syntax Tree).
132
+
133
+ Here are the language features supported by the interpreter:
134
+ - Line comments,
135
+ - All the Lox literals (booleans, numbers, strings and nil),
136
+ - `print` statement.
137
+
138
+
73
139
  ## Installation
74
140
 
75
141
  Add this line to your application's Gemfile:
@@ -91,7 +157,6 @@ Or install it yourself as:
91
157
  TODO: Write usage instructions here
92
158
 
93
159
  ## Other Lox implementations in Ruby
94
- An impressive list of Lox implementations can be found [here](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations)
95
160
 
96
161
  For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
97
162
  There are other Ruby-based projects as well:
@@ -1,10 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'loxxy/version'
4
+ require_relative 'loxxy/interpreter'
4
5
  require_relative 'loxxy/front_end/raw_parser'
5
- require_relative 'loxxy/front_end/parser'
6
6
 
7
+ # Namespace for all classes and constants of __loxxy__ gem.
7
8
  module Loxxy
8
9
  class Error < StandardError; end
9
- # Your code goes here...
10
+
11
+ # Shorthand method. Returns the sole object that represents
12
+ # a Lox false literal.
13
+ # @return [Loxxy::Datatype::False]
14
+ def self.lox_false
15
+ Datatype::False.instance
16
+ end
17
+
18
+ # Shorthand method. Returns the sole object that represents
19
+ # a Lox nil literal.
20
+ # @return [Loxxy::Datatype::Nil]
21
+ def self.lox_nil
22
+ Datatype::Nil.instance
23
+ end
24
+
25
+ # Shorthand method. Returns the sole object that represents
26
+ # a Lox true literal.
27
+ # @return [Loxxy::Datatype::True]
28
+ def self.lox_true
29
+ Datatype::True.instance
30
+ end
10
31
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_print_stmt'
4
+ require_relative 'lox_literal_expr'
5
+ require_relative 'lox_noop_expr'
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../datatype/all_datatypes'
4
- require_relative 'lox_literal_expr'
4
+ require_relative 'all_lox_nodes'
5
5
  require_relative 'lox_binary_expr'
6
6
 
7
7
  module Loxxy
@@ -10,13 +10,33 @@ module Loxxy
10
10
  # (Abstract Syntax Tree) from a sequence of input tokens and
11
11
  # visit events produced by walking over a GFGParsing object.
12
12
  class ASTBuilder < Rley::ParseRep::ASTBaseBuilder
13
- # Terminal2NodeClass = {
14
- # 'FALSE' => Datatype::False,
15
- # 'NIL' => Datatype::Nil,
16
- # 'NUMBER' => Datatype::Number,
17
- # 'STRING' => Datatype::LXString,
18
- # 'TRUE' => Datatype::True
19
- # }.freeze
13
+ unless defined?(Name2special)
14
+ # Mapping Token name => operator | separator | delimiter characters
15
+ # @return [Hash{String => String}]
16
+ Name2special = {
17
+ 'AND' => 'and',
18
+ 'BANG' => '!',
19
+ 'BANG_EQUAL' => '!=',
20
+ 'COMMA' => ',',
21
+ 'DOT' => '.',
22
+ 'EQUAL' => '=',
23
+ 'EQUAL_EQUAL' => '==',
24
+ 'GREATER' => '>',
25
+ 'GREATER_EQUAL' => '>=',
26
+ 'LEFT_BRACE' => '{',
27
+ 'LEFT_PAREN' => '(',
28
+ 'LESS' => '<',
29
+ 'LESS_EQUAL' => '<=',
30
+ 'MINUS' => '-',
31
+ 'OR' => 'or',
32
+ 'PLUS' => '+',
33
+ 'RIGHT_BRACE' => '}',
34
+ 'RIGHT_PAREN' => ')',
35
+ 'SEMICOLON' => ';',
36
+ 'SLASH' => '/',
37
+ 'STAR' => '*'
38
+ }.freeze
39
+ end # defined
20
40
 
21
41
  attr_reader :strict
22
42
 
@@ -72,11 +92,11 @@ module Loxxy
72
92
  node
73
93
  end
74
94
 
75
- # rule('term' => 'factor additive_plus')
76
- def reduce_term_additive(_production, _range, tokens, theChildren)
95
+ # rule('lhs' => 'nonterm_i nonterm_k_plus')
96
+ def reduce_binary_operator(_production, _range, tokens, theChildren)
77
97
  operand1 = theChildren[0]
78
98
 
79
- # Second child is anray with couples [operator, operand2]
99
+ # Second child is array with couples [operator, operand2]
80
100
  theChildren[1].each do |(operator, operand2)|
81
101
  operand1 = LoxBinaryExpr.new(tokens[0].position, operator, operand1, operand2)
82
102
  end
@@ -84,46 +104,131 @@ module Loxxy
84
104
  operand1
85
105
  end
86
106
 
87
- # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
88
- def reduce_additive_plus_more(_production, _range, _tokens, theChildren)
107
+ # rule('something_plus' => 'something_plus operator symbol')
108
+ def reduce_binary_plus_more(_production, _range, _tokens, theChildren)
89
109
  result = theChildren[0]
90
- operator = theChildren[1].symbol.name == 'MINUS' ? :- : :+
110
+ operator = Name2special[theChildren[1].symbol.name].to_sym
91
111
  operand2 = theChildren[2]
92
112
  result << [operator, operand2]
93
113
  end
94
114
 
95
- # rule('additive_plus' => 'additionOp factor')
96
- def reduce_additive_plus_end(_production, _range, _tokens, theChildren)
97
- operator = theChildren[0].symbol.name == 'MINUS' ? :- : :+
115
+ # rule('something_plus' => 'something_plus symbol')
116
+ def reduce_binary_plus_end(_production, _range, _tokens, theChildren)
117
+ operator = Name2special[theChildren[0].symbol.name].to_sym
98
118
  operand2 = theChildren[1]
99
119
  [[operator, operand2]]
100
120
  end
101
121
 
102
- # rule('factor' => 'multiplicative_plus')
103
- def reduce_factor_multiplicative(_production, _range, tokens, theChildren)
104
- operand1 = theChildren[0]
122
+ #####################################
123
+ # SEMANTIC ACTIONS
124
+ #####################################
105
125
 
106
- # Second child is anray with couples [operator, operand2]
107
- theChildren[1].each do |(operator, operand2)|
108
- operand1 = LoxBinaryExpr.new(tokens[0].position, operator, operand1, operand2)
109
- end
126
+ # rule('program' => 'EOF').as 'null_program'
127
+ def reduce_null_program(_production, _range, _tokens, _theChildren)
128
+ Ast::LoxNoopExpr.new(tokens[0].position)
129
+ end
110
130
 
111
- operand1
131
+ # rule('program' => 'declaration_plus EOF').as ''
132
+ def reduce_lox_program(_production, 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
139
+ end
140
+
141
+ # rule('printStmt' => 'PRINT expression SEMICOLON')
142
+ def reduce_print_stmt(_production, _range, tokens, theChildren)
143
+ Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
144
+ end
145
+
146
+ # rule('logic_or' => 'logic_and disjunct_plus')
147
+ def reduce_logic_or_plus(production, range, tokens, theChildren)
148
+ reduce_binary_operator(production, range, tokens, theChildren)
149
+ end
150
+
151
+ # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
152
+ def reduce_logic_or_plus_more(production, range, tokens, theChildren)
153
+ reduce_binary_plus_more(production, range, tokens, theChildren)
154
+ end
155
+
156
+ # rule('disjunct_plus' => 'OR logic_and')
157
+ def reduce_logic_or_plus_end(production, range, tokens, theChildren)
158
+ reduce_binary_plus_end(production, range, tokens, theChildren)
159
+ end
160
+
161
+ # rule('logic_and' => 'equality conjunct_plus')
162
+ def reduce_logic_and_plus(production, range, tokens, theChildren)
163
+ reduce_binary_operator(production, range, tokens, theChildren)
164
+ end
165
+
166
+ # rule('conjunct_plus' => 'conjunct_plus AND equality')
167
+ def reduce_logic_and_plus_more(production, range, tokens, theChildren)
168
+ reduce_binary_plus_more(production, range, tokens, theChildren)
169
+ end
170
+
171
+ # rule('conjunct_plus' => 'AND equality')
172
+ def reduce_logic_and_plus_end(production, range, tokens, theChildren)
173
+ reduce_binary_plus_end(production, range, tokens, theChildren)
174
+ end
175
+
176
+ # rule('equality' => 'comparison equalityTest_plus')
177
+ def reduce_equality_plus(production, range, tokens, theChildren)
178
+ reduce_binary_operator(production, range, tokens, theChildren)
179
+ end
180
+
181
+ # rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
182
+ def reduce_equality_t_plus_more(production, range, tokens, theChildren)
183
+ reduce_binary_plus_more(production, range, tokens, theChildren)
184
+ end
185
+
186
+ # rule('equalityTest_star' => 'equalityTest comparison')
187
+ def reduce_equality_t_plus_end(production, range, tokens, theChildren)
188
+ reduce_binary_plus_end(production, range, tokens, theChildren)
189
+ end
190
+
191
+ # rule('comparison' => 'term comparisonTest_plus')
192
+ def reduce_comparison_plus(production, range, tokens, theChildren)
193
+ reduce_binary_operator(production, range, tokens, theChildren)
194
+ end
195
+
196
+ # rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
197
+ # TODO: is it meaningful to implement this rule?
198
+
199
+ # rule('comparisonTest_plus' => 'comparisonTest term')
200
+ def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
201
+ reduce_binary_plus_end(production, range, tokens, theChildren)
202
+ end
203
+
204
+ # rule('term' => 'factor additive_plus')
205
+ def reduce_term_additive(production, range, tokens, theChildren)
206
+ reduce_binary_operator(production, range, tokens, theChildren)
207
+ end
208
+
209
+ # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
210
+ def reduce_additive_plus_more(production, range, tokens, theChildren)
211
+ reduce_binary_plus_more(production, range, tokens, theChildren)
212
+ end
213
+
214
+ # rule('additive_plus' => 'additionOp factor')
215
+ def reduce_additive_plus_end(production, range, tokens, theChildren)
216
+ reduce_binary_plus_end(production, range, tokens, theChildren)
217
+ end
218
+
219
+ # rule('factor' => 'multiplicative_plus')
220
+ def reduce_factor_multiplicative(production, range, tokens, theChildren)
221
+ reduce_binary_operator(production, range, tokens, theChildren)
112
222
  end
113
223
 
114
224
  # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
115
- def reduce_multiplicative_plus_more(_production, _range, _tokens, theChildren)
116
- result = theChildren[0]
117
- operator = theChildren[1].symbol.name == 'SLASH' ? :/ : :*
118
- operand2 = theChildren[2]
119
- result << [operator, operand2]
225
+ def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
226
+ reduce_binary_plus_more(production, range, tokens, theChildren)
120
227
  end
121
228
 
122
229
  # rule('multiplicative_plus' => 'multOp unary')
123
- def reduce_multiplicative_plus_end(_production, _range, _tokens, theChildren)
124
- operator = theChildren[0].symbol.name == 'SLASH' ? :/ : :*
125
- operand2 = theChildren[1]
126
- [[operator, operand2]]
230
+ def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
231
+ reduce_binary_plus_end(production, range, tokens, theChildren)
127
232
  end
128
233
 
129
234
  # rule('primary' => 'FALSE' | TRUE').as 'literal_expr'