loxxy 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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