loxxy 0.0.6 → 0.0.7

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: de9d3c24db15863c6ea99c2a9c8a2e413b2a243ddf79de28da5583d1944fc4e4
4
- data.tar.gz: a5f6dacfd67b6833e3c051273765e1d94c95d1d1700a974a09a3caf0519a1570
3
+ metadata.gz: d1b591e9ed6ae88b4b4232c01c4f08fd4453fb499a665c368cd64ce2135a6718
4
+ data.tar.gz: 6bc1f01f9f0a963a2781271c46391e2c2801b61cc1b076e0b79be30450bee2af
5
5
  SHA512:
6
- metadata.gz: 3c4bd62b1a9cd960ee97d807a5f44a26cc360df4344975210ca967117c62585a2138fef382ffb5ea7d79f002b9a9a951ba77622cb8d799973c0ad8a24ffbae52
7
- data.tar.gz: 5b14b5c4a6ae98f5e1d9a819160393393654cc3d817e36f1da68cd12fcfedfbce47097463dc6babcf36b5582e970ea41b4cef5ebceb768b962c8bd7b02bcb553
6
+ metadata.gz: 5dd208921fc139eb5f48b182abbe64b919c514904dfab073b09c4a9656989db5c95a5e1bd6f72733a46af8351dc6f95ab94612d8016c22e13dbcedd4637db90b
7
+ data.tar.gz: 63ac04a1cd79335daf5dbb38de2615e89044c49ec74fd174200149ef24d829861c3af0e7bf62862cbffc03940b955a6c9336344696d432e4efaa83d2dc1e9d35
@@ -170,6 +170,9 @@ Naming/BlockParameterName:
170
170
 
171
171
  Naming/MethodParameterName:
172
172
  Enabled: false
173
+
174
+ Naming/MethodName:
175
+ Enabled: false
173
176
 
174
177
  Naming/VariableName:
175
178
  Enabled: false
@@ -1,3 +1,19 @@
1
+ ## [0.0.7] - 2021-01-06
2
+ - Lox grammar reworked, initial AST classes created.
3
+
4
+ ## Added
5
+ - Class `Parser` this one generates AST's (Abstract Syntax Tree)
6
+ - Class `AST::ASTVisitor` draft initial implementation.
7
+ - Class `AST::BinaryExpr` draft initial implementation.
8
+ - Class `AST::LoxCompoundExpr` draft initial implementation.
9
+ - Class `AST::LiteralExpr` draft initial implementation.
10
+ - Class `AST::LoxNode` draft initial implementation.
11
+
12
+ ## Changed
13
+ - File `spec_helper.rb`: removed Bundler dependency
14
+ - Class `AST::ASTBuilder` added initial `reduce_` methods.
15
+ - File `README.md` Removed example with AST generation since this is in flux.
16
+
1
17
  ## [0.0.6] - 2021-01-03
2
18
  - First iteration of a parser with AST generation.
3
19
 
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Loxxy
2
2
 
