dendroid 0.0.2 → 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 +4 -4
- data/.rubocop.yml +4 -0
- data/lib/dendroid/syntax/production.rb +94 -0
- data/lib/dendroid/syntax/rule.rb +62 -0
- data/spec/dendroid/syntax/production_spec.rb +77 -0
- data/spec/dendroid/syntax/rule_spec.rb +23 -0
- data/version.txt +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a28b9efefb235a669b37c24f587704cc94b7a10f411eafdeef418094013abfc2
|
4
|
+
data.tar.gz: 1e316ac1c14b8b81ee2a3acf96a3f04d3e772e72f0a062976150ea5def7080f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72b4df1de4b537b3bb5ae1d00f56a7cde86627c5b5b2205390a02c824809bfd81ee493ee70ca01d08ea3874e9dc04beb66f09366b186fb133a4250e426281553
|
7
|
+
data.tar.gz: 5cb5126a0f116d86a91f2a2138dac2330bfaf3911548324947196d768e0ec4e45a897360ce2ab3d31e48497bd24e2fdefdbfc365c28dda5f953f20ea3a16a563
|
data/.rubocop.yml
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'rule'
|
4
|
+
|
5
|
+
module Dendroid
|
6
|
+
module Syntax
|
7
|
+
# A specialization of the Rule class.
|
8
|
+
# A production is the unique rule for the non-terminal symbol
|
9
|
+
# at its left-hand side (lhs).
|
10
|
+
class Production < Rule
|
11
|
+
# @return [Dendroid::Syntax::SymbolSeq]
|
12
|
+
attr_reader :body
|
13
|
+
|
14
|
+
# Create a Production instance.
|
15
|
+
# @param lhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
|
16
|
+
# @param rhs [Dendroid::Syntax::SymbolSeq] the sequence of symbols on rhs.
|
17
|
+
def initialize(lhs, rhs)
|
18
|
+
super(lhs)
|
19
|
+
@body = valid_body(rhs)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Predicate method to check whether the rule body (its rhs) is empty.
|
23
|
+
# @return [Boolean]
|
24
|
+
def empty?
|
25
|
+
body.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Predicate method to check whether the rule has alternatives
|
29
|
+
# @return [FalseClass]
|
30
|
+
def choice?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Predicate method to check whether the production rule body is productive.
|
35
|
+
# It is productive when it is empty or all of its rhs members are productive too.
|
36
|
+
# @return [Boolean, NilClass]
|
37
|
+
def productive?
|
38
|
+
if @productive.nil?
|
39
|
+
if body.productive?
|
40
|
+
self.productive = true
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@productive
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Mark the production rule as non-productive.
|
50
|
+
def non_productive
|
51
|
+
self.productive = false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return the text representation of the production rule
|
55
|
+
# @return [String]
|
56
|
+
def to_s
|
57
|
+
"#{head} => #{body}"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Equality operator
|
61
|
+
# Two production rules are equal when their head and rhs are equal.
|
62
|
+
# @return [Boolean]
|
63
|
+
def ==(other)
|
64
|
+
return true if equal?(other)
|
65
|
+
|
66
|
+
(head == other.head) && (body == other.body)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns an array with the symbol sequence of its rhs
|
70
|
+
# @return [Array<Dendroid::Syntax::SymbolSeq>]
|
71
|
+
def rhs
|
72
|
+
[body]
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def valid_body(rhs)
|
78
|
+
raise StandardError unless rhs.is_a?(SymbolSeq)
|
79
|
+
|
80
|
+
if rhs.size == 1 && lhs == rhs.first
|
81
|
+
# Forbid cyclic rules (e.g. A => A)
|
82
|
+
raise StandardError.new, "Cyclic rule of the kind #{lhs} => #{lhs} is not allowed."
|
83
|
+
end
|
84
|
+
|
85
|
+
rhs
|
86
|
+
end
|
87
|
+
|
88
|
+
def productive=(val)
|
89
|
+
@productive = val
|
90
|
+
lhs.productive = val
|
91
|
+
end
|
92
|
+
end # class
|
93
|
+
end # module
|
94
|
+
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,77 @@
|
|
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
|
+
require_relative '..\..\..\lib\dendroid\syntax\production'
|
8
|
+
|
9
|
+
describe Dendroid::Syntax::Production do
|
10
|
+
let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
|
11
|
+
let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
|
12
|
+
let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
|
13
|
+
let(:foo_symb) { Dendroid::Syntax::NonTerminal.new('foo') }
|
14
|
+
let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
|
15
|
+
let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
|
16
|
+
|
17
|
+
# Implements a production rule: expression => NUMBER PLUS NUMBER
|
18
|
+
subject { described_class.new(expr_symb, rhs) }
|
19
|
+
|
20
|
+
context 'Initialization:' do
|
21
|
+
it 'is initialized with a head and a body' do
|
22
|
+
expect { described_class.new(expr_symb, rhs) }.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'knows its body (aka rhs)' do
|
26
|
+
expect(subject.body).to eq(rhs)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'renders a String representation of itself' do
|
30
|
+
expect(subject.to_s).to eq('expression => NUMBER PLUS NUMBER')
|
31
|
+
end
|
32
|
+
end # context
|
33
|
+
|
34
|
+
context 'Provided services:' do
|
35
|
+
it 'knows whether its body empty is empty or not' do
|
36
|
+
expect(described_class.new(expr_symb, empty_body).empty?).to be_truthy
|
37
|
+
expect(subject.empty?).to be_falsey
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'knows its non-terminal members' do
|
41
|
+
foo_rhs = Dendroid::Syntax::SymbolSeq.new([expr_symb, plus_symb, expr_symb])
|
42
|
+
instance = described_class.new(foo_symb, foo_rhs)
|
43
|
+
expect(instance.nonterminals).to eq([expr_symb])
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'knows its terminal members' do
|
47
|
+
expect(subject.terminals).to eq([num_symb, plus_symb])
|
48
|
+
end
|
49
|
+
|
50
|
+
# rubocop: disable Lint/BinaryOperatorWithIdenticalOperands
|
51
|
+
it 'can compare with another production' do
|
52
|
+
expect(subject == subject).to be_truthy
|
53
|
+
|
54
|
+
same = described_class.new(expr_symb, rhs)
|
55
|
+
expect(subject == same).to be_truthy
|
56
|
+
|
57
|
+
# Different lhs, same rhs
|
58
|
+
different_lhs = described_class.new(foo_symb, rhs)
|
59
|
+
expect(subject == different_lhs).to be_falsey
|
60
|
+
|
61
|
+
# Same lhs, different rhs
|
62
|
+
different_rhs = described_class.new(expr_symb, empty_body)
|
63
|
+
expect(subject == different_rhs).to be_falsey
|
64
|
+
|
65
|
+
# Two productions with same lhs and empty bodies
|
66
|
+
empty = described_class.new(expr_symb, empty_body)
|
67
|
+
void = described_class.new(expr_symb, Dendroid::Syntax::SymbolSeq.new([]))
|
68
|
+
expect(empty == void).to be_truthy
|
69
|
+
|
70
|
+
# Two productions with distinct lhs and empty bodies
|
71
|
+
empty = described_class.new(expr_symb, empty_body)
|
72
|
+
void = described_class.new(foo_symb, Dendroid::Syntax::SymbolSeq.new([]))
|
73
|
+
expect(empty == void).to be_falsey
|
74
|
+
end
|
75
|
+
# rubocop: enable Lint/BinaryOperatorWithIdenticalOperands
|
76
|
+
end # context
|
77
|
+
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
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
@@ -25,10 +25,14 @@ 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/production.rb
|
29
|
+
- lib/dendroid/syntax/rule.rb
|
28
30
|
- lib/dendroid/syntax/symbol_seq.rb
|
29
31
|
- lib/dendroid/syntax/terminal.rb
|
30
32
|
- spec/dendroid/syntax/grm_symbol_spec.rb
|
31
33
|
- spec/dendroid/syntax/non_terminal_spec.rb
|
34
|
+
- spec/dendroid/syntax/production_spec.rb
|
35
|
+
- spec/dendroid/syntax/rule_spec.rb
|
32
36
|
- spec/dendroid/syntax/symbol_seq_spec.rb
|
33
37
|
- spec/dendroid/syntax/terminal_spec.rb
|
34
38
|
- spec/spec_helper.rb
|