Skip to content

Commit

Permalink
feat(types): add more grammar types
Browse files Browse the repository at this point in the history
Support bool, int, float, dict, and list of dict.
  • Loading branch information
syu-w authored Apr 5, 2024
2 parents 3d2ef93 + 1253db5 commit aacff29
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 12 deletions.
3 changes: 3 additions & 0 deletions craft_grammar/_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ def process(
if statement is None:
primitives.append(section)

elif isinstance(section, (int | float | bool | list)):
# If the section is a number, boolean, or list, it's a primitive.
primitives.append(section)
else:
# jsonschema should never let us get here.
raise GrammarSyntaxError(
Expand Down
121 changes: 119 additions & 2 deletions craft_grammar/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
_TRY = "try"


_GrammarType = Dict[str, Any]


class _GrammarBase(abc.ABC):
@classmethod
def __get_validators__(cls):
Expand All @@ -50,10 +53,77 @@ def _grammar_append(cls, entry: List, item: Any) -> None:
_mark_and_append(entry, {key: cls.validate(value)})


_GrammarType = Dict[str, Any]
# Public types for grammar-enabled attributes
class GrammarBool(_GrammarBase):
"""Grammar-enabled bool field."""

__root__: Union[bool, _GrammarType]

# Public types for grammar-enabled attributes
@classmethod
@overrides
def validate(cls, entry):
# GrammarBool entry can be a list if it contains clauses
if isinstance(entry, list):
new_entry = []
for item in entry:
if _is_grammar_clause(item):
cls._grammar_append(new_entry, item)
else:
raise TypeError(f"value must be a list of bool: {entry!r}")
return new_entry

if isinstance(entry, bool):
return entry

raise TypeError(f"value must be a bool: {entry!r}")


class GrammarInt(_GrammarBase):
"""Grammar-enabled integer field."""

__root__: Union[int, _GrammarType]

@classmethod
@overrides
def validate(cls, entry):
# GrammarInt entry can be a list if it contains clauses
if isinstance(entry, list):
new_entry = []
for item in entry:
if _is_grammar_clause(item):
cls._grammar_append(new_entry, item)
else:
raise TypeError(f"value must be a list of integer: {entry!r}")
return new_entry

if isinstance(entry, int):
return entry

raise TypeError(f"value must be a integer: {entry!r}")


class GrammarFloat(_GrammarBase):
"""Grammar-enabled float field."""

__root__: Union[float, _GrammarType]

@classmethod
@overrides
def validate(cls, entry):
# GrammarFloat entry can be a list if it contains clauses
if isinstance(entry, list):
new_entry = []
for item in entry:
if _is_grammar_clause(item):
cls._grammar_append(new_entry, item)
else:
raise TypeError(f"value must be a list of float: {entry!r}")
return new_entry

if isinstance(entry, (int, float)):
return float(entry)

raise TypeError(f"value must be a float: {entry!r}")


class GrammarStr(_GrammarBase):
Expand Down Expand Up @@ -128,6 +198,53 @@ def validate(cls, entry):
raise TypeError(f"value must be a list of single-entry dictionaries: {entry!r}")


class GrammarDict(_GrammarBase):
"""Grammar-enabled dictionary field."""

__root__: Union[Dict[str, Any], _GrammarType]

@classmethod
@overrides
def validate(cls, entry):
# GrammarDict entry can be a list if it contains clauses
if isinstance(entry, list):
new_entry = []
for item in entry:
if _is_grammar_clause(item):
cls._grammar_append(new_entry, item)
else:
raise TypeError(f"value must be a list of dictionaries: {entry!r}")
return new_entry

if isinstance(entry, dict):
return entry

raise TypeError(f"value must be a dictionary: {entry!r}")


class GrammarDictList(_GrammarBase):
"""Grammar-enabled list of dictionary field."""

__root__: Union[List[Dict[str, Any]], _GrammarType]

@classmethod
@overrides
def validate(cls, entry):
# GrammarDictList will always be a list
if isinstance(entry, list):
new_entry = []
for item in entry:
if _is_grammar_clause(item):
cls._grammar_append(new_entry, item)
elif isinstance(item, dict):
new_entry.append(item)
else:
raise TypeError(f"value must be a list of dictionaries: {entry!r}")
return new_entry

raise TypeError(f"value must be a list of dictionary: {entry!r}")


def _ensure_selector_valid(selector: str, *, clause: str) -> None:
"""Verify selector syntax."""
# spaces are not allowed in selector
Expand Down
1 change: 0 additions & 1 deletion tests/unit/test_compound.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@

@pytest.mark.parametrize("scenario", scenarios)
def test_compound_statement(scenario):

processor = GrammarProcessor(
arch=scenario["arch"],
target_arch="armhf",
Expand Down
Loading

0 comments on commit aacff29

Please sign in to comment.