rley 0.4.03 → 0.4.04

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
  SHA1:
3
- metadata.gz: 2d13db61565b20ef542592a59bcd056d4dd2192e
4
- data.tar.gz: a8d8c12a600d9621d6be682570cb165d526641ef
3
+ metadata.gz: 5100723b47c2572c2ac0b5996413eb4cd8e462ba
4
+ data.tar.gz: 4d37be9ca7128a36f77505560c7214454e413eb5
5
5
  SHA512:
6
- metadata.gz: 679e3f7d6747264092f317521e1f8f9e241ad912de71280c0c6d68454110bd3ba75fdc6f1f4a9c76ac61622ed87fba86f2662e3f15b5cd9a0e2d571b340e2988
7
- data.tar.gz: edb83ffb5fb673f27abef6c7ef10014fb8c6c312de093251f6ce509e2893c20cf5114cb77abf1b97c1406d9d22d474b0ab577829acb9d59f58117e43e96eaef2
6
+ metadata.gz: c197b1b962408e3a3ce4be5d310c227bdc638028d9a40d26b89c544232f59a8ce994228e4ae3673cbe6147dbcd520ee53c36a0620e3c7871edd3aba46762933f
7
+ data.tar.gz: 21b59e0b9b5c942be83954b35ee15650e1bc2c2aa2f9a6f1ebba645b94e3f822df510026825354512cf7eea879431feed852f34cef9fd176c5d0b89377504a46
@@ -1,8 +1,15 @@
1
+ ### 0.4.04 / 2017-05-01
2
+ * [NEW] `Asciitree` formatter class. Allows parse tree output in simple printable text.
3
+ * [CHANGE] Major enhancements in directory `examples\data_formats\JSON`. The demo command-line tool parses JSON and outputs the parse tree in one of the supported formats.
4
+
1
5
  ### 0.4.03 / 2017-04-17
2
6
  * [FIX] File `rley.rb` reference to obsolete `EarleyParser` class removed.
3
7
  * [NEW] `BracketNotation` formatter class. Allows parse tree output in Labelled Bracket Notation.
4
8
  * [CHANGE] Code refactoring in directory `examples\data_formats\JSON`. The demo command-line tool parses JSON and converts it into LBN (Labelled Bracket Notation). There are two diagrams (in SVG format) generated from the LBN output.
5
9
 
10
+ ### 0.4.02 / 2017-04-09
11
+ * [NEW] Module re-organization for clearer dependencies: Classes `Token` and `TokenRange` are moved to a separate module `Tokens`.
12
+ * [CHANGE] Code, specs, examples and `README.md` adapted to reflect the module re-organization.
6
13
 
7
14
  ### 0.4.01 / 2016-12-21
8
15
  * [NEW] File `appveyor.yml`. Add AppVeyor CI to Github commits. AppVeyor complements Travis by running builds under Windows OS.
@@ -1,32 +1,23 @@
1
+ require_relative 'cli_options'
1
2
  require_relative 'JSON_parser'
2
3
 
3
- # Create a JSON parser object
4
- parser = JSONParser.new
4
+ prog_name = 'JSON_demo'
5
+ prog_version = '0.1.0'
5
6
 
6
- # Parse the input file with name given in command-line
7
+ cli_options = CLIOptions.new(prog_name, prog_version, ARGV)
7
8
  if ARGV.empty?
