loxxy 0.0.4 → 0.0.9

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.
@@ -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
@@ -8,15 +8,14 @@ module Loxxy
8
8
  class BuiltinDatatype
9
9
  # @return [Object] The Ruby representation
10
10
  attr_reader :value
11
-
11
+
12
12
  # Constructor. Initialize a Lox value from one of its built-in data type.
13
13
  def initialize(aValue)
14
14
  @value = validated_value(aValue)
15
15
  end
16
-
17
-
16
+
18
17
  protected
19
-
18
+
20
19
  def validated_value(aValue)
21
20
  aValue
22
21
  end
@@ -8,13 +8,13 @@ module Loxxy
8
8
  # Class for representing a Lox false value.
9
9
  class False < Boolean
10
10
  include Singleton # Make a singleton class
11
-
11
+
12
12
  # Build the sole instance
13
13
  def initialize
14
14
  super(false)
15
15
  end
16
16
  end # class
17
-
18
- False.instance.freeze # Make the sole instance immutable
17
+
18
+ False.instance.freeze # Make the sole instance immutable
19
19
  end # module
20
20
  end # module
@@ -6,12 +6,6 @@ module Loxxy
6
6
  module Datatype
7
7
  # Class for representing a Lox string of characters value.
8
8
  class LXString < BuiltinDatatype
9
-
10
- # Build the sole instance
11
- def initialize(aValue)
12
- super(aValue)
13
- end
14
-
15
9
  # Compare a Lox String with another Lox (or genuine Ruby) String
16
10
  # @param other [Datatype::LxString, String]
17
11
  # @return [Boolean]
@@ -22,7 +16,8 @@ module Loxxy
22
16
  when String
23
17
  value == other
24
18
  else
25
- raise StandardError, 'Cannot compare a #{self.class} with #{other.class}'
19
+ err_msg = "Cannot compare a #{self.class} with #{other.class}"
20
+ raise StandardError, err_msg
26
21
  end
27
22
  end
28
23
 
@@ -34,8 +29,8 @@ module Loxxy
34
29
  end
35
30
 
36
31
  # Remove double quotes delimiter
37
- aValue.gsub(/(^")|("$)/, '')
32
+ aValue.gsub(/(^")|("$)/, '')
38
33
  end
39
34
  end # class
40
35
  end # module
41
- end # module
36
+ end # module
@@ -8,13 +8,13 @@ module Loxxy
8
8
  # Class for representing a Lox nil "value".
9
9
  class Nil < BuiltinDatatype
10
10
  include Singleton # Make a singleton class
11
-
11
+
12
12
  # Build the sole instance
13
13
  def initialize
14
14
  super(nil)
15
15
  end
16
16
  end # class
17
-
18
- Nil.instance.freeze # Make the sole instance immutable
17
+
18
+ Nil.instance.freeze # Make the sole instance immutable
19
19
  end # module
20
20
  end # module
@@ -6,19 +6,11 @@ module Loxxy
6
6
  module Datatype
7
7
  # Class for representing a Lox numeric value.
8
8
  class Number < BuiltinDatatype
9
-
10
- # Build the sole instance
11
- def initialize(aValue)
12
- super(aValue)
13
- end
14
-
15
9
  protected
16
10
 
17
11
  def validated_value(aValue)
18
12
  case aValue
19
- when Integer
20
- result = aValue
21
- when Numeric
13
+ when Integer, Numeric
22
14
  result = aValue
23
15
  when /^-?\d+$/
24
16
  result = aValue.to_i
@@ -32,4 +24,4 @@ module Loxxy
32
24
  end
33
25
  end # class
34
26
  end # module
35
- end # module
27
+ end # module
@@ -8,13 +8,13 @@ module Loxxy
8
8
  # Class for representing a Lox true value.
9
9
  class True < Boolean
10
10
  include Singleton # Make a singleton class
11
-
11
+
12
12
  # Build the sole instance
13
13
  def initialize
14
14
  super(true)
15
- end
15
+ end
16
16
  end # class
17
-
18
- True.instance.freeze # Make the sole instance immutable
17
+
18
+ True.instance.freeze # Make the sole instance immutable
19
19
  end # module
20
20
  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,53 +87,60 @@ 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').as 'comparison_plus'
106
+ rule('comparisonTest_plus' => 'comparisonTest_plus comparisonTest term').as 'comparison_t_plus_more'
107
+ rule('comparisonTest_plus' => 'comparisonTest term').as 'comparison_t_plus_end'
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').as 'term_additive'
114
+ rule('additive_plus' => 'additive_plus additionOp factor').as 'additive_plus_more'
115
+ rule('additive_plus' => 'additionOp factor').as 'additive_plus_end'
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' => 'unary multiplicative_plus').as 'factor_multiplicative'
120
+ rule('multiplicative_plus' => 'multiplicative_plus multOp unary').as 'multiplicative_plus_more'
121
+ rule('multiplicative_plus' => 'multOp unary').as 'multiplicative_plus_end'
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')
134
-
143
+
135
144
  # Utility rules