3
- A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html),
4
- a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/).
3
+ A Ruby implementation of the [Lox programming language](https://craftinginterpreters.com/the-lox-language.html ),
4
+ a simple language used in Bob Nystrom's online book [Crafting Interpreters](https://craftinginterpreters.com/ ).
5
5
 
6
6
  ## Purpose of this project:
7
7
  - To deliver an open source example of a programming language fully implemented in Ruby
@@ -14,7 +14,7 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
14
14
  - [DONE] Raw parser. It parses Lox programs and generates a parse tree.
15
15
  - [DONE] Tailored parser for generating AST (Abstract Syntax Tree)
16
16
  - [STARTED] Custom AST builder class
17
- - [TODO] Hierarchy classes for representing Lox expressions in AST
17
+ - [STARTED] Hierarchy classes for representing Lox expressions in AST
18
18
  - [TODO] Interpreter or transpiler
19
19
 
20
20
  ## Example
@@ -48,8 +48,7 @@ tree_formatter.render(visitor)
48
48
  This is the output produced by the above example:
49
49
  ```
50
50
  program
51
- +-- declaration_star
52
- | +-- declaration_star
51
+ +-- declaration_plus
53
52
  | +-- declaration
54
53
  | +-- statement
55
54
  | +-- printStmt
@@ -58,69 +57,18 @@ program
58
57
  | | +-- assignment
59
58
  | | +-- logic_or
60
59
  | | +-- logic_and
61
- | | | +-- equality
62
- | | | | +-- comparison
63
- | | | | | +-- term
64
- | | | | | | +-- factor
65
- | | | | | | | +-- unary
66
- | | | | | | | | +-- call
67
- | | | | | | | | +-- primary
68
- | | | | | | | | | +-- STRING: '"Hello, world!"'
69
- | | | | | | | | +-- refinement_star
70
- | | | | | | | +-- multiplicative_star
71
- | | | | | | +-- additive_star
72
- | | | | | +-- comparisonTest_star
73
- | | | | +-- equalityTest_star
74
- | | | +-- conjunct_star
75
- | | +-- disjunct_star
60
+ | | +-- equality
61
+ | | +-- comparison
62
+ | | +-- term
63
+ | | +-- factor
64
+ | | +-- unary
65
+ | | +-- call
66
+ | | +-- primary
67
+ | | +-- STRING: '"Hello, world!"'
76
68
  | +-- SEMICOLON: ';'
77
69
  +-- EOF: ''
78
70
  ```
79
71
 
80
- ### Example using Parser class
81
- Except for the `Parser` class name, this example is identical
82
- to the previous one.
83
-
84
- ```ruby
85
- require 'loxxy'
86
-
87
-
88
- lox_input = <<-LOX_END
89
- // Your first Lox program!
90
- print "Hello, world!";
91
- LOX_END
92
-
93
- # Show that the parser accepts the above program
94
- parser = Loxxy::FrontEnd::Parser.new
95
-
96
- # Now parse the input into an abstract parse tree...
97
- ptree = parser.parse(lox_input)
98
-
99
- # Display the parse tree thanks to Rley utility classes...
100
- visitor = Rley::ParseTreeVisitor.new(ptree)
101
- tree_formatter = Rley::Formatter::Asciitree.new($stdout)
102
- tree_formatter.render(visitor)
103
- ```
104
-
105
- However the output differs significantly:
106
- ```
107
- program
108
- +-- declaration_star
109
- | +-- printStmt
110
- | +-- PRINT: 'print'
111
- | +-- logic_or
112
- | | +-- logic_and
113
- | | +-- equality
114
- | | +-- comparison
115
- | | +-- term
116
- | | +-- factor
117
- | | +-- call
118
- | | +-- STRING: '"Hello, world!"'
119
- | +-- SEMICOLON: ';'
120
- +-- EOF: ''
121
- ```
122
-
123
-
124
72
 
125
73
  ## Installation
126
74
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../datatype/all_datatypes'
4
+ require_relative 'lox_literal_expr'
5
+ require_relative 'lox_binary_expr'
4
6
 
5
7
  module Loxxy
6
8
  module Ast
@@ -69,6 +71,27 @@ module Loxxy
69
71
 
70
72
  node
71
73
  end
74
+
75
+ # 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)
80
+ end
81
+
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
87
+
88
+ # rule('primary' => 'FALSE' | TRUE').as 'literal_expr'
89
+ def reduce_literal_expr(_production, _range, _tokens, theChildren)
90
+ first_child = theChildren.first
91
+ pos = first_child.token.position
92
+ literal = first_child.token.value
93
+ LoxLiteralExpr.new(pos, literal)
94
+ end
72
95
  end # class
73
96
  end # module
74
97
  end # module
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loxxy
4
+ module Ast
5
+ class ASTVisitor
6
+ # Link to the top node to visit
7
+ attr_reader(:top)
8
+
9
+ # List of objects that subscribed to the visit event notification.
10
+ attr_reader(:subscribers)
11
+
12
+ # attr_reader(:runtime)
13
+
14
+ # Build a visitor for the given top.
15
+ # @param aRoot [AST::LoxNode] the parse tree to visit.
16
+ def initialize(aTop)
17
+ raise StandardError if aTop.nil?
18
+
19
+ @top = aTop
20
+ @subscribers = []
21
+ end
22
+
23
+ # Add a subscriber for the visit event notifications.
24
+ # @param aSubscriber [Object]
25
+ def subscribe(aSubscriber)
26
+ subscribers << aSubscriber
27
+ end
28
+
29
+ # Remove the given object from the subscription list.
30
+ # The object won't be notified of visit events.
31
+ # @param aSubscriber [Object]
32
+ def unsubscribe(aSubscriber)
33
+ subscribers.delete_if { |entry| entry == aSubscriber }
34
+ end
35
+
36
+ # The signal to begin the visit of the top.
37
+ def start # (aRuntime)
38
+ # @runtime = aRuntime
39
+ top.accept(self)
40
+ end
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)
48
+ end
49
+
50
+ =begin
51
+
52
+
53
+ def visit_compound_datum(aCompoundDatum)
54
+ broadcast(:before_compound_datum, aCompoundDatum)
55
+ traverse_children(aCompoundDatum)
56
+ broadcast(:after_compound_datum, aCompoundDatum)
57
+ end
58
+
59
+ # 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)
65
+ end
66
+
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)
83
+ end
84
+ broadcast(:after_non_terminal, aNonTerminalNode)
85
+ end
86
+ =end
87
+
88
+ private
89
+
90
+ def traverse_children(aParent)
91
+ children = aParent.children
92
+ broadcast(:before_children, aParent, children)
93
+
94
+ # Let's proceed with the visit of children
95
+ children.each { |a_child| a_child.accept(self) }
96
+
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
111
+ end
112
+
113
+ # Send a notification to all subscribers.
114
+ # @param msg [Symbol] event to notify
115
+ # @param args [Array] arguments of the notification.
116
+ def broadcast(msg, *args)
117
+ subscribers.each do |subscr|
118
+ next unless subscr.respond_to?(msg) || subscr.respond_to?(:accept_all)
119
+
120
+ subscr.send(msg, runtime, *args)
121
+ end
122
+ end
123
+ end # class
124
+ end # module
125
+ end # module
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_compound_expr'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxBinaryExpr < LoxCompoundExpr
8
+ # @return [Symbol]
9
+ attr_reader :operator
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param operand1 [Loxxy::Ast::LoxNode]
13
+ # @param operand2 [Loxxy::Ast::LoxNode]
14
+ def initialize(aPosition, anOperator, operand1, operand2)
15
+ super(aPosition, [operand1, operand2])
16
+ @operator = anOperator
17
+ end
18
+
19
+ alias operands subnodes
20
+ end # class
21
+ end # module
22
+ end # module
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_node'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxCompoundExpr < LoxNode
8
+ # @return [Array<Ast::LoxNode>]
9
+ attr_reader :subnodes
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param theSubnodes [Array<Ast::LoxNode>]
13
+ def initialize(aPosition, theSubnodes)
14
+ super(aPosition)
15
+ @subnodes = theSubnodes
16
+ end
17
+
18
+ # Part of the 'visitee' role in Visitor design pattern.
19
+ # @param visitor [ASTVisitor] the visitor
20
+ def accept(visitor)
21
+ visitor.visit_compound_expr(self)
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lox_node'
4
+
5
+ module Loxxy
6
+ module Ast
7
+ class LoxLiteralExpr < LoxNode
8
+ # @return [Loxxy::Datatype::BuiltinDatatype]
9
+ attr_reader :literal
10
+
11
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
12
+ # @param aLiteral [Loxxy::Datatype::BuiltinDatatype]
13
+ def initialize(aPosition, aLiteral)
14
+ super(aPosition)
15
+ @literal = aLiteral
16
+ end
17
+
18
+ # Part of the 'visitee' role in Visitor design pattern.
19
+ # @param visitor [ASTVisitor] the visitor
20
+ def accept(visitor)
21
+ visitor.visit_literal_expr(self)
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loxxy
4
+ module Ast
5
+ class LoxNode
6
+ # return [Rley::Lexical::Position] Position of the entry in the input stream.
7
+ attr_reader :position
8
+
9
+ # @param aPosition [Rley::Lexical::Position] Position of the entry in the input stream.
10
+ def initialize(aPosition)
11
+ @position = aPosition
12
+ end
13
+
14
+ # Abstract method.
15
+ # Part of the 'visitee' role in Visitor design pattern.
16
+ # @param _visitor [LoxxyTreeVisitor] the visitor
17
+ def accept(_visitor)
18
+ raise NotImplementedError
19
+ end
20
+ end # class
21
+ end # module
22
+ end # module
@@ -26,11 +26,12 @@ module Loxxy
26
26
  add_terminals('EOF')
