loxxy 0.0.3 → 0.0.4

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: 3f8733558204a56db1177c9c25e5fae5af1676c455b1e3a2f3145a70fae741a7
4
- data.tar.gz: ac99f27c25e52703adea30eca1db61671a275355a4a3a00657f2f0359e7fe57f
3
+ metadata.gz: f5383d6eb1725f5c53bb343e46d763f18d9d69f6b010241dbc4a93c18f949a6f
4
+ data.tar.gz: 286f213ab01ebec8061a9f25c86c6f28be064891880e06942a0d6d86b4210fcb
5
5
  SHA512:
6
- metadata.gz: beb8fa74fab5d2799ac982a2585a4ba8ebce992ae2b9a98d9356fe9e0afdd8d2b08d371bd9a6940e7b8761710f876c5494697a47ba1b4a047d7a38babb0d1695
7
- data.tar.gz: 64df1b2c919c47f8852bc2885e7dc2eac5bd55b5892f2f6477e56401a9e2d1cf01080c512d2592d07472ef8ff053ba26ac0b5c29278e9aefa57cea9f639c33ba
6
+ metadata.gz: 32d447c7dbe6593ad0002ad383a917561c82da22cbf70b4102d0eaa389cf92196e03ee14ab1cc160f530b1c96a1e259db8e7923e2de81322fc854898fa306d49
7
+ data.tar.gz: 245d4c9e255b7bb9d8602ab103367d7f83baf516db61f5a9192945ffa689027eb9f5f7a282e7052bca6b9e904cd6f6ed956d61ea949e02b7916f100033ebd9ec
@@ -1,3 +1,13 @@
1
+ ## [0.0.4] - 2021-01-01
2
+ - A first parser implementation able to parse Lox source code.
3
+
4
+ ## Added
5
+ - Method `LXString::==` equality operator.
6
+
7
+ ## Changed
8
+ - class `Parser` renamed to `RawParser`
9
+ - File `README.md` Added an example showing the use of `RawParser` class.
10
+
1
11
  ## [0.0.3] - 2020-12-29
2
12
  - Scanner can recognize strings and nil tokens
3
13
  ### Added
data/README.md CHANGED
@@ -11,11 +11,37 @@ a simple language used in Bob Nystrom's online book [Crafting Interpreters](http
11
11
 
12
12
  ## Roadmap
13
13
  - [DONE] Scanner (tokenizer)
14
- - [STARTED] Recognizer
15
- - [TODO] Parser
14
+ - [DONE] Raw parser. It parses Lox programs and generates a parse tree.
15
+ - [TODO] Tailored parser for generating AST (Abstract Syntax Tree)
16
16
  - [TODO] Interpreter or transpiler
17
17
 
18
18
 
19
+ ## Example
20
+ At this, stage, the raw parser is able to recognize Lox input and to generate
21
+ a concrete parse tree from it.
22
+
23
+ ```ruby
24
+ require 'loxxy'
25
+
26
+ lox_input = <<-LOX_END
27
+ // Your first Lox program!
28
+ print "Hello, world!";
29
+ LOX_END
30
+
31
+ # Show that the raw parser accepts the above program
32
+ base_parser = Loxxy::FrontEnd::RawParser.new
33
+
34
+ # Now parse the input into a concrete parse tree...
35
+ ptree = base_parser.parse(lox_input)
36
+
37
+ # Dump the parse tree contents...
38
+ p ptree # Lengthy (unwieldy) output
39
+ ```
40
+
41
+ Soon, the `loxxy`will host another, tailored parser, that will generate
42
+ abstract syntax tree which much more convenient for further processing
43
+ (like implementing an interpreter).
44
+
19
45
  ## Installation
20
46
 
21
47
  Add this line to your application's Gemfile:
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './loxxy/version'
3
+ require_relative 'loxxy/version'
4
+ require_relative 'loxxy/front_end/raw_parser'
4
5
 
5
6
  module Loxxy
6
7
  class Error < StandardError; end
@@ -12,6 +12,20 @@ module Loxxy
12
12
  super(aValue)
13
13
  end
14
14
 
15
+ # Compare a Lox String with another Lox (or genuine Ruby) String
16
+ # @param other [Datatype::LxString, String]
17
+ # @return [Boolean]
18
+ def ==(other)
19
+ case other
20
+ when LXString
21
+ value == other.value
22
+ when String
23
+ value == other
24
+ else
25
+ raise StandardError, 'Cannot compare a #{self.class} with #{other.class}'
26
+ end
27
+ end
28
+
15
29
  protected
16
30
 
17
31
  def validated_value(aValue)
