loxxy 0.0.9 → 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: 74cd4aa3bd58cad64cad8ac452535052c761f166da883b06b593590310edd97d
4
- data.tar.gz: 15af4b4a4c86c7b8e6a14bda16433a9d1663ab581eae78331d29adeed02313d3
3
+ metadata.gz: 18b1d3e9f498ca8249775f55cc3950622d9f5a49cd67db912816cc60f15b3d16
4
+ data.tar.gz: 5766130089ebbe6d85a90a9f59ac1a505202c0e2646d4d7da93af025a80b0651
5
5
  SHA512:
6
- metadata.gz: 0dbd9244c36913eed955b1ff92bdc63fd77cc6f7cda3d6b037f3ee87cdcc8d4450673f852426a7395d968711aaf9a3a539efeeaf5e2a0d46865f4f4fa7276c74
7
- data.tar.gz: c8c65b58fa53d0cce81a3e7508317dfbeb5d8e10f883a0a011d278ca33ee647967bfd34e79fc4703ad7a5b4a3a88aece8d17ca0d0db107f0ec0fcb9ac12e4280
6
+ metadata.gz: 621832b48a5625081a0b82a8f8992d78709a1d4a053d8e4719140510242ce14d4418ca23c82d9f52186453c42d5d432203e7af496d7f92726f75622c743119e2
7
+ data.tar.gz: aa67901fae29e13d3154e274c7014fe2002fb7e054c62cf253e87e44ac421747db9f5f05ef1e108cd92c8c0124ceb93b31fa537de64f875607c096465f89aee4
@@ -1,3 +1,51 @@
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
+
1
49
  ## [0.0.9] - 2021-01-07
2
50
  - AST node generation for comparison expression.
3
51
 
data/README.md CHANGED
@@ -1,31 +1,88 @@
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 = '37 + 5; // THE answer'
78
+ result = lox.evaluate(lox_program) # => Loxxy::Datatype::Number
79
+
80
+ # `result` is a Ruby object, so let's use it...
81
+ puts result.value # Output: 42
82
+ ```
27
83
 
28
84
  ## Example using RawParser class
85
+
29
86
  ```ruby
30
87
  require 'loxxy'
31
88
 
@@ -70,6 +127,89 @@ program
70
127
  +-- EOF: ''
71
128
  ```
72
129
 
130
+ ## Suppported Lox language features
131
+ Although the interpreter should parse almost any valid Lox program,
132
+ it currently can evaluate a tiny set of AST node (AST = Abstract Syntax Tree).
133
+
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)
141
+ - `print` statement.
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
+
212
+
73
213
  ## Installation
74
214
 
75
215
  Add this line to your application's Gemfile:
@@ -91,7 +231,6 @@ Or install it yourself as:
91
231
  TODO: Write usage instructions here
92
232
 
93
233
  ## 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
234
 
96
235
  For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
97
236
  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
 
@@ -76,7 +96,7 @@ module Loxxy
76
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,6 +104,90 @@ module Loxxy
84
104
  operand1
85
105
  end
86
106
 
107
+ # rule('something_plus' => 'something_plus operator symbol')
108
+ def reduce_binary_plus_more(_production, _range, _tokens, theChildren)
109
+ result = theChildren[0]
110
+ operator = Name2special[theChildren[1].symbol.name].to_sym
111
+ operand2 = theChildren[2]
112
+ result << [operator, operand2]
113
+ end
114
+
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
118
+ operand2 = theChildren[1]
119
+ [[operator, operand2]]
120
+ end
121
+
122
+ #####################################
123
+ # SEMANTIC ACTIONS
124
+ #####################################
125
+
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
130
+
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
+
87
191
  # rule('comparison' => 'term comparisonTest_plus')
88
192
  def reduce_comparison_plus(production, range, tokens, theChildren)
89
193
  reduce_binary_operator(production, range, tokens, theChildren)
@@ -93,16 +197,8 @@ module Loxxy
93
197
  # TODO: is it meaningful to implement this rule?
94
198
 
95
199
  # rule('comparisonTest_plus' => 'comparisonTest term')
96
- def reduce_comparison_t_plus_end(_production, _range, _tokens, theChildren)
97
- name2operators = {
98
- 'GREATER' => '>',
99
- 'GREATER_EQUAL' => '>=',
100
- 'LESS' => '<',
101
- 'LESS_EQUAL' => '<='
102
- }
103
- operator = name2operators[theChildren[0].symbol.name].to_sym
104
- operand2 = theChildren[1]
105
- [[operator, operand2]]
200
+ def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
201
+ reduce_binary_plus_end(production, range, tokens, theChildren)
106
202
  end
107
203
 
108
204
  # rule('term' => 'factor additive_plus')
@@ -111,18 +207,13 @@ module Loxxy
111
207
  end
112
208
 
113
209
  # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
114
- def reduce_additive_plus_more(_production, _range, _tokens, theChildren)
115
- result = theChildren[0]
116
- operator = theChildren[1].symbol.name == 'MINUS' ? :- : :+
117
- operand2 = theChildren[2]
118
- result << [operator, operand2]
210
+ def reduce_additive_plus_more(production, range, tokens, theChildren)
211
+ reduce_binary_plus_more(production, range, tokens, theChildren)
119
212
  end
120
213
 
121
214
  # rule('additive_plus' => 'additionOp factor')
122
- def reduce_additive_plus_end(_production, _range, _tokens, theChildren)
123
- operator = theChildren[0].symbol.name == 'MINUS' ? :- : :+
124
- operand2 = theChildren[1]
125
- [[operator, operand2]]
215
+ def reduce_additive_plus_end(production, range, tokens, theChildren)
216
+ reduce_binary_plus_end(production, range, tokens, theChildren)
126
217
  end
127
218
 
128
219
  # rule('factor' => 'multiplicative_plus')
@@ -131,18 +222,13 @@ module Loxxy
131
222
  end
132
223
 
133
224
  # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
134
- def reduce_multiplicative_plus_more(_production, _range, _tokens, theChildren)
135
- result = theChildren[0]
136
- operator = theChildren[1].symbol.name == 'SLASH' ? :/ : :*
137
- operand2 = theChildren[2]
138
- result << [operator, operand2]
225
+ def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
226
+ reduce_binary_plus_more(production, range, tokens, theChildren)
139
227
  end
140
228
 
141
229
  # rule('multiplicative_plus' => 'multOp unary')
142
- def reduce_multiplicative_plus_end(_production, _range, _tokens, theChildren)
143
- operator = theChildren[0].symbol.name == 'SLASH' ? :/ : :*
144
- operand2 = theChildren[1]
145
- [[operator, operand2]]
230
+ def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
231
+ reduce_binary_plus_end(production, range, tokens, theChildren)
146
232
  end
147
233
 
148
234
  # rule('primary' => 'FALSE' | TRUE').as 'literal_expr'