27
27
 
28
28
  # Top-level rule that matches an entire Lox program
29
- rule('program' => 'declaration_star EOF')
29
+ rule('program' => 'EOF')
30
+ rule('program' => 'declaration_plus EOF')
30
31
 
31
32
  # Declarations: bind an identifier to something
32
- rule('declaration_star' => 'declaration_star declaration')
33
- rule('declaration_star' => [])
33
+ rule('declaration_plus' => 'declaration_plus declaration')
34
+ rule('declaration_plus' => 'declaration')
34
35
  rule('declaration' => 'classDecl')
35
36
  rule('declaration' => 'funDecl')
36
37
  rule('declaration' => 'varDecl')
@@ -75,7 +76,8 @@ module Loxxy
75
76
  rule('printStmt' => 'PRINT expression SEMICOLON')
76
77
  rule('returnStmt' => 'RETURN expression_opt SEMICOLON')
77
78
  rule('whileStmt' => 'WHILE LEFT_PAREN expression RIGHT_PAREN statement')
78
- rule('block' => 'LEFT_BRACE declaration_star RIGHT_BRACE')
79
+ rule('block' => 'LEFT_BRACE declaration_plus RIGHT_BRACE')
80
+ rule('block' => 'LEFT_BRACE RIGHT_BRACE')
79
81
 
