Make control directives generic and add @ifSet
This commit is contained in:
parent
619cae3f45
commit
8c0957c6f1
@ -27,7 +27,8 @@ SubBlock[ws=' \t']:
|
|||||||
|
|
||||||
Directive:
|
Directive:
|
||||||
UserDirective | SousDirective | ForDirective | ImportDirective |
|
UserDirective | SousDirective | ForDirective | ImportDirective |
|
||||||
RecipeEdgeDirective | ResourceEdgeDirective | ListenEdgeDirective
|
RecipeEdgeDirective | ResourceEdgeDirective | ListenEdgeDirective |
|
||||||
|
IfSetDirective
|
||||||
;
|
;
|
||||||
|
|
||||||
UserDirective[ws=' \t']:
|
UserDirective[ws=' \t']:
|
||||||
@ -53,6 +54,10 @@ ForDirective[ws=' \t']:
|
|||||||
)
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
IfSetDirective[ws=' \t']:
|
||||||
|
'@ifSet' variable=DottedIdString /\n/+
|
||||||
|
;
|
||||||
|
|
||||||
ResourceEdgeDirectiveKind:
|
ResourceEdgeDirectiveKind:
|
||||||
'@needs' | '@wants' | '@provides'
|
'@needs' | '@wants' | '@provides'
|
||||||
;
|
;
|
||||||
|
@ -45,8 +45,13 @@ scoml_classes = scoml_grammar.namespaces["scoml"]
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ControlDirective:
|
||||||
|
def iter_over(self, vars: Variables) -> Iterable[Variables]:
|
||||||
|
raise NotImplementedError("Abstract.")
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
class ForDirective:
|
class ForDirective(ControlDirective):
|
||||||
"""
|
"""
|
||||||
For loop_variable in collection
|
For loop_variable in collection
|
||||||
"""
|
"""
|
||||||
@ -57,6 +62,41 @@ class ForDirective:
|
|||||||
# List of literals or str for a variable (by name)
|
# List of literals or str for a variable (by name)
|
||||||
collection: Union[str, List[Any]]
|
collection: Union[str, List[Any]]
|
||||||
|
|
||||||
|
def iter_over(self, vars: Variables):
|
||||||
|
to_iter = self.collection
|
||||||
|
if isinstance(to_iter, str):
|
||||||
|
to_iter = vars.get_dotted(to_iter)
|
||||||
|
|
||||||
|
if not isinstance(to_iter, list):
|
||||||
|
raise ValueError(f"to_iter = {to_iter!r} not a list")
|
||||||
|
|
||||||
|
for item in to_iter:
|
||||||
|
new_vars = Variables(vars)
|
||||||
|
new_vars.set_dotted(self.loop_variable, item)
|
||||||
|
yield new_vars
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(auto_attribs=True)
|
||||||
|
class IfDirective(ControlDirective):
|
||||||
|
def condition_true(self, vars: Variables) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def iter_over(self, vars: Variables) -> Iterable[Variables]:
|
||||||
|
if self.condition_true(vars):
|
||||||
|
yield vars
|
||||||
|
else:
|
||||||
|
yield from ()
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(auto_attribs=True)
|
||||||
|
class IfSetDirective(IfDirective):
|
||||||
|
# Name of the variable to check for existence.
|
||||||
|
check_variable: str
|
||||||
|
|
||||||
|
def condition_true(self, vars: Variables) -> bool:
|
||||||
|
print(f"isset? {self.check_variable} {vars.has_dotted(self.check_variable)}")
|
||||||
|
return vars.has_dotted(self.check_variable)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@attr.s(auto_attribs=True)
|
||||||
class RecipeEdgeDirective:
|
class RecipeEdgeDirective:
|
||||||
@ -94,7 +134,7 @@ class MenuBlock:
|
|||||||
|
|
||||||
user_directive: Optional[str] = None
|
user_directive: Optional[str] = None
|
||||||
sous_directive: Optional[str] = None
|
sous_directive: Optional[str] = None
|
||||||
for_directives: List[ForDirective] = attr.ib(factory=list)
|
control_directives: List[ForDirective] = attr.ib(factory=list)
|
||||||
import_directives: List[str] = attr.ib(factory=list)
|
import_directives: List[str] = attr.ib(factory=list)
|
||||||
recipe_edges: List[RecipeEdgeDirective] = attr.ib(factory=list)
|
recipe_edges: List[RecipeEdgeDirective] = attr.ib(factory=list)
|
||||||
resource_edges: List[ResourceEdgeDirective] = attr.ib(factory=list)
|
resource_edges: List[ResourceEdgeDirective] = attr.ib(factory=list)
|
||||||
@ -114,7 +154,7 @@ class MenuRecipe:
|
|||||||
|
|
||||||
user_directive: Optional[str] = None
|
user_directive: Optional[str] = None
|
||||||
sous_directive: Optional[str] = None
|
sous_directive: Optional[str] = None
|
||||||
for_directives: List[ForDirective] = attr.ib(factory=list)
|
control_directives: List[ForDirective] = attr.ib(factory=list)
|
||||||
recipe_edges: List[RecipeEdgeDirective] = attr.ib(factory=list)
|
recipe_edges: List[RecipeEdgeDirective] = attr.ib(factory=list)
|
||||||
resource_edges: List[ResourceEdgeDirective] = attr.ib(factory=list)
|
resource_edges: List[ResourceEdgeDirective] = attr.ib(factory=list)
|
||||||
listen_edges: List[ListenEdgeDirective] = attr.ib(factory=list)
|
listen_edges: List[ListenEdgeDirective] = attr.ib(factory=list)
|
||||||
@ -183,6 +223,18 @@ def convert_textx_recipe(txrecipe_or_subblock, parent: Optional[MenuBlock]):
|
|||||||
or convert_textx_resource(directive.resource),
|
or convert_textx_resource(directive.resource),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
elif isinstance(directive, scoml_classes["ForDirective"]):
|
||||||
|
for_list = directive.collection or convert_textx_value(directive.list)
|
||||||
|
assert isinstance(for_list, list) or isinstance(for_list, str)
|
||||||
|
recipe.control_directives.append(
|
||||||
|
ForDirective(directive.loop_variable, for_list)
|
||||||
|
)
|
||||||
|
elif isinstance(directive, scoml_classes["IfSetDirective"]):
|
||||||
|
var = directive.variable
|
||||||
|
assert isinstance(var, str)
|
||||||
|
recipe.control_directives.append(
|
||||||
|
IfSetDirective(var)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown directive {directive}")
|
raise ValueError(f"Unknown directive {directive}")
|
||||||
|
|
||||||
@ -224,8 +276,14 @@ def convert_textx_block(txblock, parent: Optional[MenuBlock]) -> MenuBlock:
|
|||||||
elif isinstance(directive, scoml_classes["ForDirective"]):
|
elif isinstance(directive, scoml_classes["ForDirective"]):
|
||||||
for_list = directive.collection or convert_textx_value(directive.list)
|
for_list = directive.collection or convert_textx_value(directive.list)
|
||||||
assert isinstance(for_list, list) or isinstance(for_list, str)
|
assert isinstance(for_list, list) or isinstance(for_list, str)
|
||||||
block.for_directives.append(
|
block.control_directives.append(
|
||||||
ForDirective(directive.loop_variable, for_list,)
|
ForDirective(directive.loop_variable, for_list)
|
||||||
|
)
|
||||||
|
elif isinstance(directive, scoml_classes["IfSetDirective"]):
|
||||||
|
var = directive.variable
|
||||||
|
assert isinstance(var, str)
|
||||||
|
block.control_directives.append(
|
||||||
|
IfSetDirective(var)
|
||||||
)
|
)
|
||||||
elif isinstance(directive, scoml_classes["ImportDirective"]):
|
elif isinstance(directive, scoml_classes["ImportDirective"]):
|
||||||
block.import_directives.append(directive.importee)
|
block.import_directives.append(directive.importee)
|
||||||
@ -342,7 +400,7 @@ class MenuLoader:
|
|||||||
a: Union[MenuBlock, MenuRecipe] = referrer
|
a: Union[MenuBlock, MenuRecipe] = referrer
|
||||||
strip = 0
|
strip = 0
|
||||||
while a != first_common_ancestor:
|
while a != first_common_ancestor:
|
||||||
strip += len(a.for_directives)
|
strip += len(a.control_directives)
|
||||||
parent = a.parent
|
parent = a.parent
|
||||||
assert parent is not None
|
assert parent is not None
|
||||||
a = parent
|
a = parent
|
||||||
@ -350,7 +408,7 @@ class MenuLoader:
|
|||||||
a = menu_recipe
|
a = menu_recipe
|
||||||
extra = 0
|
extra = 0
|
||||||
while a != first_common_ancestor:
|
while a != first_common_ancestor:
|
||||||
extra += len(a.for_directives)
|
extra += len(a.control_directives)
|
||||||
parent = a.parent
|
parent = a.parent
|
||||||
assert parent is not None
|
assert parent is not None
|
||||||
a = parent
|
a = parent
|
||||||
@ -394,7 +452,7 @@ class MenuLoader:
|
|||||||
if recipe_class is None:
|
if recipe_class is None:
|
||||||
raise ValueError(f"No recipe class found for {recipe.kind!r}")
|
raise ValueError(f"No recipe class found for {recipe.kind!r}")
|
||||||
|
|
||||||
fors = fors + tuple(recipe.for_directives)
|
fors = fors + tuple(recipe.control_directives)
|
||||||
|
|
||||||
if recipe.user_directive:
|
if recipe.user_directive:
|
||||||
applicable_user = recipe.user_directive
|
applicable_user = recipe.user_directive
|
||||||
@ -411,7 +469,7 @@ class MenuLoader:
|
|||||||
assert applicable_user is not None
|
assert applicable_user is not None
|
||||||
|
|
||||||
sous_vars = self._head.variables[sous]
|
sous_vars = self._head.variables[sous]
|
||||||
for context_vars, for_indices in self._for_apply(fors, sous_vars, tuple()):
|
for context_vars, for_indices in self._control_apply(fors, sous_vars, tuple()):
|
||||||
context = RecipeContext(
|
context = RecipeContext(
|
||||||
sous=sous,
|
sous=sous,
|
||||||
user=applicable_user,
|
user=applicable_user,
|
||||||
@ -439,7 +497,7 @@ class MenuLoader:
|
|||||||
sous_mask: Optional[Set[str]],
|
sous_mask: Optional[Set[str]],
|
||||||
applicable_user: Optional[str],
|
applicable_user: Optional[str],
|
||||||
):
|
):
|
||||||
fors = fors + tuple(block.for_directives)
|
fors = fors + tuple(block.control_directives)
|
||||||
|
|
||||||
if block.user_directive:
|
if block.user_directive:
|
||||||
applicable_user = block.user_directive
|
applicable_user = block.user_directive
|
||||||
@ -483,7 +541,7 @@ class MenuLoader:
|
|||||||
# TODO(feature): add edges
|
# TODO(feature): add edges
|
||||||
|
|
||||||
# add fors
|
# add fors
|
||||||
fors = fors + tuple(recipe.for_directives)
|
fors = fors + tuple(recipe.control_directives)
|
||||||
|
|
||||||
if recipe.sous_directive:
|
if recipe.sous_directive:
|
||||||
applicable_souss = self._head.get_souss_for_hostspec(recipe.sous_directive)
|
applicable_souss = self._head.get_souss_for_hostspec(recipe.sous_directive)
|
||||||
@ -493,7 +551,7 @@ class MenuLoader:
|
|||||||
|
|
||||||
for sous in applicable_souss:
|
for sous in applicable_souss:
|
||||||
sous_vars = self._head.variables[sous]
|
sous_vars = self._head.variables[sous]
|
||||||
for _vars, for_indices in self._for_apply(fors, sous_vars, tuple()):
|
for _vars, for_indices in self._control_apply(fors, sous_vars, tuple()):
|
||||||
instance = self._recipes[recipe][(sous, for_indices)] # noqa
|
instance = self._recipes[recipe][(sous, for_indices)] # noqa
|
||||||
|
|
||||||
for recipe_edge in recipe.recipe_edges:
|
for recipe_edge in recipe.recipe_edges:
|
||||||
@ -573,7 +631,7 @@ class MenuLoader:
|
|||||||
|
|
||||||
# TODO(feature): add edges
|
# TODO(feature): add edges
|
||||||
|
|
||||||
fors = fors + tuple(block.for_directives)
|
fors = fors + tuple(block.control_directives)
|
||||||
|
|
||||||
if block.sous_directive:
|
if block.sous_directive:
|
||||||
applicable_souss = self._head.get_souss_for_hostspec(block.sous_directive)
|
applicable_souss = self._head.get_souss_for_hostspec(block.sous_directive)
|
||||||
@ -604,27 +662,18 @@ class MenuLoader:
|
|||||||
unit, tuple(), self._head.get_souss_for_hostspec("all"), sous_subset
|
unit, tuple(), self._head.get_souss_for_hostspec("all"), sous_subset
|
||||||
)
|
)
|
||||||
|
|
||||||
def _for_apply(
|
def _control_apply(
|
||||||
self, fors: Tuple[ForDirective, ...], vars: "Variables", accum: Tuple[int, ...]
|
self, controls: Tuple[ControlDirective, ...], vars: "Variables", accum: Tuple[int, ...]
|
||||||
) -> Iterable[Tuple["Variables", Tuple[int, ...]]]:
|
) -> Iterable[Tuple["Variables", Tuple[int, ...]]]:
|
||||||
if not fors:
|
if not controls:
|
||||||
yield vars, accum
|
yield vars, accum
|
||||||
return
|
return
|
||||||
|
|
||||||
head = fors[0]
|
head = controls[0]
|
||||||
tail = fors[1:]
|
tail = controls[1:]
|
||||||
|
|
||||||
to_iter = head.collection
|
for idx, new_vars in enumerate(head.iter_over(vars)):
|
||||||
if isinstance(to_iter, str):
|
yield from self._control_apply(tail, new_vars, accum + (idx,))
|
||||||
to_iter = vars.get_dotted(to_iter)
|
|
||||||
|
|
||||||
if not isinstance(to_iter, list):
|
|
||||||
raise ValueError(f"to_iter = {to_iter!r} not a list")
|
|
||||||
|
|
||||||
for idx, item in enumerate(to_iter):
|
|
||||||
new_vars = Variables(vars)
|
|
||||||
new_vars.set_dotted(head.loop_variable, item)
|
|
||||||
yield from self._for_apply(tail, new_vars, accum + (idx,))
|
|
||||||
|
|
||||||
def load_menus_in_dir(self) -> RecipeDag:
|
def load_menus_in_dir(self) -> RecipeDag:
|
||||||
dag = RecipeDag()
|
dag = RecipeDag()
|
||||||
|
Loading…
Reference in New Issue
Block a user