Skip to content

Commit ae06098

Browse files
committed
add tests for dyckParity TODO: fix nonterminal leafs in DyckParityBeta
1 parent 7eaaabc commit ae06098

File tree

6 files changed

+520
-0
lines changed

6 files changed

+520
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package solver.correctnessTests.dyckKParityAlpha
2+
3+
import org.ucfs.grammar.combinator.Grammar
4+
import org.ucfs.grammar.combinator.regexp.Epsilon
5+
import org.ucfs.grammar.combinator.regexp.Nt
6+
import org.ucfs.grammar.combinator.regexp.Regexp
7+
import org.ucfs.grammar.combinator.regexp.or
8+
import org.ucfs.grammar.combinator.regexp.times
9+
import org.ucfs.rsm.symbol.Nonterminal
10+
import org.ucfs.rsm.symbol.Term
11+
12+
private fun maskToParity(mask: Int, k: Int): String {
13+
val sb = StringBuilder()
14+
15+
for (i in 0 until k) {
16+
if (((mask shr i) % 2) == 0) {
17+
sb.append("p")
18+
} else {
19+
sb.append("i")
20+
}
21+
}
22+
23+
return sb.toString()
24+
}
25+
26+
private fun initNtReflectively(grammar: Grammar, nt: Nt, name: String) {
27+
val ntClass = Nt::class.java
28+
29+
val nameField = ntClass.getDeclaredField("name")
30+
nameField.isAccessible = true
31+
nameField.set(nt, name)
32+
33+
val nontermField = ntClass.getDeclaredField("nonterm")
34+
nontermField.isAccessible = true
35+
nontermField.set(nt, Nonterminal(name))
36+
37+
grammar.nonTerms.add(nt)
38+
}
39+
40+
fun dyckAlphaGrammarKParity(
41+
parenthesesIds: List<String> = listOf("1"),
42+
bracketsIds: List<String> = listOf("1"),
43+
k: Int = 1
44+
): Grammar {
45+
return object : Grammar() {
46+
val S by Nt().asStart()
47+
48+
init {
49+
val grammar = this
50+
val numStates = 1 shl k // 2^k - Total number of parity states
51+
52+
// Create 2^k non-terminals (states)
53+
// Each state S_mask corresponds to a row where the parity
54+
// of the k brackets is equal to the 'mask'
55+
val Smap = buildMap<Int, Nt> {
56+
for (mask in 0 until numStates) {
57+
val p = maskToParity(mask, k)
58+
val nt = Nt()
59+
initNtReflectively(grammar, nt, "S$p")
60+
put(mask, nt)
61+
}
62+
}
63+
64+
val sortedB = bracketsIds.sorted()
65+
val labelGroup = mutableMapOf<String, Int>()
66+
// Assign each group of brackets the corresponding bit (group)
67+
for ((i, label) in sortedB.withIndex()) {
68+
labelGroup[label] = i % k
69+
}
70+
71+
for (currentMask in 0 until numStates) {
72+
val currentNt = Smap.getValue(currentMask)
73+
val alternatives = mutableListOf<Regexp>()
74+
75+
// Exit Rule (Eq 10)
76+
if (currentMask == 0) {
77+
alternatives.add(Epsilon)
78+
}
79+
80+
// Rules for BRACKETS - Beta (Eq 12, 13)
81+
// Brackets consume a token and change state (XOR by 1 bit)
82+
// Rule: Target -> Terminal * Next, where Target = Bit ^ Next
83+
for (brId in sortedB) {
84+
val group = labelGroup.getValue(brId)
85+
val bit = 1 shl group
86+
87+
val nextMask = currentMask xor bit
88+
val nextNt = Smap.getValue(nextMask)
89+
90+
val open = Term("ob--$brId")
91+
val close = Term("cb--$brId")
92+
93+
alternatives.add(open * nextNt)
94+
alternatives.add(close * nextNt)
95+
}
96+
97+
// Rules for PARENTHESES - Alpha (Eq 11 + 14)
98+
// Parentheses are transparent (do not add their parity) and contain a substring.
99+
// Rule: Target -> ( Inner ) * Next, where Target = Inner ^ Next
100+
for (parId in parenthesesIds) {
101+
val open = Term("op--$parId")
102+
val close = Term("cp--$parId")
103+
104+
// We iterate over all possible masks for the content INSIDE the parentheses (innerMask)
105+
for (innerMask in 0 until numStates) {
106+
val S_inner = Smap.getValue(innerMask)
107+
108+
val nextMask = currentMask xor innerMask
109+
val S_next = Smap.getValue(nextMask)
110+
111+
alternatives.add((open * S_inner * close) * S_next)
112+
}
113+
}
114+
115+
alternatives.add(Term("normal") * currentNt)
116+
117+
// Combine all alternatives into one rule for the current non-terminal
118+
if (alternatives.isNotEmpty()) {
119+
var combined = alternatives[0]
120+
for (i in 1 until alternatives.size) {
121+
combined = combined or alternatives[i]
122+
}
123+
currentNt /= combined
124+
}
125+
}
126+
127+
// The starting rule S must lead to a fully balanced state (mask 0)
128+
S /= Smap.getValue(0)
129+
}
130+
}
131+
}
132+
133+
fun dyckBetaGrammarKParity(
134+
parenthesesIds: List<String> = listOf("1"),
135+
bracketsIds: List<String> = listOf("1"),
136+
k: Int = 1
137+
): Grammar {
138+
return object : Grammar() {
139+
val S by Nt().asStart()
140+
141+
init {
142+
val grammar = this
143+
val numStates = 1 shl k // 2^k - Total number of parity states
144+
145+
// Create 2^k non-terminals (states)
146+
// Each state S_mask corresponds to a row where the parity
147+
// of the k parentheses is equal to the 'mask'
148+
val Smap = buildMap<Int, Nt> {
149+
for (mask in 0 until numStates) {
150+
val p = maskToParity(mask, k)
151+
val nt = Nt()
152+
initNtReflectively(grammar, nt, "S$p")
153+
put(mask, nt)
154+
}
155+
}
156+
157+
val sortedP = parenthesesIds.sorted()
158+
val labelGroup = mutableMapOf<String, Int>()
159+
// Assign each group of parentheses the corresponding bit (group)
160+
for ((i, label) in sortedP.withIndex()) {
161+
labelGroup[label] = i % k
162+
}
163+
164+
for (currentMask in 0 until numStates) {
165+
val currentNt = Smap.getValue(currentMask)
166+
val alternatives = mutableListOf<Regexp>()
167+
168+
// Exit Rule (Eq 10)
169+
if (currentMask == 0) {
170+
alternatives.add(Epsilon)
171+
}
172+
173+
// Rules for PARENTHESES - analogically Beta (Eq 12, 13)
174+
// Parentheses consume a token and change state (XOR by 1 bit)
175+
// Rule: Target -> Terminal * Next, where Target = Bit ^ Next
176+
for (parId in sortedP) {
177+
val group = labelGroup.getValue(parId)
178+
val bit = 1 shl group
179+
180+
val nextMask = currentMask xor bit
181+
val nextNt = Smap.getValue(nextMask)
182+
183+
val open = Term("op--$parId")
184+
val close = Term("cp--$parId")
185+
186+
alternatives.add(open * nextNt)
187+
alternatives.add(close * nextNt)
188+
}
189+
190+
// Rules for BRACKETS - analogically Alpha (Eq 11 + 14)
191+
// Brackets are transparent (do not add their parity) and contain a substring.
192+
// Rule: Target -> ( Inner ) * Next, where Target = Inner ^ Next
193+
for (brId in bracketsIds) {
194+
val open = Term("ob--$brId")
195+
val close = Term("cb--$brId")
196+
197+
// We iterate over all possible masks for the content INSIDE the brackets (innerMask)
198+
for (innerMask in 0 until numStates) {
199+
val S_inner = Smap.getValue(innerMask)
200+
201+
val nextMask = currentMask xor innerMask
202+
val S_next = Smap.getValue(nextMask)
203+
204+
alternatives.add((open * S_inner * close) * S_next)
205+
}
206+
}
207+
208+
alternatives.add(Term("normal") * currentNt)
209+
210+
// Combine all alternatives into one rule for the current non-terminal
211+
if (alternatives.isNotEmpty()) {
212+
var combined = alternatives[0]
213+
for (i in 1 until alternatives.size) {
214+
combined = combined or alternatives[i]
215+
}
216+
currentNt /= combined
217+
}
218+
}
219+
220+
// The starting rule S must lead to a fully balanced state (mask 0)
221+
S /= Smap.getValue(0)
222+
}
223+
}
224+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package solver.correctnessTests.dyckKParityAlpha
2+
3+
import org.junit.jupiter.api.Test
4+
import solver.correctnessTests.AbstractCorrectnessTest
5+
6+
class DyckKParityAlphaTest : AbstractCorrectnessTest() {
7+
@Test
8+
override fun checkTreeCorrectnessForGrammar() {
9+
runTests(dyckAlphaGrammarKParity())
10+
runTests(dyckBetaGrammarKParity())
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
digraph G {
2+
start -> 3;
3+
start -> 4;
4+
start -> 5;
5+
start -> 7;
6+
start -> 8;
7+
3->4[label="op--1"];
8+
4->5[label="cp--1"];
9+
7->4[label="ob--1"];
10+
4->8[label="cb--1"];
11+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
digraph g {
2+
labelloc="t"
3+
label=""
4+
subgraph cluster_0{
5+
labelloc="t"
6+
_0_0 [label = "0 Nonterminal S, input: [3, 3]", shape = invtrapezium]
7+
_0_1 [label = "1 Range , input: [3, 3], rsm: [S_0, S_1]", shape = ellipse]
8+
_0_2 [label = "2 Nonterminal Sp, input: [3, 3]", shape = invtrapezium]
9+
_0_3 [label = "3 Range , input: [3, 3], rsm: [Sp_0, Sp_0]", shape = ellipse]
10+
_0_4 [label = "4 Epsilon RSM: Sp_0, input: [3, 3]", shape = invhouse]
11+
_0_0->_0_1
12+
_0_1->_0_2
13+
_0_2->_0_3
14+
_0_3->_0_4
15+
}
16+
17+
subgraph cluster_1{
18+
labelloc="t"
19+
_1_0 [label = "0 Nonterminal S, input: [3, 5]", shape = invtrapezium]
20+
_1_1 [label = "1 Range , input: [3, 5], rsm: [S_0, S_1]", shape = ellipse]
21+
_1_2 [label = "10 Range , input: [4, 5], rsm: [Sp_6, Sp_3]", shape = ellipse]
22+
_1_3 [label = "11 Intermediate input: 4, rsm: Sp_2, input: [3, 4]", shape = plain]
23+
_1_4 [label = "12 Terminal 'cp--1', input: [4, 5]", shape = rectangle]
24+
_1_5 [label = "13 Range , input: [3, 4], rsm: [Sp_0, Sp_2]", shape = ellipse]
25+
_1_6 [label = "14 Range , input: [4, 4], rsm: [Sp_2, Sp_6]", shape = ellipse]
26+
_1_7 [label = "15 Terminal 'op--1', input: [3, 4]", shape = rectangle]
27+
_1_8 [label = "16 Nonterminal Sp, input: [4, 4]", shape = invtrapezium]
28+
_1_9 [label = "2 Nonterminal Sp, input: [3, 5]", shape = invtrapezium]
29+
_1_10 [label = "3 Range , input: [3, 5], rsm: [Sp_0, Sp_4]", shape = ellipse]
30+
_1_11 [label = "4 Intermediate input: 5, rsm: Sp_3, input: [3, 5]", shape = plain]
31+
_1_12 [label = "5 Range , input: [3, 5], rsm: [Sp_0, Sp_3]", shape = ellipse]
32+
_1_13 [label = "6 Range , input: [5, 5], rsm: [Sp_3, Sp_4]", shape = ellipse]
33+
_1_14 [label = "7 Intermediate input: 4, rsm: Sp_6, input: [3, 5]", shape = plain]
34+
_1_15 [label = "8 Nonterminal Sp, input: [5, 5]", shape = invtrapezium]
35+
_1_16 [label = "9 Range , input: [3, 4], rsm: [Sp_0, Sp_6]", shape = ellipse]
36+
_1_0->_1_1
37+
_1_1->_1_9
38+
_1_2->_1_4
39+
_1_3->_1_5
40+
_1_3->_1_6
41+
_1_5->_1_7
42+
_1_6->_1_8
43+
_1_9->_1_10
44+
_1_10->_1_11
45+
_1_11->_1_12
46+
_1_11->_1_13
47+
_1_12->_1_14
48+
_1_13->_1_15
49+
_1_14->_1_16
50+
_1_14->_1_2
51+
_1_16->_1_3
52+
}
53+
54+
subgraph cluster_2{
55+
labelloc="t"
56+
_2_0 [label = "0 Nonterminal S, input: [4, 4]", shape = invtrapezium]
57+
_2_1 [label = "1 Range , input: [4, 4], rsm: [S_0, S_1]", shape = ellipse]
58+
_2_2 [label = "2 Nonterminal Sp, input: [4, 4]", shape = invtrapezium]
59+
_2_3 [label = "3 Range , input: [4, 4], rsm: [Sp_0, Sp_0]", shape = ellipse]
60+
_2_4 [label = "4 Epsilon RSM: Sp_0, input: [4, 4]", shape = invhouse]
61+
_2_0->_2_1
62+
_2_1->_2_2
63+
_2_2->_2_3
64+
_2_3->_2_4
65+
}
66+
67+
subgraph cluster_3{
68+
labelloc="t"
69+
_3_0 [label = "0 Nonterminal S, input: [5, 5]", shape = invtrapezium]
70+
_3_1 [label = "1 Range , input: [5, 5], rsm: [S_0, S_1]", shape = ellipse]
71+
_3_2 [label = "2 Nonterminal Sp, input: [5, 5]", shape = invtrapezium]
72+
_3_3 [label = "3 Range , input: [5, 5], rsm: [Sp_0, Sp_0]", shape = ellipse]
73+
_3_4 [label = "4 Epsilon RSM: Sp_0, input: [5, 5]", shape = invhouse]
74+
_3_0->_3_1
75+
_3_1->_3_2
76+
_3_2->_3_3
77+
_3_3->_3_4
78+
}
79+
80+
subgraph cluster_4{
81+
labelloc="t"
82+
_4_0 [label = "0 Nonterminal S, input: [7, 7]", shape = invtrapezium]
83+
_4_1 [label = "1 Range , input: [7, 7], rsm: [S_0, S_1]", shape = ellipse]
84+
_4_2 [label = "2 Nonterminal Sp, input: [7, 7]", shape = invtrapezium]
85+
_4_3 [label = "3 Range , input: [7, 7], rsm: [Sp_0, Sp_0]", shape = ellipse]
86+
_4_4 [label = "4 Epsilon RSM: Sp_0, input: [7, 7]", shape = invhouse]
87+
_4_0->_4_1
88+
_4_1->_4_2
89+
_4_2->_4_3
90+
_4_3->_4_4
91+
}
92+
93+
subgraph cluster_5{
94+
labelloc="t"
95+
_5_0 [label = "0 Nonterminal S, input: [7, 8]", shape = invtrapezium]
96+
_5_1 [label = "1 Range , input: [7, 8], rsm: [S_0, S_1]", shape = ellipse]
97+
_5_2 [label = "10 Intermediate input: 8, rsm: Si_1, input: [4, 8]", shape = plain]
98+
_5_3 [label = "11 Range , input: [4, 8], rsm: [Si_0, Si_1]", shape = ellipse]
99+
_5_4 [label = "12 Range , input: [8, 8], rsm: [Si_1, Si_4]", shape = ellipse]
100+
_5_5 [label = "13 Terminal 'cb--1', input: [4, 8]", shape = rectangle]
101+
_5_6 [label = "14 Nonterminal Sp, input: [8, 8]", shape = invtrapezium]
102+
_5_7 [label = "2 Nonterminal Sp, input: [7, 8]", shape = invtrapezium]
103+
_5_8 [label = "3 Range , input: [7, 8], rsm: [Sp_0, Sp_4]", shape = ellipse]
104+
_5_9 [label = "4 Intermediate input: 4, rsm: Sp_1, input: [7, 8]", shape = plain]
105+
_5_10 [label = "5 Range , input: [7, 4], rsm: [Sp_0, Sp_1]", shape = ellipse]
106+
_5_11 [label = "6 Range , input: [4, 8], rsm: [Sp_1, Sp_4]", shape = ellipse]
107+
_5_12 [label = "7 Terminal 'ob--1', input: [7, 4]", shape = rectangle]
108+
_5_13 [label = "8 Nonterminal Si, input: [4, 8]", shape = invtrapezium]
109+
_5_14 [label = "9 Range , input: [4, 8], rsm: [Si_0, Si_4]", shape = ellipse]
110+
_5_0->_5_1
111+
_5_1->_5_7
112+
_5_2->_5_3
113+
_5_2->_5_4
114+
_5_3->_5_5
115+
_5_4->_5_6
116+
_5_7->_5_8
117+
_5_8->_5_9
118+
_5_9->_5_10
119+
_5_9->_5_11
120+
_5_10->_5_12
121+
_5_11->_5_13
122+
_5_13->_5_14
123+
_5_14->_5_2
124+
}
125+
126+
subgraph cluster_6{
127+
labelloc="t"
128+
_6_0 [label = "0 Nonterminal S, input: [8, 8]", shape = invtrapezium]
129+
_6_1 [label = "1 Range , input: [8, 8], rsm: [S_0, S_1]", shape = ellipse]
130+
_6_2 [label = "2 Nonterminal Sp, input: [8, 8]", shape = invtrapezium]
131+
_6_3 [label = "3 Range , input: [8, 8], rsm: [Sp_0, Sp_0]", shape = ellipse]
132+
_6_4 [label = "4 Epsilon RSM: Sp_0, input: [8, 8]", shape = invhouse]
133+
_6_0->_6_1
134+
_6_1->_6_2
135+
_6_2->_6_3
136+
_6_3->_6_4
137+
}
138+
139+
}

0 commit comments

Comments
 (0)