80
82
  # Expressions: produce values
81
83
  rule('expression_opt' => 'expression')
@@ -85,49 +87,56 @@ module Loxxy
85
87
  rule('assignment' => 'logic_or')
86
88
  rule('owner_opt' => 'call DOT')
87
89
  rule('owner_opt' => [])
88
- rule('logic_or' => 'logic_and disjunct_star')
89
- rule('disjunct_star' => 'disjunct_star OR logic_and')
90
- rule('disjunct_star' => [])
91
- rule('logic_and' => 'equality conjunct_star')
92
- rule('conjunct_star' => 'conjunct_star AND equality')
93
- rule('conjunct_star' => [])
94
- rule('equality' => 'comparison equalityTest_star')
95
- rule('equalityTest_star' => 'equalityTest_star equalityTest comparison')
96
- rule('equalityTest_star' => [])
90
+ rule('logic_or' => 'logic_and')
91
+ rule('logic_or' => 'logic_and disjunct_plus')
92
+ rule('disjunct_plus' => 'disjunct_plus OR logic_and')
93
+ rule('disjunct_plus' => 'OR logic_and')
94
+ rule('logic_and' => 'equality')
95
+ rule('logic_and' => 'equality conjunct_plus')
96
+ rule('conjunct_plus' => 'conjunct_plus AND equality')
97
+ rule('conjunct_' => 'AND equality')
98
+ rule('equality' => 'comparison')
99
+ rule('equality' => 'comparison equalityTest_plus')
100
+ rule('equalityTest_plus' => 'equalityTest_plus equalityTest comparison')
101
+ rule('equalityTest_star' => 'equalityTest comparison')
97
102
  rule('equalityTest' => 'BANG_EQUAL')
98
103
  rule('equalityTest' => 'EQUAL_EQUAL')
