The GBNF parser now correctly handles SORE's overloaded + operator: - + inside (a+b+c) → alternation (not repetition) - + outside parens → repetition - +? and +* compound operators → normalized to Star Also adds implicit concatenation when LPAREN follows a repetition, so a+(b+c) parses as a+ followed by (b|c). 28 tests pass (13 new disjunction/compound tests). Full suite: 212 passed.
108 lines
3.8 KiB
Python
108 lines
3.8 KiB
Python
"""Tests for SORE → GBNF converter."""
|
|
import pytest
|
|
from bex.gbnf import to_gbnf, to_gbnf_with_rules
|
|
|
|
|
|
class TestToGBNF:
|
|
def test_literal(self):
|
|
assert to_gbnf('mockk') == 'root ::= "mockk"'
|
|
|
|
def test_concat(self):
|
|
assert to_gbnf('raise.ValueError') == 'root ::= "raise" "ValueError"'
|
|
|
|
def test_plus_group(self):
|
|
assert to_gbnf('(append)+') == 'root ::= "append"+'
|
|
|
|
def test_plus_concat(self):
|
|
assert to_gbnf('raise.(ValueError)+') == 'root ::= "raise" "ValueError"+'
|
|
|
|
def test_nested_optional_plus(self):
|
|
assert to_gbnf('assertEquals.(of.(assertFailsWith)?)+') == \
|
|
'root ::= "assertEquals" ("of" "assertFailsWith"?)+'
|
|
|
|
def test_long_concat(self):
|
|
assert to_gbnf('filesIn.filter.contains.assertTrue.(hasImport)+') == \
|
|
'root ::= "filesIn" "filter" "contains" "assertTrue" "hasImport"+'
|
|
|
|
def test_simple_concat(self):
|
|
assert to_gbnf('trim.lowercase.(warn)+') == 'root ::= "trim" "lowercase" "warn"+'
|
|
|
|
def test_flat_concat(self):
|
|
assert to_gbnf('DoclingConfig.assertThatThrownBy.validateCriticalSettings.isInstanceOf.hasMessageContaining') == \
|
|
'root ::= "DoclingConfig" "assertThatThrownBy" "validateCriticalSettings" "isInstanceOf" "hasMessageContaining"'
|
|
|
|
def test_simple_plus(self):
|
|
assert to_gbnf('(abort)+') == 'root ::= "abort"+'
|
|
|
|
def test_concat_with_plus(self):
|
|
assert to_gbnf('return.(url_for)+') == 'root ::= "return" "url_for"+'
|
|
|
|
def test_star(self):
|
|
assert to_gbnf('(foo)*') == 'root ::= "foo"*'
|
|
|
|
def test_optional(self):
|
|
assert to_gbnf('(bar)?') == 'root ::= "bar"?'
|
|
|
|
|
|
class TestToGBNFWithRules:
|
|
def test_named_rule(self):
|
|
result = to_gbnf_with_rules('raise.(ValueError)+', name='my-pattern')
|
|
assert result == 'my-pattern ::= "raise" "ValueError"+'
|
|
|
|
def test_default_name(self):
|
|
result = to_gbnf_with_rules('mockk')
|
|
assert result == 'root ::= "mockk"'
|
|
|
|
def test_nested(self):
|
|
result = to_gbnf_with_rules('assertEquals.(of.(assertFailsWith)?)+')
|
|
assert result == 'root ::= "assertEquals" ("of" "assertFailsWith"?)+'
|
|
|
|
|
|
class TestGBNFDisjunction:
|
|
"""Test + as disjunction inside parentheses (SORE convention)."""
|
|
|
|
def test_simple_disjunction(self):
|
|
assert to_gbnf('(a+b)') == 'root ::= "a" | "b"'
|
|
|
|
def test_disjunction_with_rep(self):
|
|
assert to_gbnf('(a+b)+') == 'root ::= ("a" | "b")+'
|
|
|
|
def test_disjunction_optional(self):
|
|
assert to_gbnf('(a+b)?') == 'root ::= ("a" | "b")?'
|
|
|
|
def test_disjunction_star(self):
|
|
assert to_gbnf('(a+b)*') == 'root ::= ("a" | "b")*'
|
|
|
|
def test_disjunction_in_concat(self):
|
|
assert to_gbnf('warn+.(BAD_REQUEST+CONFLICT)+') == \
|
|
'root ::= "warn"+ ("BAD_REQUEST" | "CONFLICT")+'
|
|
|
|
def test_four_way_disjunction(self):
|
|
assert to_gbnf('(assertEquals+authorize+coEvery+coVerify)+') == \
|
|
'root ::= ("assertEquals" | "authorize" | "coEvery" | "coVerify")+'
|
|
|
|
def test_disjunction_parenthesized_in_concat(self):
|
|
assert to_gbnf('a+(b+c)') == 'root ::= "a"+ ("b" | "c")'
|
|
|
|
def test_disjunction_rep_then_disjunction(self):
|
|
assert to_gbnf('(a+b)+.(c+d)') == 'root ::= ("a" | "b")+ ("c" | "d")'
|
|
|
|
|
|
class TestGBNFCompoundRepetition:
|
|
"""Test compound repetition operators: +?, +*, etc."""
|
|
|
|
def test_plus_question(self):
|
|
assert to_gbnf('(a)+?') == 'root ::= "a"*'
|
|
|
|
def test_plus_star(self):
|
|
assert to_gbnf('(a)+*') == 'root ::= "a"*'
|
|
|
|
def test_question_plus(self):
|
|
assert to_gbnf('(a)?+') == 'root ::= "a"+'
|
|
|
|
def test_flask_pattern(self):
|
|
result = to_gbnf('return.(key+self)+?.(Markup+UUID)+?.to_json+?')
|
|
assert result == 'root ::= "return" ("key" | "self")* ("Markup" | "UUID")* "to_json"*'
|
|
|
|
def test_double_plus(self):
|
|
assert to_gbnf('a++.b') == 'root ::= "a"+ "b"'
|