loxxy 0.0.11 → 0.0.16

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: 583f5c2f620ea8f45b339607103955eda2f32c02f0358ccb88cf4d20b4398d21
4
- data.tar.gz: f349b949c1315473027eaaab589ff1cf2da89deca3b243cd1508c088bf9a9cc5
3
+ metadata.gz: 7d7d30d3246aa8a8f06f3456d3996c680e9ddb4ed31bcf9fff99685702e50dd1
4
+ data.tar.gz: dc428ddd0fc03e153e70873bc2827b6e4aac813e5fdfba40ef2f28bfa7283fc2
5
5
  SHA512:
6
- metadata.gz: 9c93648c634783348a22b3b65cf710a59c0ca5e3cefc5a20d7c60b9b429b4334e1e5603cc2bf74f302aed881daa71634cb7253445b3f376838197637f79f8cc7
7
- data.tar.gz: 67d60e89985322911a7842a2b5fc2e22b4ccc3136ffa216027089ea05ea56a6932d1de9b8917173ad072695aaa6abb2147d456f54d09f2878d7831937c42af74
6
+ metadata.gz: d90f2494a324be036dae2f3d38636c571009b60f19d2b3d917b242ab98d231e03bb121f577e5598ac63514cc5817814972cabe927f05af2842a5a97c89eb69d9
7
+ data.tar.gz: e09a70a27baa1408904e1bfe512f4f523b8e4efd1aab00412b993c8f4a35b5498f1f6ddb5e67697108c80a91778301e41c15741451e73e1d869496d96441757a
@@ -1,3 +1,77 @@
1
+ ## [0.0.16] - 2021-01-11
2
+ - The interpreter can evaluate product and division of two numbers.
3
+ - It also implements equality `==` and inequality `!=` operators
4
+
5
+ ## Added
6
+ - Method `Datatype::False#==` for equality testing
7
+ - Method `Datatype::False#!=` for inequality testing
8
+ - Method `Datatype::LXString#==` for equality testing
9
+ - Method `Datatype::LXString#!=` for inequality testing
10
+ - Method `Datatype::Nil#==` for equality testing
11
+ - Method `Datatype::Nil#!=` for inequality testing
12
+ - Method `Datatype::Number#==` for equality testing
13
+ - Method `Datatype::Number#!=` for inequality testing
14
+ - Method `Datatype::Number#*` for multiply operator
15
+ - Method `Datatype::Number#/` for divide operator
16
+ - Method `Datatype::True#==` for equality testing
17
+ - Method `Datatype::True#!=` for inequality testing
18
+
19
+ ## Changed
20
+ - Method `BackEnd::Engine#after_binary_expr` to allow `*`, `/`, `==`, `!=` operators
21
+ - File `README.md` updated for the newly implemented operators
22
+
23
+ ## [0.0.15] - 2021-01-11
24
+ - The interpreter can evaluate substraction between two numbers.
25
+
26
+ ## Added
27
+ - Method `Datatype::Number#-` implmenting the subtraction operation
28
+
29
+ ## Changed
30
+ - File `README.md` minor editorial changes.
31
+ - File `lx_string_spec.rb` Added test for string concatentation
32
+ - File `number_spec.rb` Added tests for addition and subtraction operations
33
+ - File `interpreter_spec.rb` Added tests for subtraction operation
34
+
35
+ ## [0.0.14] - 2021-01-10
36
+ - The interpreter can evaluate addition of numbers and string concatenation
37
+
38
+ ## Added
39
+ - Method `Ast::ASTVisitor::visit_binary_expr` for visiting binary expressions
40
+ - Method `Ast::LoxBinaryExpr#accept` for visitor pattern
41
+ - Method `BackEnd::Engine#after_binary_expr` to trigger execution of binary operator
42
+ - `Boolean` class hierarchy: added methos `true?` and `false?` to ease spec test writing
43
+ - Method `Datatype::LXString#+` implementation of the string concatenation
44
+ - Method `Datatype::Number#+` implementation of the addition of numbers
45
+
46
+ ## Changed
47
+ - File `interpreter_spec.rb` Added tests for addition operation and string concatenation
48
+
49
+
50
+ ## [0.0.13] - 2021-01-10
51
+ - The interpreter can evaluate directly simple literals.
52
+
53
+ ## Changed
54
+ - Class `AST::ASTBuilder` added `reduce_exprStmt` to support the evaluation of literals.
55
+ - File `README.md` added one more example.
56
+ - File `parser_spec.rb` Updated the tests to reflect the change in the AST.
57
+ - File `interpreter_spec.rb` Added a test for literal expression.
58
+
59
+ ## Fixed
60
+ - File `loxxy.rb`: shorthand method `lox_true` referenced the ... false object (oops).
61
+
62
+ ## [0.0.12] - 2021-01-09
63
+ - Initial interpreter capable of evaluating a tiny subset of Lox language.
64
+
65
+ ## Added
66
+ - Class `AST::LoxNoopExpr`
67
+ - Class `AST::LoxPrintStmt`
68
+ - Class `BackEnd::Engine` implementation of the print statement logic
69
+ - Class `Interpreter`
70
+
71
+ ## Changed
72
+ - Class `Ast::ASTVisitor` Added visit method
73
+ - File `README.md` added Hello world example.
74
+
1
75
  ## [0.0.11] - 2021-01-08