99
- rule('comparison' => 'term comparisonTest_star')
100
- rule('comparisonTest_star' => 'comparisonTest_star comparisonTest term')
101
- rule('comparisonTest_star' => [])
104
+ rule('comparison' => 'term')
105
+ rule('comparison' => 'term comparisonTest_plus')
106
+ rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term')
107
+ rule('comparisonTest_plus' => 'comparisonTest term')
102
108
  rule('comparisonTest' => 'GREATER')
103
109
  rule('comparisonTest' => 'GREATER_EQUAL')
104
110
  rule('comparisonTest' => 'LESS')
105
111
  rule('comparisonTest' => 'LESS_EQUAL')
106
- rule('term' => 'factor additive_star')
107
- rule('additive_star' => 'additive_star additionOp factor')
108
- rule('additive_star' => [])
112
+ rule('term' => 'factor')
113
+ rule('term' => 'factor additive_plus')
114
+ rule('additive_plus' => 'additive_plus additionOp factor').as 'additionOp_expr'
115
+ rule('additive_plus' => 'additionOp factor')
109
116
  rule('additionOp' => 'MINUS')
110
117
  rule('additionOp' => 'PLUS')
111
- rule('factor' => 'unary multiplicative_star')
112
- rule('multiplicative_star' => 'multiplicative_star multOp unary')
113
- rule('multiplicative_star' => [])
118
+ rule('factor' => 'unary')
119
+ rule('factor' => 'multiplicative_plus')
120
+ rule('multiplicative_plus' => 'multiplicative_plus multOp unary') # .as 'multOp_expr'
121
+ rule('multiplicative_plus' => 'multOp unary')
114
122
  rule('multOp' => 'SLASH')
115
123
  rule('multOp' => 'STAR')
116
124
  rule('unary' => 'unaryOp unary')
117
125
  rule('unary' => 'call')
118
126
  rule('unaryOp' => 'BANG')
119
127
  rule('unaryOp' => 'MINUS')
120
- rule('call' => 'primary refinement_star')
121
- rule('refinement_star' => 'refinement_star refinement')
122
- rule('refinement_star' => [])
128
+ rule('call' => 'primary')
129
+ rule('call' => 'primary refinement_plus')
130
+ rule('refinement_plus' => 'refinement_plus refinement')
131
+ rule('refinement_plus' => 'refinement')
123
132
  rule('refinement' => 'LEFT_PAREN arguments_opt RIGHT_PAREN')
124
133
  rule('refinement' => 'DOT IDENTIFIER')
125
- rule('primary' => 'TRUE')
126
- rule('primary' => 'FALSE')
127
- rule('primary' => 'NIL')
134
+ rule('primary' => 'TRUE').as 'literal_expr'
135
+ rule('primary' => 'FALSE').as 'literal_expr'
136
+ rule('primary' => 'NIL').as 'literal_expr'
128
137
  rule('primary' => 'THIS')
129
- rule('primary' => 'NUMBER')
130
- rule('primary' => 'STRING')
138
+ rule('primary' => 'NUMBER').as 'literal_expr'
139
+ rule('primary' => 'STRING').as 'literal_expr'
131
140
  rule('primary' => 'IDENTIFIER')
132
141
  rule('primary' => 'LEFT_PAREN expression RIGHT_PAREN')
133
142
  rule('primary' => 'SUPER DOT IDENTIFIER')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.6'
4
+ VERSION = '0.0.7'
5
5
  end
@@ -10,6 +10,18 @@ module Loxxy
10
10
  describe Parser do
11
11
  subject { Parser.new }
12
12
 
13
+ # Utility method to walk towards deeply nested node
14
+ # @param aNTNode [Rley::PTree::NonTerminalNode]
15
+ # @param subnodePath[Array<Integer>] An Array of subnode indices
16
+ def walk_subnodes(aNTNode, subnodePath)
17
+ curr_node = aNTNode
18
+ subnodePath.each do |index|
19
+ curr_node = curr_node.subnodes[index]
20
+ end
21
+
22
+ curr_node
23
+ end
24
+
13
25
  context 'Initialization:' do
14
26
  it 'should be initialized without argument' do
15
27
  expect { Parser.new }.not_to raise_error
