dendroid 0.0.1 → 0.0.3
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 +4 -4
- data/.rubocop.yml +8 -0
- data/LICENSE +22 -22
- data/README.md +60 -60
- data/bin/dendroid +6 -7
- data/dendroid.gemspec +14 -13
- data/lib/dendroid/dendroid.rb +5 -0
- data/lib/dendroid/syntax/grm_symbol.rb +45 -45
- data/lib/dendroid/syntax/non_terminal.rb +38 -43
- data/lib/dendroid/syntax/rule.rb +62 -0
- data/lib/dendroid/syntax/symbol_seq.rb +63 -0
- data/lib/dendroid/syntax/terminal.rb +39 -40
- data/spec/dendroid/syntax/grm_symbol_spec.rb +45 -43
- data/spec/dendroid/syntax/non_terminal_spec.rb +16 -16
- data/spec/dendroid/syntax/rule_spec.rb +23 -0
- data/spec/dendroid/syntax/symbol_seq_spec.rb +73 -0
- data/spec/dendroid/syntax/terminal_spec.rb +28 -29
- data/spec/spec_helper.rb +12 -13
- data/version.txt +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 869334c2ae25a3765d7105eca383f1ba89162ae9a9446a2b5268a0d463261cc7
|
4
|
+
data.tar.gz: 7811bca0c671959b90618a8e667c57da65ed2c5d8ccc4171a86e0c8a3a80a6c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 582c360f25cab435de732a6a7837461302a4af9325f6ddf93448be3c9f674acf59c05e6aa756d21a7ea805c1c55373bed298c4c659844fb67781f51c3444bf67
|
7
|
+
data.tar.gz: 52a3e9325988a534998dbbfb35471220c087702f7a97df0acc77c62ef55202be4cb5a858fe16e77ee525f4bfb98ff6fa7756fb99689ce980a8e02cd38ab24aa0
|
data/.rubocop.yml
CHANGED
data/LICENSE
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
The MIT License
|
2
|
-
|
3
|
-
Copyright (c) 2023 Dimitri Geshef
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
'Software'), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Dimitri Geshef
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,60 +1,60 @@
|
|
1
|
-
dendroid
|
2
|
-
===========
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
Features
|
7
|
-
--------
|
8
|
-
|
9
|
-
*
|
10
|
-
|
11
|
-
Examples
|
12
|
-
--------
|
13
|
-
|
14
|
-
FIXME (code sample of usage)
|
15
|
-
|
16
|
-
Requirements
|
17
|
-
------------
|
18
|
-
|
19
|
-
* FIXME (list of requirements)
|
20
|
-
|
21
|
-
Install
|
22
|
-
-------
|
23
|
-
|
24
|
-
* FIXME (sudo gem install, anything else)
|
25
|
-
|
26
|
-
Author
|
27
|
-
------
|
28
|
-
|
29
|
-
Original author:
|
30
|
-
|
31
|
-
Contributors:
|
32
|
-
|
33
|
-
* FIXME (contributor 1?)
|
34
|
-
* FIXME (contributor 2?)
|
35
|
-
|
36
|
-
License
|
37
|
-
-------
|
38
|
-
|
39
|
-
(The MIT License)
|
40
|
-
|
41
|
-
Copyright (c) 2023
|
42
|
-
|
43
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
44
|
-
a copy of this software and associated documentation files (the
|
45
|
-
'Software'), to deal in the Software without restriction, including
|
46
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
47
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
48
|
-
permit persons to whom the Software is furnished to do so, subject to
|
49
|
-
the following conditions:
|
50
|
-
|
51
|
-
The above copyright notice and this permission notice shall be
|
52
|
-
included in all copies or substantial portions of the Software.
|
53
|
-
|
54
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
55
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
56
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
57
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
58
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
59
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
60
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
dendroid
|
2
|
+
===========
|
3
|
+
|
4
|
+
A Ruby implementation of the Earley parsing algorithm. WIP
|
5
|
+
|
6
|
+
Features
|
7
|
+
--------
|
8
|
+
|
9
|
+
* WIP
|
10
|
+
|
11
|
+
Examples
|
12
|
+
--------
|
13
|
+
|
14
|
+
FIXME (code sample of usage)
|
15
|
+
|
16
|
+
Requirements
|
17
|
+
------------
|
18
|
+
|
19
|
+
* FIXME (list of requirements)
|
20
|
+
|
21
|
+
Install
|
22
|
+
-------
|
23
|
+
|
24
|
+
* FIXME (sudo gem install, anything else)
|
25
|
+
|
26
|
+
Author
|
27
|
+
------
|
28
|
+
|
29
|
+
Original author: Dimitri Geshef
|
30
|
+
|
31
|
+
Contributors:
|
32
|
+
|
33
|
+
* FIXME (contributor 1?)
|
34
|
+
* FIXME (contributor 2?)
|
35
|
+
|
36
|
+
License
|
37
|
+
-------
|
38
|
+
|
39
|
+
(The MIT License)
|
40
|
+
|
41
|
+
Copyright (c) 2023 Dimitri Geshef
|
42
|
+
|
43
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
44
|
+
a copy of this software and associated documentation files (the
|
45
|
+
'Software'), to deal in the Software without restriction, including
|
46
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
47
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
48
|
+
permit persons to whom the Software is furnished to do so, subject to
|
49
|
+
the following conditions:
|
50
|
+
|
51
|
+
The above copyright notice and this permission notice shall be
|
52
|
+
included in all copies or substantial portions of the Software.
|
53
|
+
|
54
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
55
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
56
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
57
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
58
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
59
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
60
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/dendroid
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
root = File.expand_path('../..', __FILE__)
|
4
|
-
require File.join(root, %w[lib dendroid])
|
5
|
-
|
6
|
-
# Put your code here
|
7
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root = File.expand_path('../..', __FILE__)
|
4
|
+
require File.join(root, %w[lib dendroid])
|
5
|
+
|
6
|
+
# Put your code here
|
data/dendroid.gemspec
CHANGED
@@ -3,25 +3,26 @@
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'dendroid'
|
5
5
|
s.version = begin
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
libpath = ::File.expand_path(__FILE__) + ::File::SEPARATOR
|
7
|
+
path = ::File.dirname(libpath) + ::File::SEPARATOR
|
8
|
+
::File.read("#{path}version.txt").strip
|
9
|
+
end
|
10
10
|
s.summary = 'Dendroid. TODO'
|
11
11
|
s.description = 'WIP. A Ruby implementation of a Earley parser'
|
12
12
|
s.authors = ['Dimitri Geshef']
|
13
13
|
s.email = 'famished.tiger@yahoo.com'
|
14
14
|
s.files = Dir['bin/dendroid',
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
'lib/*.*',
|
16
|
+
'lib/**/*.rb',
|
17
|
+
'spec/**/*.rb',
|
18
|
+
'.rubocop.yml',
|
19
|
+
'dendroid.gemspec',
|
20
|
+
'LICENSE',
|
21
|
+
'Rakefile',
|
22
|
+
'README.md',
|
23
|
+
'version.txt'
|
24
24
|
]
|
25
|
+
s.required_ruby_version = '>=3.1'
|
25
26
|
s.homepage = 'https://github.com/famished-tiger/Dendroid'
|
26
27
|
s.license = 'MIT'
|
27
28
|
end
|
data/lib/dendroid/dendroid.rb
CHANGED
@@ -1,45 +1,45 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Dendroid
|
4
|
-
module Syntax
|
5
|
-
# Abstract class for grammar symbols.
|
6
|
-
# A grammar symbol is an element that appears in grammar rules.
|
7
|
-
class GrmSymbol
|
8
|
-
# @return [String] The name of the grammar symbol
|
9
|
-
attr_reader :name
|
10
|
-
|
11
|
-
# Constructor.
|
12
|
-
# aSymbolName [String] The name of the grammar symbol.
|
13
|
-
def initialize(symbolName)
|
14
|
-
@name = valid_name(symbolName)
|
15
|
-
end
|
16
|
-
|
17
|
-
# The String representation of the grammar symbol
|
18
|
-
# @return [String]
|
19
|
-
def to_s
|
20
|
-
name.to_s
|
21
|
-
end
|
22
|
-
|
23
|
-
# Equality testing (based on symbol name)
|
24
|
-
# @return [Boolean]
|
25
|
-
def ==(other)
|
26
|
-
name == other.name
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def valid_name(symbolName)
|
32
|
-
if symbolName.is_a?(String)
|
33
|
-
stripped = symbolName.strip
|
34
|
-
if stripped.empty?
|
35
|
-
err_msg = 'A symbol name cannot be empty.'
|
36
|
-
raise StandardError, err_msg
|
37
|
-
end
|
38
|
-
stripped.to_sym
|
39
|
-
else
|
40
|
-
symbolName
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end # class
|
44
|
-
end # module
|
45
|
-
end # module
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dendroid
|
4
|
+
module Syntax
|
5
|
+
# Abstract class for grammar symbols.
|
6
|
+
# A grammar symbol is an element that appears in grammar rules.
|
7
|
+
class GrmSymbol
|
8
|
+
# @return [String] The name of the grammar symbol
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# Constructor.
|
12
|
+
# aSymbolName [String] The name of the grammar symbol.
|
13
|
+
def initialize(symbolName)
|
14
|
+
@name = valid_name(symbolName)
|
15
|
+
end
|
16
|
+
|
17
|
+
# The String representation of the grammar symbol
|
18
|
+
# @return [String]
|
19
|
+
def to_s
|
20
|
+
name.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
# Equality testing (based on symbol name)
|
24
|
+
# @return [Boolean]
|
25
|
+
def ==(other)
|
26
|
+
name == other.name
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def valid_name(symbolName)
|
32
|
+
if symbolName.is_a?(String)
|
33
|
+
stripped = symbolName.strip
|
34
|
+
if stripped.empty?
|
35
|
+
err_msg = 'A symbol name cannot be empty.'
|
36
|
+
raise StandardError, err_msg
|
37
|
+
end
|
38
|
+
stripped.to_sym
|
39
|
+
else
|
40
|
+
symbolName
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end # class
|
44
|
+
end # module
|
45
|
+
end # module
|
@@ -1,43 +1,38 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@productive
|
40
|
-
end
|
41
|
-
end # class
|
42
|
-
end # module
|
43
|
-
end # module
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'grm_symbol'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Syntax
|
7
|
+
# A non-terminal symbol (sometimes called a syntactic variable) represents
|
8
|
+
# a composition of terminal or non-terminal symbols
|
9
|
+
class NonTerminal < GrmSymbol
|
10
|
+
# @return [Boolean] true if symbol can derive the null token
|
11
|
+
attr_accessor :nullable
|
12
|
+
|
13
|
+
# @return [Boolean] true iff the symbol always matches a non-empty
|
14
|
+
# sequence of terminal symbols
|
15
|
+
attr_accessor :productive
|
16
|
+
|
17
|
+
# Predicate method to check whether the symbol is a terminal symbol.
|
18
|
+
# @return [FalseClass]
|
19
|
+
def terminal?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Predicate method to check whether the symbol can derive (match)
|
24
|
+
# the null token.
|
25
|
+
# @return [Boolean]
|
26
|
+
def nullable?
|
27
|
+
@nullable
|
28
|
+
end
|
29
|
+
|
30
|
+
# Predicate method to check whether the symbol always matches
|
31
|
+
# a non-empty sequence of terminal symbols.
|
32
|
+
# @return [Boolean]
|
33
|
+
def productive?
|
34
|
+
@productive
|
35
|
+
end
|
36
|
+
end # class
|
37
|
+
end # module
|
38
|
+
end # module
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dendroid
|
4
|
+
module Syntax
|
5
|
+
# In a context-free grammar, a rule has its left-hand side (LHS)
|
6
|
+
# that consists solely of one non-terminal symbol.
|
7
|
+
# and the right-hand side (RHS) consists of one or more sequence of symbols.
|
8
|
+
# The symbols in RHS can be either terminal or non-terminal symbols.
|
9
|
+
# The rule stipulates that the LHS is equivalent to the RHS,
|
10
|
+
# in other words every occurrence of the LHS can be substituted to
|
11
|
+
# corresponding RHS.
|
12
|
+
class Rule
|
13
|
+
# @return [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
|
14
|
+
attr_reader :head
|
15
|
+
alias lhs head
|
16
|
+
|
17
|
+
# Create a Rule instance.
|
18
|
+
# @param lhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
|
19
|
+
def initialize(lhs)
|
20
|
+
@head = valid_head(lhs)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the text representation of the rule
|
24
|
+
# @return [String]
|
25
|
+
def to_s
|
26
|
+
head.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
# The set of all grammar symbols that occur in the rhs.
|
30
|
+
# @return [Array<Dendroid::Syntax::GrmSymbol>]
|
31
|
+
def rhs_symbols
|
32
|
+
symbols = rhs.reduce([]) do |result, alt|
|
33
|
+
result.concat(alt.members)
|
34
|
+
end
|
35
|
+
symbols.uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
# The set of all non-terminal symbols that occur in the rhs.
|
39
|
+
# @return [Array<Dendroid::Syntax::NonTerminal>]
|
40
|
+
def nonterminals
|
41
|
+
rhs_symbols.reject(&:terminal?)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The set of all terminal symbols that occur in the rhs.
|
45
|
+
# @return [Array<Dendroid::Syntax::Terminal>]
|
46
|
+
def terminals
|
47
|
+
rhs_symbols.select(&:terminal?)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def valid_head(lhs)
|
53
|
+
if lhs.terminal?
|
54
|
+
err_msg = "Terminal symbol '#{lhs}' may not be on left-side of a rule."
|
55
|
+
raise StandardError, err_msg
|
56
|
+
end
|
57
|
+
|
58
|
+
lhs
|
59
|
+
end
|
60
|
+
end # class
|
61
|
+
end # module
|
62
|
+
end # module
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require_relative 'grm_symbol'
|
5
|
+
|
6
|
+
module Dendroid
|
7
|
+
module Syntax
|
8
|
+
# A sequence of grammar symbols. This class is used to represent
|
9
|
+
# members of right-hand side of production rules
|
10
|
+
class SymbolSeq
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
# @return [Array<Dendroid::Syntax::GrmSymbol>] The sequence of symbols
|
14
|
+
attr_reader :members
|
15
|
+
|
16
|
+
def_delegators(:@members, :empty?, :first, :map, :size)
|
17
|
+
|
18
|
+
# Create a sequence of grammar symbols (as in right-hand side of
|
19
|
+
# a production rule).
|
20
|
+
# @param symbols [Array<Dendroid::Syntax::GrmSymbol>] An array of symbols.
|
21
|
+
def initialize(symbols)
|
22
|
+
@members = symbols
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] A text representation of the symbol sequence
|
26
|
+
def to_s
|
27
|
+
members.join(' ')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Retrieve all the non-terminal symbols in the sequence.
|
31
|
+
# @return [Array<Dendroid::Syntax::NonTerminal>] array of non-terminals.
|
32
|
+
def nonterminals
|
33
|
+
members.reject(&:terminal?)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Retrieve all the terminal symbols in the sequence.
|
37
|
+
# @return [Array<Dendroid::Syntax::Terminal>] array of terminals
|
38
|
+
def terminals
|
39
|
+
members.select(&:terminal?)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Predicate method to check whether the sequence always derives (matches)
|
43
|
+
# a non-empty sequence of terminal symbols.
|
44
|
+
# @return [Boolean]
|
45
|
+
def productive?
|
46
|
+
empty? || members.all?(&:productive?)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Equality operator.
|
50
|
+
# @param other [Dendroid::Syntax::SymbolSeq]
|
51
|
+
# @return [Boolean] true when members are equal to the ones from `other`
|
52
|
+
def ==(other)
|
53
|
+
members == other.members
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def valid_members(symbols)
|
59
|
+
raise StandardError unless symbols.all? { |symb| symb.is_a?(GrmSymbol) }
|
60
|
+
end
|
61
|
+
end # class
|
62
|
+
end # module
|
63
|
+
end # module
|
@@ -1,40 +1,39 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
class
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end # module
|
39
|
-
|
40
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'grm_symbol'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Syntax
|
7
|
+
# A terminal symbol is an elementary symbol of the language defined by the grammar.
|
8
|
+
# More specifically, it represents a class of 'words'(or a token) of the language.
|
9
|
+
class Terminal < GrmSymbol
|
10
|
+
# Constructor.
|
11
|
+
# aSymbolName [String] The name of the grammar symbol.
|
12
|
+
def initialize(symbolName)
|
13
|
+
super(symbolName)
|
14
|
+
freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
# Predicate method to check whether the symbol is a terminal symbol.
|
18
|
+
# @return [TrueClass]
|
19
|
+
def terminal?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Predicate method to check whether the symbol derives (matches)
|
24
|
+
# the empty string. As a terminal symbol corresponds to an input token,
|
25
|
+
# it is by definition non-nullable.
|
26
|
+
# @return [FalseClass]
|
27
|
+
def nullable?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Predicate method to check whether the symbol always matches
|
32
|
+
# a non-empty sequence of terminal symbols.
|
33
|
+
# @return [TrueClass]
|
34
|
+
def productive?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end # class
|
38
|
+
end # module
|
39
|
+
end # module
|
@@ -1,43 +1,45 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '..\..\spec_helper'
|
4
|
-
require_relative '..\..\..\lib\dendroid\syntax\grm_symbol'
|
5
|
-
|
6
|
-
describe Dendroid::Syntax::GrmSymbol do
|
7
|
-
let(:sample_symbol_name) { 'INTEGER' }
|
8
|
-
|
9
|
-
subject { described_class.new(sample_symbol_name) }
|
10
|
-
|
11
|
-
context 'Initialization:' do
|
12
|
-
it 'is initialized with a name as String' do
|
13
|
-
expect { described_class.new(sample_symbol_name) }.not_to raise_error
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'is initialized with a name as Symbol' do
|
17
|
-
expect { described_class.new(:INTEGER) }.not_to raise_error
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'raises an error if the symbol name is empty' do
|
21
|
-
err_msg = 'A symbol name cannot be empty.'
|
22
|
-
expect { described_class.new('') }.to raise_error(StandardError, err_msg)
|
23
|
-
end
|
24
|
-
end # context
|
25
|
-
|
26
|
-
context 'Provided services:' do
|
27
|
-
it 'knows its name as a Symbol' do
|
28
|
-
expect(subject.name).to eq(sample_symbol_name.to_sym)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'renders a String representation of itself' do
|
32
|
-
expect(subject.to_s).to eq(sample_symbol_name)
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '..\..\spec_helper'
|
4
|
+
require_relative '..\..\..\lib\dendroid\syntax\grm_symbol'
|
5
|
+
|
6
|
+
describe Dendroid::Syntax::GrmSymbol do
|
7
|
+
let(:sample_symbol_name) { 'INTEGER' }
|
8
|
+
|
9
|
+
subject { described_class.new(sample_symbol_name) }
|
10
|
+
|
11
|
+
context 'Initialization:' do
|
12
|
+
it 'is initialized with a name as String' do
|
13
|
+
expect { described_class.new(sample_symbol_name) }.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'is initialized with a name as Symbol' do
|
17
|
+
expect { described_class.new(:INTEGER) }.not_to raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'raises an error if the symbol name is empty' do
|
21
|
+
err_msg = 'A symbol name cannot be empty.'
|
22
|
+
expect { described_class.new('') }.to raise_error(StandardError, err_msg)
|
23
|
+
end
|
24
|
+
end # context
|
25
|
+
|
26
|
+
context 'Provided services:' do
|
27
|
+
it 'knows its name as a Symbol' do
|
28
|
+
expect(subject.name).to eq(sample_symbol_name.to_sym)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'renders a String representation of itself' do
|
32
|
+
expect(subject.to_s).to eq(sample_symbol_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
36
|
+
it 'can compare with another symbol' do
|
37
|
+
expect(subject == subject).to be_truthy
|
38
|
+
same = described_class.new(sample_symbol_name)
|
39
|
+
expect(subject == same).to be_truthy
|
40
|
+
different = described_class.new('NUMBER')
|
41
|
+
expect(subject == different).to be_falsey
|
42
|
+
end
|
43
|
+
# rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
|
44
|
+
end # context
|
45
|
+
end # describe
|
@@ -1,16 +1,16 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '..\..\spec_helper'
|
4
|
-
require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
|
5
|
-
|
6
|
-
describe Dendroid::Syntax::NonTerminal do
|
7
|
-
let(:sample_nonterminal_name) { 'EXPRESSION' }
|
8
|
-
|
9
|
-
subject { described_class.new(sample_nonterminal_name) }
|
10
|
-
|
11
|
-
context 'Provided services:' do
|
12
|
-
it 'knows it is not a terminal symbol' do
|
13
|
-
expect(subject.terminal?).to be_falsey
|
14
|
-
end
|
15
|
-
end # context
|
16
|
-
end # describe
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '..\..\spec_helper'
|
4
|
+
require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
|
5
|
+
|
6
|
+
describe Dendroid::Syntax::NonTerminal do
|
7
|
+
let(:sample_nonterminal_name) { 'EXPRESSION' }
|
8
|
+
|
9
|
+
subject { described_class.new(sample_nonterminal_name) }
|
10
|
+
|
11
|
+
context 'Provided services:' do
|
12
|
+
it 'knows it is not a terminal symbol' do
|
13
|
+
expect(subject.terminal?).to be_falsey
|
14
|
+
end
|
15
|
+
end # context
|
16
|
+
end # describe
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '..\..\spec_helper'
|
4
|
+
require_relative '..\..\..\lib\dendroid\syntax\terminal'
|
5
|
+
require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
|
6
|
+
require_relative '..\..\..\lib\dendroid\syntax\rule'
|
7
|
+
|
8
|
+
describe Dendroid::Syntax::Rule do
|
9
|
+
let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
|
10
|
+
let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
|
11
|
+
|
12
|
+
subject { described_class.new(expr_symb) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'is initialized with a non-terminal' do
|
16
|
+
expect { described_class.new(expr_symb) }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'knows its head (aka lhs)' do
|
20
|
+
expect(subject.head).to eq(expr_symb)
|
21
|
+
end
|
22
|
+
end # context
|
23
|
+
end # describe
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '..\..\spec_helper'
|
4
|
+
require_relative '..\..\..\lib\dendroid\syntax\terminal'
|
5
|
+
require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
|
6
|
+
require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
|
7
|
+
|
8
|
+
describe Dendroid::Syntax::SymbolSeq do
|
9
|
+
let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
|
10
|
+
let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
|
11
|
+
|
12
|
+
subject { described_class.new([num_symb, plus_symb, num_symb]) }
|
13
|
+
|
14
|
+
context 'Initialization:' do
|
15
|
+
it 'is initialized with an empty array' do
|
16
|
+
expect { described_class.new([]) }.not_to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is initialized with an array of GrmSymbols' do
|
20
|
+
expect { described_class.new([num_symb, plus_symb, num_symb]) }.not_to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'knows its members' do
|
24
|
+
expect(subject.members).to eq([num_symb, plus_symb, num_symb])
|
25
|
+
end
|
26
|
+
end # context
|
27
|
+
|
28
|
+
context 'Provided services:' do
|
29
|
+
let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('PLUS') }
|
30
|
+
|
31
|
+
it 'knows whether its body empty is empty or not' do
|
32
|
+
expect(described_class.new([]).empty?).to be_truthy
|
33
|
+
expect(subject.empty?).to be_falsey
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'renders a String representation of itself' do
|
37
|
+
expect(subject.to_s).to eq('NUMBER PLUS NUMBER')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'knows its non-terminal members' do
|
41
|
+
instance = described_class.new([expr_symb, plus_symb, expr_symb])
|
42
|
+
expect(instance.nonterminals).to eq([expr_symb, expr_symb])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'knows its terminal members' do
|
46
|
+
expect(subject.terminals).to eq([num_symb, plus_symb, num_symb])
|
47
|
+
end
|
48
|
+
|
49
|
+
# rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
50
|
+
it 'can compare with another symbol sequence' do
|
51
|
+
expect(subject == subject).to be_truthy
|
52
|
+
same = described_class.new([num_symb, plus_symb, num_symb])
|
53
|
+
expect(subject == same).to be_truthy
|
54
|
+
different = described_class.new([num_symb, plus_symb, plus_symb])
|
55
|
+
expect(subject == different).to be_falsey
|
56
|
+
|
57
|
+
# Comparing two empty sequences
|
58
|
+
empty = described_class.new([])
|
59
|
+
void = described_class.new([])
|
60
|
+
expect(empty == void).to be_truthy
|
61
|
+
end
|
62
|
+
# rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
|
63
|
+
|
64
|
+
it 'knows whether it is productive' do
|
65
|
+
# Case: all members are productive
|
66
|
+
expect(subject.productive?).to be_truthy
|
67
|
+
|
68
|
+
# Case: at least one member is non-productive (expr_symb.productive is nil)
|
69
|
+
instance = described_class.new([expr_symb, plus_symb, num_symb])
|
70
|
+
expect(instance.productive?).to be_falsey
|
71
|
+
end
|
72
|
+
end # context
|
73
|
+
end # describe
|
@@ -1,29 +1,28 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '..\..\spec_helper'
|
4
|
-
require_relative '..\..\..\lib\dendroid\syntax\terminal'
|
5
|
-
|
6
|
-
describe Dendroid::Syntax::Terminal do
|
7
|
-
let(:sample_terminal_name) { 'INTEGER' }
|
8
|
-
|
9
|
-
subject { described_class.new(sample_terminal_name) }
|
10
|
-
|
11
|
-
context 'Provided services:' do
|
12
|
-
it 'knows it is a terminal symbol' do
|
13
|
-
expect(subject.terminal?).to be_truthy
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'is frozen after initialization' do
|
17
|
-
expect(subject).to be_frozen
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'is not nullable' do
|
21
|
-
expect(subject).not_to be_nullable
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'is productive (generative)' do
|
25
|
-
expect(subject).to be_productive
|
26
|
-
end
|
27
|
-
end # context
|
28
|
-
end # describe
|
29
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '..\..\spec_helper'
|
4
|
+
require_relative '..\..\..\lib\dendroid\syntax\terminal'
|
5
|
+
|
6
|
+
describe Dendroid::Syntax::Terminal do
|
7
|
+
let(:sample_terminal_name) { 'INTEGER' }
|
8
|
+
|
9
|
+
subject { described_class.new(sample_terminal_name) }
|
10
|
+
|
11
|
+
context 'Provided services:' do
|
12
|
+
it 'knows it is a terminal symbol' do
|
13
|
+
expect(subject.terminal?).to be_truthy
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'is frozen after initialization' do
|
17
|
+
expect(subject).to be_frozen
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'is not nullable' do
|
21
|
+
expect(subject).not_to be_nullable
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is productive (generative)' do
|
25
|
+
expect(subject).to be_productive
|
26
|
+
end
|
27
|
+
end # context
|
28
|
+
end # describe
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# require 'rubygems'
|
4
|
-
require 'rspec'
|
5
|
-
|
6
|
-
RSpec.configure do |config|
|
7
|
-
# Enable flags like --only-failures and --next-failure
|
8
|
-
config.example_status_persistence_file_path =
|
9
|
-
config.expect_with :rspec do |c|
|
10
|
-
c.syntax = :expect
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# require 'rubygems'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
# Enable flags like --only-failures and --next-failure
|
8
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
9
|
+
config.expect_with :rspec do |c|
|
10
|
+
c.syntax = :expect
|
11
|
+
end
|
12
|
+
end
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dendroid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: WIP. A Ruby implementation of a Earley parser
|
14
14
|
email: famished.tiger@yahoo.com
|
@@ -25,9 +25,13 @@ files:
|
|
25
25
|
- lib/dendroid/dendroid.rb
|
26
26
|
- lib/dendroid/syntax/grm_symbol.rb
|
27
27
|
- lib/dendroid/syntax/non_terminal.rb
|
28
|
+
- lib/dendroid/syntax/rule.rb
|
29
|
+
- lib/dendroid/syntax/symbol_seq.rb
|
28
30
|
- lib/dendroid/syntax/terminal.rb
|
29
31
|
- spec/dendroid/syntax/grm_symbol_spec.rb
|
30
32
|
- spec/dendroid/syntax/non_terminal_spec.rb
|
33
|
+
- spec/dendroid/syntax/rule_spec.rb
|
34
|
+
- spec/dendroid/syntax/symbol_seq_spec.rb
|
31
35
|
- spec/dendroid/syntax/terminal_spec.rb
|
32
36
|
- spec/spec_helper.rb
|
33
37
|
- version.txt
|
@@ -43,7 +47,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
43
47
|
requirements:
|
44
48
|
- - ">="
|
45
49
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
50
|
+
version: '3.1'
|
47
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
52
|
requirements:
|
49
53
|
- - ">="
|