2
76
  - AST node generation for logical expression (and, or).
3
77
 
data/README.md CHANGED
@@ -2,69 +2,87 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/loxxy.svg)](https://badge.fury.io/rb/loxxy)
3
3
  [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/famished-tiger/loxxy/blob/main/LICENSE.txt)
4
4
 
5
-
5
+ ### What is loxxy?
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 __loxxy__ gem hosts two distinct parsers classes (`RawParser` and `Parser`).
17
- - A `RawParser` instance is able to recognize valid Lox input and to generate
18
- a concrete parse tree from it.
19
- - A `Parser` instance can also parse Lox source code but will generate an AST
20
- (Abstract Syntax Tree) that will be used by the future tree-walking interpreter.
21
- Currently it generates AST for arithmetic expressions with literal numbers only.
22
-
23
- ## Roadmap
24
- ### Done
25
- - Scanner (tokenizer)
26
- - Lox grammar (in format required by Rley gem)
27
- - Raw parser. It parses Lox programs and generates a raw parse tree.
28
- - Tailored parser for generating AST (Abstract Syntax Tree)
29
-
30
- ### Started
31
- - Custom AST builder class
32
- - Classes for representing Lox expressions in AST
33
-
34
- ### TODO
35
- AST Node generation
36
- Goal: parser should generate AST for any input Lox program
37
- - [X] Equality operator
38
- - [X] Logical operator (and, or)
39
- - [] Unary expressions (negate, not)
40
- - [] Grouping expressions
41
- - [] Print statement
42
- - [] Simple assignment expressions
43
- - [] Variable declaration
44
- - [] Dot notation
45
- - [] Block statement
46
- - [] If statement
47
- - [] For statement
48
- - [] While statement
49
- - [] Function declaration
50
- - [] Call expression
51
- - [] Return statement
52
- - [] Class declaration
53
- - [] AST generation covers complete grammar
54
-
55
- Tree-walking:
56
- - [] Tree visitor recognizes all AST node types
57
-
58
- Interpreter:
59
- - Keywords: symbol table, scope, activation record, class & object model, Lox test suite
60
- - [] Milestone: Interpreter handles expressions but function calls
61
- - [] Milestone: Interpreter handles `for`, `if`, `print`, `while` and block statements
62
- - [] Milestone: Interpreter handles variable declarations (global, block)
63
- - [] Milestone: Interpreter handles function declarations and calls
64
- - [] Milestone: Interpreter supports class and object definition
65
- - [] Milestone: Lox interpreter complete
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
+ But the __loxxy__ gem hosts 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 = '47 - 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
+ ```
66
83
 
67
84
  ## Example using RawParser class
85
+
68
86
  ```ruby
69
87
  require 'loxxy'
70
88
 
@@ -109,6 +127,89 @@ program
109
127
  +-- EOF: ''
110
128
  ```
111
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
+
142
+ ### Comments
143
+
144
+ Loxxy supports single line C-style comments.
145
+
146
+ ```javascript
147
+ // single line comment
148
+ ```
149
+
150
+ ### Keywords
151
+
152
+ The parser knows all the __Lox__ reserved keywords:
153
+ ```lang-none
154
+ and, class, else, false, fun, for, if, nil, or,
155
+ print, return, super, this, true, var, while
156
+ ```
157
+ Of these, the interpreter implements: `false`, `nil`, `true`
158
+
159
+ ### Operators and Special Chars
160
+ #### Operators
161
+ The parser recognizes all the __Lox__ operators, delimiters and separators:
162
+ - Arithmetic operators: `+`, `-`, `*`, `/`
163
+ - Comparison operators: `>`, `>=`, `<`, `<=`
164
+ - Equality operators: `==`, `!=`
165
+
166
+ Of these, the interpreter implements:
167
+ `+` (addition of two numbers or string concatenation)
168
+ `-` (difference between two numbers)
169
+ `*` (product of two numbers)
170
+ `/` (division of two number)
171
+ `==` and `!=` (in)equality testing of two Lox values
172
+
173
+ #### Delimiters
174
+ The parser knows all the __Lox__ grouping delimiters:
175
+ (`, ), `{`, `}`
176
+
177
+ These aren't yet implemented in the interpreter.
178
+
179
+ The other characters that have a special meaning in __Lox__ are:
180
+ - `,` Used in parameter list
181
+ - `.` For the dot notation (i.e. calling a method)
182
+ - `;` The semicolon is used to terminates expressions
183
+ - `=` Assignment
184
+
185
+ The parser recognizes them all but the interpreter accepts the semicolons only.
186
+
187
+ ### Datatypes
188
+
189
+ loxxy supports all the standard __Lox__ datatypes:
190
+ - `Boolean`: Can be `true` or `false`
191
+ - `Number`: Can be an integer or a floating-point numbers. For example: `123, 12.34, -45.67`
192
+ - `String`: Sequence of characters surrounded by `"`. For example: `"Hello!"`
193
+ - `Nil`: Used to define a null value, denoted by the `nil` keyword
194
+
195
+ ## Statements
196
+ ### Implemented expressions
197
+ Loxxy implements expressions:
198
+ - Plain literals only; or,
199
+ - Addition, subtraction, product and division of two numbers; or,
200
+ - Concatenation of two strings,
201
+ - (In)equality test of two Lox values
202
+
203
+ ### Implemented statements
204
+ Loxxy implements the following statements:
205
+ - Expressions (see above sub-section)
206
+ - Print statement
207
+
208
+ ```javascript
209
+ // Print statement with nested concatenation
210
+ print "Hello" + ", " + "world!";
211
+ ```
212
+
112
213
  ## Installation