@@ -25,11 +37,7 @@ module Loxxy
25
37
  # Parse results MUST to comply to grammar rule:
26
38
  # program => declaration_star EOF
27
39
  # where the declaration_star MUST be empty
28
- expect(aParseTree.root.symbol.name).to eq('program')
29
- expect(aParseTree.root.subnodes.size).to eq(1)
30
- eof = aParseTree.root.subnodes[0]
31
- expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
32
- expect(eof.symbol.name).to eq('EOF')
40
+ expect(aParseTree.root.symbol.name).to eq('EOF')
33
41
  end
34
42
 
35
43
  it 'should cope with an empty input' do
@@ -52,18 +60,59 @@ module Loxxy
52
60
  end
53
61
  end # context
54
62
 
55
- context 'Parsing expressions:' do
56
- # Utility method to walk towards deeply nested node
57
- # @param aNTNode [Rley::PTree::NonTerminalNode]
58
- # @param subnodePath[Array<Integer>] An Array of subnode indices
59
- def walk_subnodes(aNTNode, subnodePath)
60
- curr_node = aNTNode
61
- subnodePath.each do |index|
62
- curr_node = curr_node.subnodes[index]
63
+ context 'Parsing literals:' do
64
+ it 'should parse a false literal' do
65
+ input = 'false;'
66
+ ptree = subject.parse(input)
67
+ leaf = walk_subnodes(ptree.root, [0, 0])
68
+ expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
69
+ expect(leaf.literal).to be_equal(Datatype::False.instance)
70
+ end
71
+
72
+ it 'should parse a true literal' do
73
+ input = 'true;'
74
+ ptree = subject.parse(input)
75
+ leaf = walk_subnodes(ptree.root, [0, 0])
76
+ expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
77
+ expect(leaf.literal).to be_equal(Datatype::True.instance)
78
+ end
79
+
80
+ it 'should parse number literals' do
81
+ inputs = %w[1234; 12.34;]
82
+ inputs.each do |source|
83
+ ptree = subject.parse(source)
84
+ leaf = walk_subnodes(ptree.root, [0, 0])
85
+ expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
86
+ expect(leaf.literal).to be_kind_of(Datatype::Number)
87
+ expect(leaf.literal.value).to eq(source.to_f)
63
88
  end
89
+ end
64
90
 
65
- curr_node
91
+ it 'should parse string literals' do
92
+ inputs = [
93
+ '"I am a string";',
94
+ '"";',
95
+ '"123";'
96
+ ]
97
+ inputs.each do |source|
98
+ ptree = subject.parse(source)
99
+ leaf = walk_subnodes(ptree.root, [0, 0])
100
+ expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
101
+ expect(leaf.literal).to be_kind_of(Datatype::LXString)
102
+ expect(leaf.literal.value).to eq(source.gsub(/(^")|(";$)/, ''))
103
+ end
66
104
  end
105
+
106
+ it 'should parse a nil literal' do
107
+ input = 'nil;'
108
+ ptree = subject.parse(input)
109
+ leaf = walk_subnodes(ptree.root, [0, 0])
110
+ expect(leaf).to be_kind_of(Ast::LoxLiteralExpr)
111
+ expect(leaf.literal).to be_equal(Datatype::Nil.instance)
112
+ end
113
+ end
114
+
115
+ context 'Parsing expressions:' do
67
116
  it 'should parse a hello world program' do
68
117
  program = <<-LOX_END
69
118
  // Your first Lox program!
@@ -72,25 +121,31 @@ LOX_END
72
121
  ptree = subject.parse(program)
73
122
  root = ptree.root
74
123
  expect(root.symbol.name).to eq('program')
75
- (decls, eof) = root.subnodes
76
- expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
77
- expect(decls.symbol.name).to eq('declaration_star')
78
- prnt_stmt = decls.subnodes[0]
124
+ (prnt_stmt, eof) = root.subnodes
79
125
  expect(prnt_stmt).to be_kind_of(Rley::PTree::NonTerminalNode)
126
+ expect(prnt_stmt.symbol.name).to eq('printStmt')
80
127
  expect(prnt_stmt.subnodes.size).to eq(3)
