loxxy 0.0.7 → 0.0.12

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: d1b591e9ed6ae88b4b4232c01c4f08fd4453fb499a665c368cd64ce2135a6718
4
- data.tar.gz: 6bc1f01f9f0a963a2781271c46391e2c2801b61cc1b076e0b79be30450bee2af
3
+ metadata.gz: 7274fd2d139354b984962f9317cae890344e528540f2f157772e7395cdfd7d33
4
+ data.tar.gz: c1b38a4dfcb441c00604926cbaf397ab555624270ca647448d9cc4d4f463f500
5
5
  SHA512:
6
- metadata.gz: 5dd208921fc139eb5f48b182abbe64b919c514904dfab073b09c4a9656989db5c95a5e1bd6f72733a46af8351dc6f95ab94612d8016c22e13dbcedd4637db90b
7
- data.tar.gz: 63ac04a1cd79335daf5dbb38de2615e89044c49ec74fd174200149ef24d829861c3af0e7bf62862cbffc03940b955a6c9336344696d432e4efaa83d2dc1e9d35
6
+ metadata.gz: 27d5de6532213d1a995f86da84ccbd41a11b5f85c305e57a62f64637ad17852e622685e4261b1c62deb31c924bb8f8c29c4edd97e9776fabf7e0d06e5c302534
7
+ data.tar.gz: dab4a9e391c120c159a63e316f90065faf1415b54c9aa4c6f349f6be8e95de1289a2478f395e6b9e19b2e6f09482ae4db49a70e0d5d857eb5475cecad0590c69
@@ -1,3 +1,56 @@
1
+ ## [0.0.12] - 2021-01-09
2
+ - Initial interpreter capable of evaluating a tiny subset of Lox language.
3
+
4
+ ## Added
5
+ - Class `AST::LoxNoopExpr`
6
+ - Class `AST::LoxPrintStmt`
7
+ - Class `BackEnd::Engine` implementation of the print statement logic
8
+ - Class `Interpreter`
9
+
10
+ ## Changed
11
+ - Class `Ast::ASTVisitor` Added visit method
12
+ - File `README.md` added Hello world example.
13
+
14
+ ## [0.0.11] - 2021-01-08
15
+ - AST node generation for logical expression (and, or).
16
+
17
+ ## Changed
18
+ - Class `AST::ASTBuilder` added `reduce_` methods for logical operations.
19
+ - File `grammar.rb`added name to logical expression rules
20
+ - File `README.md` added gem version and license badges, expanded roadmap section.
21
+
22
+ ## Fixed
23
+ - File `grammar.rb`: a rule had incomplete non-terminal name `conjunct_` in its lhs.
24
+
25
+
26
+ ## [0.0.10] - 2021-01-08
27
+ - AST node generation for equality expression.
28
+
29
+ ## Changed
30
+ - Class `AST::ASTBuilder` refactoring and added `reduce_` methods for equality operations.
31
+ - File `grammar.rb`added name to equality rules
32
+ - File `README.md` added gem version and license badges, expanded roadmap section.
33
+
34
+ ## Fixed
35
+ - File `grammar.rb`: a rule had still the discarded non-terminal `equalityTest_star` in its lhs.
36
+
37
+ ## [0.0.9] - 2021-01-07
38
+ - AST node generation for comparison expression.
39
+
40
+ ## Changed
41
+ - Class `AST::ASTBuilder` added `reduce_` methods for comparison operations.
42
+ - File `grammar.rb`added name to comparison rules
43
+
44
+ ## [0.0.8] - 2021-01-07
45
+ - AST node generation for arithmetic operations of number literals.
46
+
47
+ ## Changed
48
+ - Class `AST::ASTBuilder` added `reduce_` methods for arithmetic operations.
49
+ - File `grammar.rb`added name to arithmetic rules
50
+
51
+ ## Fixed
52
+ - File `grammar.rb`: second rule for `factor` had a missing member in rhs.
53
+
1
54
  ## [0.0.7] - 2021-01-06
2
55
  - Lox grammar reworked, initial AST classes created.
3
56
 
data/README.md CHANGED
@@ -1,4 +1,7 @@
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/ ).
@@ -6,25 +9,30 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
6
9
  ## Purpose of this project:
7
10
  - To deliver an open source example of a programming language fully implemented in Ruby
8
11
  (from the scanner, parser, code generation).