113
214
 
114
215
  Add this line to your application's Gemfile:
@@ -130,7 +231,6 @@ Or install it yourself as:
130
231
  TODO: Write usage instructions here
131
232
 
132
233
  ## Other Lox implementations in Ruby
133
- An impressive list of Lox implementations can be found [here](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations)
134
234
 
135
235
  For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
136
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,31 +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
- # Mapping Token name => operator | separator | delimiter characters
14
- # @return [Hash{String => String}]
15
- Name2special = {
16
- 'AND' => 'and',
17
- 'BANG' => '!',
18
- 'BANG_EQUAL' => '!=',
19
- 'COMMA' => ',',
20
- 'DOT' => '.',
21
- 'EQUAL' => '=',
22
- 'EQUAL_EQUAL' => '==',
23
- 'GREATER' => '>',
24
- 'GREATER_EQUAL' => '>=',
25
- 'LEFT_BRACE' => '{',
26
- 'LEFT_PAREN' => '(',
27
- 'LESS' => '<',
28
- 'LESS_EQUAL' => '<=',
29
- 'MINUS' => '-',
30
- 'OR' => 'or',
31
- 'PLUS' => '+',
32
- 'RIGHT_BRACE' => '}',
33
- 'RIGHT_PAREN' => ')',
34
- 'SEMICOLON' => ';',
35
- 'SLASH' => '/',
36
- 'STAR' => '*'
37
- }.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
38
40
 
39
41
  attr_reader :strict
40
42
 
@@ -117,6 +119,30 @@ module Loxxy
117
119
  [[operator, operand2]]
118
120
  end
119
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
+
120
146
  # rule('logic_or' => 'logic_and disjunct_plus')
121
147
  def reduce_logic_or_plus(production, range, tokens, theChildren)
122
148
  reduce_binary_operator(production, range, tokens, theChildren)
@@ -141,7 +167,7 @@ module Loxxy
141
167
  def reduce_logic_and_plus_more(production, range, tokens, theChildren)
142
168
  reduce_binary_plus_more(production, range, tokens, theChildren)
143
169
  end
144
-
170
+
145
171
  # rule('conjunct_plus' => 'AND equality')
146
172
  def reduce_logic_and_plus_end(production, range, tokens, theChildren)
147
173
  reduce_binary_plus_end(production, range, tokens, theChildren)