@@ -5,7 +5,22 @@ require_relative 'grammar'
5
5
 
6
6
  module Loxxy
7
7
  module FrontEnd
8
- class Parser
8
+ # A Lox parser that produce concrete parse trees.
9
+ # Concrete parse trees are the default kind of parse tree
10
+ # generated by the Rley library.
11
+ # They consist of two node types only:
12
+ # - NonTerminalNode
13
+ # - TerminalNode
14
+ # A NonTerminalNode has zero or more child nodes (called subnodes)
15
+ # A TerminalNode is leaf node, that is, it has no child node.
16
+ # While concrete parse tree nodes can be generated out of the box,
17
+ # they have the following drawbacks:
18
+ # - Generic node classes that aren't always suited for the needs of
19
+ # the language being processing.
20
+ # - Concrete parse tree tend to be deeply nested, which may complicate
21
+ # further processing.
22
+ class RawParser
23
+ # @return [Rley::Engine] A facade object for the Rley parsing library
9
24
  attr_reader(:engine)
10
25
 
11
26
  def initialize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loxxy
4
- VERSION = '0.0.3'
4
+ VERSION = '0.0.4'
5
5
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+
5
+ # Load the class under test
6
+ require_relative '../../lib/loxxy/datatype/lx_string'
7
+
8
+ module Loxxy
9
+ module Datatype
10
+ describe LXString do
11
+ let(:sample_text) { 'some_text' }
12
+ subject { LXString.new(sample_text) }
13
+
14
+ context 'Initialization:' do
15
+ it 'should accept a String value at initialization' do
16
+ expect { LXString.new(sample_text) }.not_to raise_error
17
+ end
18
+
19
+ it 'should know its value' do
20
+ expect(subject.value).to eq(sample_text)
21
+ end
22
+ end
23
+
24
+ context 'Provided services:' do
25
+ it 'compares with another string' do
26
+ expect(subject).to eq(sample_text)
27
+ expect(subject).to eq(LXString.new(sample_text.dup))
28
+
29
+ expect(subject).not_to eq('')
30
+ expect(subject).not_to eq('other-text')
31
+ end
32
+ end
33
+ end # describe
34
+ end # module
35
+ end # module
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper' # Use the RSpec framework
4
+
5
+ # Load the class under test
6
+ require_relative '../../lib/loxxy/front_end/raw_parser'
7
+
8
+ module Loxxy
9
+ module FrontEnd
10
+ describe RawParser do
11
+ subject { RawParser.new }
12
+
13
+ context 'Initialization:' do
14
+ it 'should be initialized without argument' do
15
+ expect { RawParser.new }.not_to raise_error
16
+ end
17
+
18
+ it 'should have its parse engine initialized' do
19
+ expect(subject.engine).to be_kind_of(Rley::Engine)
20
+ end
21
+ end # context
22
+
23
+ context 'Parsing blank files:' do
24
+ def check_empty_input_result(aParseTree)
25
+ # Parse results MUST to comply to grammar rule:
26
+ # program => declaration_star EOF
27
+ # where the declaration_star MUST be empty
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
33
+ expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
34
+ expect(eof.symbol.name).to eq('EOF')
35
+ end
36
+
37
+ it 'should cope with an empty input' do
38
+ ptree = subject.parse('')
39
+ check_empty_input_result(ptree)
40
+ end
41
+
42
+ it 'should cope with whitespaces only input' do
43
+ ptree = subject.parse(' ' * 80 + "\n" * 20)
44
+ check_empty_input_result(ptree)
45
+ end
46
+
47
+ it 'should cope with comments only input' do
48
+ input = +''
49
+ %w[First Second Third].each do |ordinal|
50
+ input << "// #{ordinal} comment line\r\n"
51
+ end
52
+ ptree = subject.parse(input)
53
+ check_empty_input_result(ptree)
54
+ end
55
+ end # context
56
+
57
+ context 'Parsing expressions:' do
58
+ # Utility method to walk towards deeply nested node
59
+ # @param aNTNode [Rley::PTree::NonTerminalNode]
60
+ # @param subnodePath[Array<Integer>] An Array of subnode indices
61
+ def walk_subnodes(aNTNode, subnodePath)
62
+ curr_node = aNTNode
63
+ subnodePath.each do |index|
64
+ curr_node = curr_node.subnodes[index]
65
+ end
66
+
67
+ curr_node
68
+ end
69
+ it 'should parse a hello world program' do
70
+ program = <<-LOX_END
71
+ // Your first Lox program!
72
+ print "Hello, world!";
73
+ LOX_END
74
+ ptree = subject.parse(program)
75
+ root = ptree.root
76
+ expect(root.symbol.name).to eq('program')
77
+ (decls, eof) = root.subnodes
78
+ 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]
81
+ expect(stmt).to be_kind_of(Rley::PTree::NonTerminalNode)
82
+ expect(stmt.symbol.name).to eq('statement')
83
+ prnt_stmt = stmt.subnodes[0]
84
+ expect(prnt_stmt).to be_kind_of(Rley::PTree::NonTerminalNode)
85
+ expect(prnt_stmt.subnodes.size).to eq(3)
86
+ expect(prnt_stmt.subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
87
+ expect(prnt_stmt.subnodes[0].symbol.name).to eq('PRINT')
88
+ expect(prnt_stmt.subnodes[1]).to be_kind_of(Rley::PTree::NonTerminalNode)
89
+ leaf_node = walk_subnodes(prnt_stmt, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
90
+ expect(leaf_node).to be_kind_of(Rley::PTree::TerminalNode)
91
+ expect(leaf_node.symbol.name).to eq('STRING')
92
+ expect(leaf_node.token.value).to eq('Hello, world!')
93
+ expect(prnt_stmt.subnodes[2]).to be_kind_of(Rley::PTree::TerminalNode)
94
+ expect(prnt_stmt.subnodes[2].symbol.name).to eq('SEMICOLON')
95
+ end
96
+ end
97
+ end # describe
98
+ end # module
99
+ end # module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loxxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
@@ -93,11 +93,12 @@ files:
93
93
  - lib/loxxy/datatype/true.rb
94
94
  - lib/loxxy/front_end/grammar.rb
95
95
  - lib/loxxy/front_end/literal.rb
96
- - lib/loxxy/front_end/parser.rb
96
+ - lib/loxxy/front_end/raw_parser.rb
97
97
  - lib/loxxy/front_end/scanner.rb
98
98
  - lib/loxxy/version.rb
99
99
  - loxxy.gemspec
100
- - spec/front_end/parser_spec.rb
100
+ - spec/datatype/lx_string_spec.rb
101
+ - spec/front_end/raw_parser_spec.rb
101
102
  - spec/front_end/scanner_spec.rb
102
103
  - spec/loxxy_spec.rb
103
104
  - spec/spec_helper.rb
@@ -126,6 +127,7 @@ signing_key:
126
127
  specification_version: 4
127
128
  summary: An implementation of the Lox programming language. WIP
128
129
  test_files:
129
- - spec/front_end/parser_spec.rb
130
+ - spec/datatype/lx_string_spec.rb
131
+ - spec/front_end/raw_parser_spec.rb
130
132
  - spec/front_end/scanner_spec.rb
131
133
  - spec/loxxy_spec.rb
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../spec_helper' # Use the RSpec framework
4
- require_relative '../../lib/loxxy/front_end/parser' # Load the class under test
5
-
6
- module Loxxy
7
- module FrontEnd
8
- describe Parser do
9
- subject { Parser.new }
10
-
11
- context 'Initialization:' do
12
- it 'should be initialized without argument' do
13
- expect { Parser.new }.not_to raise_error
14
- end
15
-
16
- it 'should have its parse engine initialized' do
17
- expect(subject.engine).to be_kind_of(Rley::Engine)
18
- end
19
- end # context
20
-
21
- context 'Parsing blank files:' do
22
- def check_empty_input_result(aParseTree)
23
- # Parse results MUST to comply to grammar rule:
24
- # program => declaration_star EOF
25
- # where the declaration_star MUST be empty
26
- expect(aParseTree.root.symbol.name).to eq('program')
27
- (decls, eof) = aParseTree.root.subnodes
28
- expect(decls).to be_kind_of(Rley::PTree::NonTerminalNode)
29
- expect(decls.symbol.name).to eq('declaration_star')
30
- expect(decls.subnodes).to be_empty
31
- expect(eof).to be_kind_of(Rley::PTree::TerminalNode)
32
- expect(eof.symbol.name).to eq('EOF')
33
- end
34
-
35
- it 'should cope with an empty input' do
36
- ptree = subject.parse('')
37
- check_empty_input_result(ptree)
38
- end
39
-
40
- it 'should cope with whitespaces only input' do
41
- ptree = subject.parse(' ' * 80 + "\n" * 20)
42
- check_empty_input_result(ptree)
43
- end
44
-
45
- it 'should cope with comments only input' do
46
- input = +''
47
- %w[First Second Third].each do |ordinal|
48
- input << "// #{ordinal} comment line\r\n"
49
- end
50
- ptree = subject.parse(input)
51
- check_empty_input_result(ptree)
52
- end
53
- end # context
54
- end # describe
55
- end # module
56
- end # module