9
- - The implementation should be mature enough to run (LoxLox)[https://github.com/benhoyt/loxlox],
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
- ## Roadmap
13
- - [DONE] Scanner (tokenizer)
14
- - [DONE] Raw parser. It parses Lox programs and generates a parse tree.
15
- - [DONE] Tailored parser for generating AST (Abstract Syntax Tree)
16
- - [STARTED] Custom AST builder class
17
- - [STARTED] Hierarchy classes for representing Lox expressions in AST
18
- - [TODO] Interpreter or transpiler
19
-
20
- ## Example
21
- The __loxxy__ hosts two distinct parsers classes (`RawParser` and `Parser`).
22
- - A `RawParser` instance is able to recognize Lox input and to generate
23
- a concrete parse tree from it.
24
- - A `Parser` instance can also parse Lox source code but will generate an AST
25
- (Abstract Syntax Tree) that will be used by the future tree-walking interpreter.
26
-
27
- ### Example using RawParser class
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.
18
+
19
+ The __loxxy__ gem also a parser class `RawPaser` that can, in principle, any valid Lox input.
20
+
21
+ ## Hello world example
22
+ ```ruby
23
+ require 'loxxy'
24
+
25
+ lox_program = <<LOX_END
26
+ // Your first Lox program!
27
+ print "Hello, world!";
28
+ LOX_END
29
+
30
+ lox = Loxxy::Interpreter.new
31
+ lox.evaluate(lox_program) # => Hello, world!
32
+ ```
33
+
34
+ ## Example using RawParser class
35
+
28
36
  ```ruby
29
37
  require 'loxxy'
30
38
 
@@ -69,6 +77,15 @@ program
69
77
  +-- EOF: ''
70
78
  ```
71
79
 
80
+ ## Suppported Lox language features
81
+ Although the interpreter should parse almost any valid Lox program,
82
+ it currently can evaluate a tiny set of AST node (AST = Abstract Syntax Tree).
83
+
84
+ Here are the language features supported by the interpreter:
85
+ - Line comments,
86
+ - All the Lox literals (booleans, numbers, strings and nil),
87
+ - `print` statement.
88
+
72
89
 
73
90
  ## Installation
74
91
 
@@ -91,9 +108,9 @@ Or install it yourself as:
91
108
  TODO: Write usage instructions here
92
109
 
93
110
  ## Other Lox implementations in Ruby
94
- An impressive list of Lox implementations can be found [here](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations
111
+ An impressive list of Lox implementations can be found [here](https://github.com/munificent/craftinginterpreters/wiki/Lox-implementations)
95
112
 
96
- For Ruby, ther is the [lox](https://github.com/rdodson41/ruby-lox) gem.
113
+ For Ruby, there is the [lox](https://github.com/rdodson41/ruby-lox) gem.
97
114
  There are other Ruby-based projects as well:
98
115
  - [SlowLox](https://github.com/ArminKleinert/SlowLox), described as a "1-to-1 conversion of JLox to Ruby"
99
116
  - [rulox](https://github.com/LevitatingBusinessMan/rulox)
@@ -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::False.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,18 +92,139 @@ module Loxxy
72
92
  node
73
93
  end
74
94
 
95
+ # rule('lhs' => 'nonterm_i nonterm_k_plus')
96
+ def reduce_binary_operator(_production, _range, tokens, theChildren)
97
+ operand1 = theChildren[0]
98
+
99
+ # Second child is array with couples [operator, operand2]
100
+ theChildren[1].each do |(operator, operand2)|
101
+ operand1 = LoxBinaryExpr.new(tokens[0].position, operator, operand1, operand2)
102
+ end
103
+
104
+ operand1
105
+ end
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)
134
+ end
135
+
136
+ # rule('printStmt' => 'PRINT expression SEMICOLON')
137
+ def reduce_print_stmt(_production, _range, tokens, theChildren)
138
+ Ast::LoxPrintStmt.new(tokens[1].position, theChildren[1])
139
+ end
140
+
141
+ # rule('logic_or' => 'logic_and disjunct_plus')
142
+ def reduce_logic_or_plus(production, range, tokens, theChildren)
143
+ reduce_binary_operator(production, range, tokens, theChildren)
144
+ end
145
+
146
+ # rule('disjunct_plus' => 'disjunct_plus OR logic_and')
147
+ def reduce_logic_or_plus_more(production, range, tokens, theChildren)
148
+ reduce_binary_plus_more(production, range, tokens, theChildren)
149
+ end
150
+
151
+ # rule('disjunct_plus' => 'OR logic_and')
152
+ def reduce_logic_or_plus_end(production, range, tokens, theChildren)
153
+ reduce_binary_plus_end(production, range, tokens, theChildren)
154
+ end
155
+
156
+ # rule('logic_and' => 'equality conjunct_plus')
157
+ def reduce_logic_and_plus(production, range, tokens, theChildren)
158
+ reduce_binary_operator(production, range, tokens, theChildren)
159
+ end
160
+
161
+ # rule('conjunct_plus' => 'conjunct_plus AND equality')
162
+ def reduce_logic_and_plus_more(production, range, tokens, theChildren)
163
+ reduce_binary_plus_more(production, range, tokens, theChildren)
164
+ end
165
+
166
+ # rule('conjunct_plus' => 'AND equality')
167
+ def reduce_logic_and_plus_end(production, range, tokens, theChildren)
168
+ reduce_binary_plus_end(production, range, tokens, theChildren)
169
+ end
170
+
171
+ # rule('equality' => 'comparison equalityTest_plus')
172
+ def reduce_equality_plus(production, range, tokens, theChildren)
173
+ reduce_binary_operator(production, range, tokens, theChildren)
174
+ end
175
+
176
+ # rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
177
+ def reduce_equality_t_plus_more(production, range, tokens, theChildren)
178
+ reduce_binary_plus_more(production, range, tokens, theChildren)
179
+ end
180
+
181
+ # rule('equalityTest_star' => 'equalityTest comparison')
182
+ def reduce_equality_t_plus_end(production, range, tokens, theChildren)
183
+ reduce_binary_plus_end(production, range, tokens, theChildren)
184
+ end
185
+
186
+ # rule('comparison' => 'term comparisonTest_plus')
187
+ def reduce_comparison_plus(production, range, tokens, theChildren)
188
+ reduce_binary_operator(production, range, tokens, theChildren)
189
+ end
190
+
191
+ # rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
192
+ # TODO: is it meaningful to implement this rule?
193
+
194
+ # rule('comparisonTest_plus' => 'comparisonTest term')
195
+ def reduce_comparison_t_plus_end(production, range, tokens, theChildren)
196
+ reduce_binary_plus_end(production, range, tokens, theChildren)
197
+ end
198
+
199
+ # rule('term' => 'factor additive_plus')
200
+ def reduce_term_additive(production, range, tokens, theChildren)
201
+ reduce_binary_operator(production, range, tokens, theChildren)
202
+ end
203
+
75
204
  # rule('additive_star' => 'additive_star additionOp factor').as 'additionOp_expr'
76
- def reduce_additionOp_expr(_production, _range, _tokens, theChildren)
77
- (operand1, operator_node, operand2) = theChildren
78
- operator = operator_node.symbol.name == 'MINUS' ? :- : :+
79
- LoxBinaryExpr.new(operator_node.token.position, operator, operand1, operand2)
205
+ def reduce_additive_plus_more(production, range, tokens, theChildren)
206
+ reduce_binary_plus_more(production, range, tokens, theChildren)
207
+ end
208
+
209
+ # rule('additive_plus' => 'additionOp factor')
210
+ def reduce_additive_plus_end(production, range, tokens, theChildren)
211
+ reduce_binary_plus_end(production, range, tokens, theChildren)
212
+ end
213
+
214
+ # rule('factor' => 'multiplicative_plus')
215
+ def reduce_factor_multiplicative(production, range, tokens, theChildren)
216
+ reduce_binary_operator(production, range, tokens, theChildren)
80
217
  end
81
218
 
82
- # rule('multiplicative_star' => 'multiplicative_star multOp unary').as 'multOp_expr'
83
- # def reduce_multOp_expr(_production, _range, _tokens, theChildren)
84
- # (operand1, operator, operand2) = theChildren
85
- # LoxBinaryExpr.new(operator.token.position, operator, operand1, operand2)
86
- # end
219
+ # rule('multiplicative_plus' => 'multiplicative_plus multOp unary')
220
+ def reduce_multiplicative_plus_more(production, range, tokens, theChildren)
221
+ reduce_binary_plus_more(production, range, tokens, theChildren)
222
+ end
223
+
224
+ # rule('multiplicative_plus' => 'multOp unary')
225
+ def reduce_multiplicative_plus_end(production, range, tokens, theChildren)
226
+ reduce_binary_plus_end(production, range, tokens, theChildren)
227
+ end
87
228
 
88
229
  # rule('primary' => 'FALSE' | TRUE').as 'literal_expr'
89
230
  def reduce_literal_expr(_production, _range, _tokens, theChildren)
@@ -39,75 +39,55 @@ module Loxxy
39
39
  top.accept(self)
40
40
  end
41
41
 
42
- # Visit event. The visitor is visiting the
43
- # given terminal node containing a datatype object.
44
- # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
45
- def visit_literal_expr(aLiteralExpr)
46
- broadcast(:before_literal_expr, aLiteralExpr)
47
- broadcast(:after_literal_expr, aLiteralExpr)
42
+ # Visit event. The visitor is about to visit the ptree.
43
+ # @param aParseTree [Rley::PTre::ParseTree] the ptree to visit.
44
+ def start_visit_ptree(aParseTree)
45
+ broadcast(:before_ptree, aParseTree)
48
46
  end
49
47
 
50
- =begin
51
-
48
+ # Visit event. The visitor has completed the visit of the ptree.
49
+ # @param aParseTree [Rley::PTre::ParseTree] the visited ptree.
50
+ def end_visit_ptree(aParseTree)
51
+ broadcast(:after_ptree, aParseTree)
52
+ end
52
53
 
53
- def visit_compound_datum(aCompoundDatum)
54
- broadcast(:before_compound_datum, aCompoundDatum)
55
- traverse_children(aCompoundDatum)
56
- broadcast(:after_compound_datum, aCompoundDatum)
54
+ # Visit event. The visitor is about to visit a print statement expression.
55
+ # @param aPrintStmt [AST::LOXPrintStmt] the print statement node to visit
56
+ def visit_print_stmt(aPrintStmt)
57
+ broadcast(:before_print_stmt, aPrintStmt)
58
+ traverse_subnodes(aPrintStmt)
59
+ broadcast(:after_print_stmt, aPrintStmt)
57
60
  end
58
61
 
59
62
  # Visit event. The visitor is visiting the
60
- # given empty list object.
61
- # @param anEmptyList [SkmEmptyList] the empty list object to visit.
62
- def visit_empty_list(anEmptyList)
63
- broadcast(:before_empty_list, anEmptyList)
64
- broadcast(:after_empty_list, anEmptyList)
63
+ # given terminal node containing a datatype object.
64
+ # @param aLiteralExpr [AST::LoxLiteralExpr] the leaf node to visit.
65
+ def visit_literal_expr(aLiteralExpr)
66
+ broadcast(:before_literal_expr, aLiteralExpr)
67
+ broadcast(:after_literal_expr, aLiteralExpr)
65
68
  end
66
69
 
67
- def visit_pair(aPair)
68
- broadcast(:before_pair, aPair)
69
- traverse_car_cdr(aPair)
70
- broadcast(:after_pair, aPair)
71
- end
72
- =end
73
- =begin
74
- # Visit event. The visitor is about to visit the given non terminal node.
75
- # @param aNonTerminalNode [NonTerminalNode] the node to visit.
76
- def visit_nonterminal(aNonTerminalNode)
77
- if @traversal == :post_order
78
- broadcast(:before_non_terminal, aNonTerminalNode)
79
- traverse_subnodes(aNonTerminalNode)
80
- else
81
- traverse_subnodes(aNonTerminalNode)
82
- broadcast(:before_non_terminal, aNonTerminalNode)
70
+ # Visit event. The visitor is about to visit the given non terminal node.
71
+ # @param aNonTerminalNode [Rley::PTre::NonTerminalNode] the node to visit.
72
+ def visit_nonterminal(_non_terminal_node)
73
+ # Loxxy interpreter encountered a CST node (Concrete Syntax Tree)
74
+ # that it cannot handle.
75
+ raise NotImplementedError, 'Loxxy cannot execute this code yet.'
83
76
  end
84
- broadcast(:after_non_terminal, aNonTerminalNode)
85
- end
86
- =end
87
77
 
88
78
  private
89
79
 
90
- def traverse_children(aParent)
91
- children = aParent.children
92
- broadcast(:before_children, aParent, children)
80
+ # Visit event. The visitor is about to visit the subnodes of a non
81
+ # terminal node.
82
+ # @param aParentNode [Ast::LocCompoundExpr] the parent node.
83
+ def traverse_subnodes(aParentNode)
84
+ subnodes = aParentNode.subnodes
85
+ broadcast(:before_subnodes, aParentNode, subnodes)
93
86
 
94
- # Let's proceed with the visit of children
95
- children.each { |a_child| a_child.accept(self) }
87
+ # Let's proceed with the visit of subnodes
88
+ subnodes.each { |a_node| a_node.accept(self) }
96
89
 
97
- broadcast(:after_children, aParent, children)
98
- end
99
-
100
- def traverse_car_cdr(aPair)
101
- if aPair.car
102
- broadcast(:before_car, aPair, aPair.car)
103
- aPair.car.accept(self)
104
- broadcast(:after_car, aPair, aPair.car)
105
- end
106
- if aPair.cdr
107
- broadcast(:before_cdr, aPair, aPair.cdr)
108
- aPair.cdr.accept(self)
109
- broadcast(:after_cdr, aPair, aPair.cdr)
110
- end
90
+ broadcast(:after_subnodes, aParentNode, subnodes)
111
91
  end
112
92
 
113
93
  # Send a notification to all subscribers.
@@ -117,7 +97,7 @@ module Loxxy
117
97
  subscribers.each do |subscr|
118
98
  next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
119
99
 
120
- subscr.send(msg, runtime, *args)
100
+ subscr.send(msg, *args)
121
101
  end
122
102
  end
123
103
  end # class