81
128
  expect(prnt_stmt.subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
82
129
  expect(prnt_stmt.subnodes[0].symbol.name).to eq('PRINT')
83
- expect(prnt_stmt.subnodes[1]).to be_kind_of(Rley::PTree::NonTerminalNode)
84
- # leaf_node = walk_subnodes(prnt_stmt, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
85
- # expect(leaf_node).to be_kind_of(Rley::PTree::TerminalNode)
86
- # expect(leaf_node.symbol.name).to eq('STRING')
87
- # expect(leaf_node.token.value).to eq('Hello, world!')
130
+ expect(prnt_stmt.subnodes[1]).to be_kind_of(Loxxy::Ast::LoxLiteralExpr)
131
+ expect(prnt_stmt.subnodes[1].literal).to be_kind_of(Loxxy::Datatype::LXString)
132
+ expect(prnt_stmt.subnodes[1].literal.value).to eq('Hello, world!')
88
133
  expect(prnt_stmt.subnodes[2]).to be_kind_of(Rley::PTree::TerminalNode)
89
134
  expect(prnt_stmt.subnodes[2].symbol.name).to eq('SEMICOLON')
90
135
  expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
91
136
  expect(eof.symbol.name).to eq('EOF')
92
137
  end
93
- end
138
+ end # context
139
+
140
+ context 'Parsing literals:' do
141
+ it 'should parse the addition of two number literals' do
142
+ # input = '123 + 456;'
143
+ # ptree = subject.parse(input)
144
+ # term = walk_subnodes(ptree.root, [0, 0, 0, 0, 0, 0, 0])
145
+ # expect(leaf).to be_kind_of(Ast::LoxBinaryExpr)
146
+ # expect(leaf.literal).to be_equal(Datatype::False.instance)
147
+ end
148
+ end # context
94
149
  end # describe
95
150
  end # module
96
151
  end # module
@@ -26,10 +26,7 @@ module Loxxy
26
26
  # program => declaration_star EOF
27
27
  # where the declaration_star MUST be empty
28
28
  expect(aParseTree.root.symbol.name).to eq('program')
29
- (decls, eof) = aParseTree.root.subnodes
30
- expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
31
- expect(decls.symbol.name).to eq('declaration_star')
32
- expect(decls.subnodes).to be_empty
29
+ eof = aParseTree.root.subnodes.first
33
30
  expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
34
31
  expect(eof.symbol.name).to eq('EOF')
35
32
  end
@@ -76,8 +73,8 @@ LOX_END
76
73
  expect(root.symbol.name).to eq('program')
77
74
  (decls, eof) = root.subnodes
78
75
  expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
79
- expect(decls.symbol.name).to eq('declaration_star')
80
- stmt = decls.subnodes[1].subnodes[0]
76
+ expect(decls.symbol.name).to eq('declaration_plus')
77
+ stmt = decls.subnodes[0].subnodes[0]
81
78
  expect(stmt).to be_kind_of(Rley::PTree::NonTerminalNode)
82
79
  expect(stmt.symbol.name).to eq('statement')
83
80
  prnt_stmt = stmt.subnodes[0]
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.6
4
+ version: 0.0.7
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-03 00:00:00.000000000 Z
11
+ date: 2021-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rley
@@ -85,6 +85,11 @@ files:
85
85
  - Rakefile
86
86
  - lib/loxxy.rb
87
87
  - lib/loxxy/ast/ast_builder.rb
88
+ - lib/loxxy/ast/ast_visitor.rb
89
+ - lib/loxxy/ast/lox_binary_expr.rb
90
+ - lib/loxxy/ast/lox_compound_expr.rb
91
+ - lib/loxxy/ast/lox_literal_expr.rb
92
+ - lib/loxxy/ast/lox_node.rb
88
93
  - lib/loxxy/datatype/all_datatypes.rb
89
94
  - lib/loxxy/datatype/boolean.rb
90
95
  - lib/loxxy/datatype/builtin_datatype.rb