8
- msg = <<-END_MSG
9
- A demo utility that converts a JSON file into labelled square notation (LBN).
10
- Use online tools (e.g. http://yohasebe.com/rsyntaxtree/) to visualize
11
- parse trees from LBN output.
12
-
13
- Command-line syntax:
14
-
15
- ruby #{__FILE__} filename
16
- where:
17
- filename is the name of a JSON file
18
-
19
- Example:
20
- ruby #{__FILE__} sample01.json
21
- END_MSG
22
- puts msg
9
+ puts 'Missing input file name.'
10
+ puts 'Use -h option for command-line help.'
23
11
  exit(1)
24
12
  end
13
+
25
14
  file_name = ARGV[0]
15
+ # Create a JSON parser object
16
+ parser = JSONParser.new
26
17
  result = parser.parse_file(file_name) # result object contains parse details
27
18
 
28
19
  unless result.success?
29
- # Stop if the parse failed...
20
+ # Stop if parse failed...
30
21
  puts "Parsing of '#{file_name}' failed"
31
22
  puts result.failure_reason.message
32
23
  exit(1)
@@ -34,15 +25,18 @@ end
34
25
 
35
26
  # Generate a parse tree from the parse result
36
27
  ptree = result.parse_tree
37
- require 'yaml'
38
- File.open('json1.yml', 'w') {|f| YAML.dump(ptree, f)}
28
+
29
+ # Select the output format
30
+ case cli_options[:format]
31
+ when :ascii_tree
32
+ renderer = Rley::Formatter::Asciitree.new($stdout)
33
+ when :labelled
34
+ renderer = Rley::Formatter::BracketNotation.new($stdout)
35
+ end
39
36
 
40
37
  # Let's create a parse tree visitor
41
38
  visitor = Rley::ParseTreeVisitor.new(ptree)
42
39
 
43
- # Output the labelled bracket notation of the tree
44
- use_notation = Rley::Formatter::BracketNotation.new($stdout)
45
- use_notation.render(visitor)
46
-
47
-
40
+ # Now output formatted parse tree
41
+ renderer.render(visitor)
48
42
  # End of file
@@ -4,7 +4,7 @@ require 'rley' # Load the gem
4
4
 
5
5
  ########################################
6
6
  # Define a grammar for JSON
7
- # Original JSON grammar is available http://www.json.org/fatfree.html
7
+ # Original JSON grammar is available at: http://www.json.org/fatfree.html
8
8
  # Official JSON grammar: http://rfc7159.net/rfc7159#rfc.section.2
9
9
  # Names of grammar elements are based on the RFC 7159 documentation
10
10
  builder = Rley::Syntax::GrammarBuilder.new do
@@ -27,11 +27,11 @@ builder = Rley::Syntax::GrammarBuilder.new do
27
27
  rule 'member-list' => %w(member-list value-separator member)
28
28
  rule 'member-list' => 'member'
29
29
  rule 'member' => %w(string name-separator value)
30
- rule 'array' => %w(begin-array array_items end-array)
30
+ rule 'array' => %w(begin-array array-items end-array)
31
31
  rule 'array' => %w(begin-array end-array)
32
- rule 'array_items' => %w(array_items value-separator value)
33
- rule 'array_items' => %w(value)
32
+ rule 'array-items' => %w(array-items value-separator value)
33
+ rule 'array-items' => %w(value)
34
34
  end
35
35
 
36
- # And now build the grammar...
36
+ # And now build the JSON grammar...
37
37
  GrammarJSON = builder.grammar
@@ -1,6 +1,6 @@
1
1
  # Purpose: to demonstrate how to build and render a parse tree for JSON
2
2
  # language
3
- require 'rley' # Load the gem
3
+ require 'rley' # Load the Rley gem
4
4
  require_relative 'json_lexer'
5
5
 
6
6
  # Steps to render a parse tree (of a valid parsed input):
@@ -0,0 +1,65 @@
1
+ require 'optparse'
2
+
3
+ # A Hash specialization that collects the command-line options
4
+ class CLIOptions < Hash
5
+ #labelled square notation (LBN).
6
+ #Use online tools (e.g. http://yohasebe.com/rsyntaxtree/) to visualize
7
+ #parse trees from LBN output.
8
+
9
+ def initialize(progName, progVersion, args)
10
+ super()
11
+
12
+ # Default values
13
+ self[:prog_name] = progName
14
+ self[:prog_version] = progVersion
15
+ self[:format] = :ascii_tree
16
+
17
+ options = build_option_parser
18
+ options.parse!(args)
19
+ end
20
+
21
+ private
22
+
23
+ def build_option_parser
24
+ OptionParser.new do |opts|
25
+ opts.banner = <<-END_BANNER
26
+ #{self[:prog_name]}: a demo utility that parses a JSON file
27
+ and renders its parse tree to the standard output
28
+ in the format specified in the command-line.
29
+
30
+ Usage: JSON_demo.rb [options] FILE
31
+
32
+ Examples:
33
+ JSON_demo --format ascii_tree sample01.jon
34
+ END_BANNER
35
+
36
+ opts.separator ''
37
+
38
+ format_help = <<-END_TEXT
39
+ Select the output format (default: ascii_tree). Available formats:
40
+ ascii_tree Simple text representation of parse trees
41
+ labelled Labelled square notation (LBN)
42
+ Use online tools (e.g. http://yohasebe.com/rsyntaxtree/)
43
+ to visualize parse trees from LBN output.
44
+ END_TEXT
45
+ formats = %i(ascii_tree labelled)
46
+ opts.on('-f', '--format FORMAT', formats, format_help) do |frm|
47
+ self[:format] = frm
48
+ end
49
+
50
+ opts.separator ''
51
+ opts.separator ' **** Utility ****'
52
+
53
+ opts.on('-v', '--version', 'Display the program version.') do
54
+ puts self[:prog_version]
55
+ exit
56
+ end
57
+
58
+ # No argument, shows at tail. This will print an options summary.
59
+ opts.on_tail('-h', '--help', 'Display this help message.') do
60
+ puts opts
61
+ exit
62
+ end
63
+ end
64
+ end
65
+ end # class
@@ -9,7 +9,7 @@ require_relative './rley/parser/gfg_earley_parser'
9
9
  require_relative './rley/parse_tree_visitor'
10
10
  require_relative './rley/formatter/debug'
11
11
  require_relative './rley/formatter/json'
12
- require_relative './rley/formatter/debug'
12
+ require_relative './rley/formatter/asciitree'
13
13
  require_relative './rley/formatter/bracket_notation'
14
14
 
15
15
  # End of file
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Rley # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.4.03'.freeze
6
+ Version = '0.4.04'.freeze
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm".freeze
@@ -0,0 +1,128 @@
1
+ require_relative 'base_formatter'
2
+
3
+
4
+ module Rley # This module is used as a namespace
5
+ # Namespace dedicated to parse tree formatters.
6
+ module Formatter
7
+ # A formatter class that draws parse trees by using characters
8
+ class Asciitree < BaseFormatter
9
+ # TODO
10
+ attr_reader(:curr_path)
11
+
12
+ # For each node in curr_path, there is a corresponding string value.
13
+ # Allowed string values are: 'first', 'last', 'first_and_last', 'other'
14
+ attr_reader(:ranks)
15
+
16
+ attr_reader(:nesting_prefix)
17
+
18
+ attr_reader(:blank_indent)
19
+
20
+ attr_reader(:continuation_indent)
21
+
22
+ # Constructor.
23
+ # @param anIO [IO] The output stream to which the rendered grammar
24
+ # is written.
25
+ def initialize(anIO)
26
+ super(anIO)
27
+ @curr_path = []
28
+ @ranks = []
29
+
30
+ @nesting_prefix = '+-- '
31
+ @blank_indent = ' '
32
+ @continuation_indent = '| '
33
+ end
34
+
35
+
36
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
37
+ # Notification of a visit event: the visitor is about to visit
38
+ # the children of a non-terminal node
39
+ # @param _parent [NonTerminalNode]
40
+ # @param _children [Array] array of children nodes
41
+ def before_subnodes(parent, children)
42
+ rank_of(parent)
43
+ curr_path << parent
44
+ end
45
+
46
+
47
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
48
+ # Notification of a visit event: the visitor is about to visit
49
+ # a non-terminal node
50
+ # @param nonterm [NonTerminalNode]
51
+ def before_non_terminal(aNonTerm)
52
+ emit(aNonTerm)
53
+ end
54
+
55
+
56
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
57
+ # Notification of a visit event: the visitor is about to visit
58
+ # a terminal node
59
+ # @param _term [TerminalNode]
60
+ def before_terminal(aTerm)
61
+ emit(aTerm, ": '#{aTerm.token.lexeme}'")
62
+ end
63
+
64
+
65
+ # Method called by a ParseTreeVisitor to which the formatter subscribed.
66
+ # Notification of a visit event: the visitor completed the visit of
67
+ # the children of a non-terminal node.
68
+ # @param _parent [NonTerminalNode]
69
+ # @param _children [Array] array of children nodes
70
+ def after_subnodes(_parent, _children)
71
+ curr_path.pop
72
+ ranks.pop
73
+ end
74
+
75
+ private
76
+
77
+ # Parent node is last node in current path
78
+ # or current path is empty (then aChild is root node)
79
+ def rank_of(aChild)
80
+ if curr_path.empty?
81
+ rank = 'root'
82
+ elsif curr_path[-1].subnodes.size == 1
83
+ rank = 'first_and_last'
84
+ else
85
+ parent = curr_path[-1]
86
+ siblings = parent.subnodes
87
+ siblings_last_index = siblings.size - 1
88
+ rank = case siblings.find_index(aChild)
89
+ when 0 then 'first'
90
+ when siblings_last_index then 'last'
91
+ else
92
+ 'other'
93
+ end
94
+ end
95
+ self.ranks << rank
96
+ end
97
+
98
+ # 'root', 'first', 'first_and_last', 'last', 'other'
99
+ def path_prefix()
100
+ return '' if ranks.empty?
101
+
102
+ prefix = ''
103
+ @ranks.each_with_index do |rank, i|
104
+ next if i == 0
105
+
106
+ case rank
107
+ when 'first', 'other'
108
+ prefix << continuation_indent
109
+
110
+ when 'last', 'first_and_last', 'root'
111
+ prefix << blank_indent
112
+ else
113
+ end
114
+ end
115
+
116
+ prefix << nesting_prefix
117
+ return prefix
118
+ end
119
+
120
+
121
+ def emit(aNode, aSuffix = '')
122
+ output.puts("#{path_prefix}#{aNode.symbol.name}#{aSuffix}")
123
+ end
124
+ end # class
125
+ end # module
126
+ end # module
127
+
128
+ # End of file
@@ -0,0 +1,95 @@
1
+ require_relative '../../spec_helper'
2
+ require 'stringio'
3
+
4
+ require_relative '../support/grammar_abc_helper'
5
+ require_relative '../../../lib/rley/tokens/token'
6
+ require_relative '../../../lib/rley/parser/gfg_earley_parser'
7
+ require_relative '../../../lib/rley/ptree/parse_tree'
8
+ require_relative '../../../lib/rley/parse_tree_visitor'
9
+ # Load the class under test
10
+ require_relative '../../../lib/rley/formatter/asciitree'
11
+
12
+ module Rley # Re-open the module to get rid of qualified names
13
+ module Formatter
14
+ describe Asciitree do
15
+ include GrammarABCHelper # Mix-in module for grammar abc
16
+
17
+ # Factory method. Build a production with the given sequence
18
+ # of symbols as its rhs.
19
+ let(:grammar_abc) do
20
+ builder = grammar_abc_builder
21
+ builder.grammar
22
+ end
23
+
24
+ # Variables for the terminal symbols
25
+ let(:a_) { grammar_abc.name2symbol['a'] }
26
+ let(:b_) { grammar_abc.name2symbol['b'] }
27
+ let(:c_) { grammar_abc.name2symbol['c'] }
28
+
29
+ # Helper method that mimicks the output of a tokenizer
30
+ # for the language specified by gramma_abc
31
+ let(:grm_abc_tokens1) do
32
+ [
33
+ Tokens::Token.new('a', a_),
34
+ Tokens::Token.new('a', a_),
35
+ Tokens::Token.new('b', b_),
36
+ Tokens::Token.new('c', c_),
37
+ Tokens::Token.new('c', c_)
38
+ ]
39
+ end
40
+
41
+ # Factory method that builds a sample parse tree.
42
+ # Generated tree has the following structure:
43
+ # S[0,5]
44
+ # +- A[0,5]
45
+ # +- a[0,0]
46
+ # +- A[1,4]
47
+ # | +- a[1,1]
48
+ # | +- A[2,3]
49
+ # | | +- b[2,3]
50
+ # | +- c[3,4]
51
+ # +- c[4,5]
52
+ # Capital letters represent non-terminal nodes
53
+ let(:grm_abc_ptree1) do
54
+ parser = Parser::GFGEarleyParser.new(grammar_abc)
55
+ parse_result = parser.parse(grm_abc_tokens1)
56
+ parse_result.parse_tree
57
+ end
58
+
59
+ let(:destination) { StringIO.new('', 'w') }
60
+ subject { Asciitree.new(destination) }
61
+
62
+ context 'Standard creation & initialization:' do
63
+ it 'should be initialized with an IO argument' do
64
+ expect { Asciitree.new(StringIO.new('', 'w')) }.not_to raise_error
65
+ end
66
+
67
+ it 'should know its output destination' do
68
+ expect(subject.output).to eq(destination)
69
+ end
70
+ end # context
71
+
72
+
73
+ context 'Rendering:' do
74
+ it 'should render a parse tree' do
75
+ visitor = Rley::ParseTreeVisitor.new(grm_abc_ptree1)
76
+ subject.render(visitor)
77
+ expectations = <<-SNIPPET
78
+ S
79
+ +-- A
80
+ +-- a: 'a'
81
+ +-- A
82
+ | +-- a: 'a'
83
+ | +-- A
84
+ | | +-- b: 'b'
85
+ | +-- c: 'c'
86
+ +-- c: 'c'
87
+ SNIPPET
88
+ expect(destination.string).to eq(expectations)
89
+ end
90
+ end # context
91
+ end # describe
92
+ end # module
93
+ end # module
94
+
95
+ # End of file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.03
4
+ version: 0.4.04
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-17 00:00:00.000000000 Z
11
+ date: 2017-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -135,12 +135,14 @@ files:
135
135
  - examples/data_formats/JSON/JSON_grammar.rb
136
136
  - examples/data_formats/JSON/JSON_lexer.rb
137
137
  - examples/data_formats/JSON/JSON_parser.rb
138
+ - examples/data_formats/JSON/cli_options.rb
138
139
  - examples/general/calc/calc_demo.rb
139
140
  - examples/general/calc/calc_grammar.rb
140
141
  - examples/general/calc/calc_lexer.rb
141
142
  - examples/general/calc/calc_parser.rb
142
143
  - lib/rley.rb
143
144
  - lib/rley/constants.rb
145
+ - lib/rley/formatter/asciitree.rb
144
146
  - lib/rley/formatter/base_formatter.rb
145
147
  - lib/rley/formatter/bracket_notation.rb
146
148
  - lib/rley/formatter/debug.rb
@@ -202,6 +204,7 @@ files:
202
204
  - lib/rley/syntax/verbatim_symbol.rb
203
205
  - lib/rley/tokens/token.rb
204
206
  - lib/rley/tokens/token_range.rb
207
+ - spec/rley/formatter/asciitree_spec.rb
205
208
  - spec/rley/formatter/bracket_notation_spec.rb
206
209
  - spec/rley/formatter/debug_spec.rb
207
210
  - spec/rley/formatter/json_spec.rb
@@ -293,6 +296,7 @@ signing_key:
293
296
  specification_version: 4
294
297
  summary: Ruby implementation of the Earley's parsing algorithm
295
298
  test_files:
299
+ - spec/rley/formatter/asciitree_spec.rb
296
300
  - spec/rley/formatter/bracket_notation_spec.rb
297
301
  - spec/rley/formatter/debug_spec.rb
298
302
  - spec/rley/formatter/json_spec.rb