136
145
  rule('function' => 'IDENTIFIER LEFT_PAREN params_opt RIGHT_PAREN block')
137
146
  rule('params_opt' => 'parameters')
@@ -142,11 +151,12 @@ module Loxxy
142
151
  rule('arguments_opt' => [])
143
152
  rule('arguments' => 'arguments COMMA expression')
144
153
  rule('arguments' => 'expression')
145
-
146
154
  end
147
155
 
148
- # And now build the grammar and make it accessible via a constant
149
- # @return [Rley::Syntax::Grammar]
150
- Grammar = builder.grammar
156
+ unless defined?(Grammar)
157
+ # And now build the grammar and make it accessible via a constant
158
+ # @return [Rley::Syntax::Grammar]
159
+ Grammar = builder.grammar
160
+ end
151
161
  end # module
152
162
  end # module
@@ -14,7 +14,7 @@ module Loxxy
14
14
  # @param theLexeme [String] the lexeme (= piece of text from input)
15
15
  # @param aTerminal [Rley::Syntax::Terminal, String]
16
16
  # The terminal symbol corresponding to the lexeme.
17
- # @param aPosition [Rley::Lexical::Position] The position of lexeme
17
+ # @param aPosition [Rley::Lexical::Position] The position of lexeme
18
18
  # in input text.
19
19
  def initialize(aValue, aLexeme, aTerminal, aPosition)
20
20
  super(aLexeme, aTerminal, aPosition)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scanner'
4
+ require_relative 'grammar'
5
+ require_relative '../ast/ast_builder'
6
+
7
+ module Loxxy
8
+ module FrontEnd
9
+ # A Lox parser that produce concrete parse trees.
10
+ # Concrete parse trees are the default kind of parse tree
11
+ # generated by the Rley library.
12
+ # They consist of two node types only:
13
+ # - NonTerminalNode
14
+ # - TerminalNode
15
+ # A NonTerminalNode has zero or more child nodes (called subnodes)
16
+ # A TerminalNode is leaf node, that is, it has no child node.
17
+ # While concrete parse tree nodes can be generated out of the box,
18
+ # they have the following drawbacks:
19
+ # - Generic node classes that aren't always suited for the needs of
20
+ # the language being processing.
21
+ # - Concrete parse tree tend to be deeply nested, which may complicate
22
+ # further processing.
23
+ class Parser
24
+ # @return [Rley::Engine] A facade object for the Rley parsing library
25
+ attr_reader(:engine)
26
+
27
+ def initialize
28
+ # Create a Rley facade object
29
+ @engine = Rley::Engine.new do |cfg|
30
+ cfg.diagnose = true
31
+ cfg.repr_builder = Ast::ASTBuilder
32
+ end
33
+
34
+ # Step 1. Load Lox grammar
35
+ @engine.use_grammar(Loxxy::FrontEnd::Grammar)
36
+ end
37
+
38
+ # Parse the given Lox program into a parse tree.
39
+ # @param source [String] Lox program to parse
40
+ # @return [Rley::ParseTree] A parse tree equivalent to the Lox input.
41
+ def parse(source)
42
+ lexer = Scanner.new(source)
43
+ result = engine.parse(lexer.tokens)
44
+
45
+ unless result.success?
46
+ # Stop if the parse failed...
47
+ line1 = "Parsing failed\n"
48
+ line2 = "Reason: #{result.failure_reason.message}"
49
+ raise StandardError, line1 + line2
50
+ end
51
+
52
+ return engine.convert(result) # engine.to_ptree(result)
53
+ end
54
+ end # class
55
+ end # module
56
+ end # module
@@ -15,7 +15,7 @@ module Loxxy
15
15
  # A TerminalNode is leaf node, that is, it has no child node.
16
16
  # While concrete parse tree nodes can be generated out of the box,
17
17
  # they have the following drawbacks:
18
- # - Generic node classes that aren't always suited for the needs of
18
+ # - Generic node classes that aren't always suited for the needs of
19
19
  # the language being processing.
20
20
  # - Concrete parse tree tend to be deeply nested, which may complicate
21
21
  # further processing.
@@ -27,7 +27,6 @@ module Loxxy
27
27
  # Create a Rley facade object
28
28
  @engine = Rley::Engine.new do |cfg|
29
29
  cfg.diagnose = true
30
- # cfg.repr_builder = SkmBuilder
31
30
  end
32
31
 
33
32
  # Step 1. Load Lox grammar
@@ -52,4 +51,4 @@ module Loxxy
52
51
  end
53
52
  end # class
54
53
  end # module
55
- end # module
54
+ end # module