diff --git a/src/gboml/ast/base.py b/src/gboml/ast/base.py index 5f7388e2dc1bc754efc5a6a88b8683a8688e184f..dad9d10f43915b1fbb481b343fcf782dc2868626 100644 --- a/src/gboml/ast/base.py +++ b/src/gboml/ast/base.py @@ -12,3 +12,7 @@ class Meta: @dataclass class GBOMLObject: meta: Optional[Meta] = field(default=None, kw_only=True, repr=False) + +@dataclass +class NamedGBOMLObject(GBOMLObject): + name: str \ No newline at end of file diff --git a/src/gboml/ast/constraints.py b/src/gboml/ast/constraints.py index 42038b5e90c4a36d28a0acd32294766762745a02..eeae2791b714478b00274bc7a6919f760cf6c784 100644 --- a/src/gboml/ast/constraints.py +++ b/src/gboml/ast/constraints.py @@ -26,7 +26,7 @@ class StdConstraint(Constraint): op: Operator rhs: Expression loop: Optional[Loop] = None - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass @@ -34,7 +34,7 @@ class SOSConstraint(Constraint): type: SOSType content: Array loop: Optional[Loop] = None - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass diff --git a/src/gboml/ast/expressions.py b/src/gboml/ast/expressions.py index 97a50ea5d5e4f07c9ff83c72a8ad7826a774dd92..fc7e42165ee7478518a487c373d9b826ad9f706b 100644 --- a/src/gboml/ast/expressions.py +++ b/src/gboml/ast/expressions.py @@ -13,6 +13,3 @@ Expression = int | float | ExpressionObj @dataclass class BoolExpression(GBOMLObject): pass - - - diff --git a/src/gboml/ast/hyperedges.py b/src/gboml/ast/hyperedges.py index e09ce5f544f8ca3e039ed9bf68e88c4256866ef4..df46a8392c2af9195daebed462b215734f8ca8b3 100644 --- a/src/gboml/ast/hyperedges.py +++ b/src/gboml/ast/hyperedges.py @@ -3,14 +3,14 @@ from typing import Optional from gboml.ast.importable import Extends from gboml.ast.loops import Loop -from gboml.ast.base import GBOMLObject +from gboml.ast.base import NamedGBOMLObject from gboml.ast.constraints import Constraint, CtrActivation from gboml.ast.path import VarOrParam from gboml.ast.variables import Definition @dataclass -class HyperEdge(GBOMLObject): +class HyperEdge(NamedGBOMLObject): pass @@ -21,15 +21,16 @@ class HyperEdgeDefinition(HyperEdge): parameters: list[Definition] = field(default_factory=list) constraints: list[Constraint] = field(default_factory=list) activations: list[CtrActivation] = field(default_factory=list) - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass class HyperEdgeGenerator(HyperEdge): - name: VarOrParam + name: str + indices: list["RValue"] loop: Loop import_from: Optional[Extends] = None parameters: list[Definition] = field(default_factory=list) constraints: list[Constraint] = field(default_factory=list) activations: list[CtrActivation] = field(default_factory=list) - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) diff --git a/src/gboml/ast/nodes.py b/src/gboml/ast/nodes.py index 14aa719a901a5051aa90c43d6184f58a41a8f4d3..0092e3a931ea61d6a6aab8eeeca29b6de1990ca6 100644 --- a/src/gboml/ast/nodes.py +++ b/src/gboml/ast/nodes.py @@ -3,17 +3,16 @@ from typing import Optional from gboml.ast.loops import Loop from gboml.ast.activation import Activation -from gboml.ast.base import GBOMLObject +from gboml.ast.base import NamedGBOMLObject from gboml.ast.constraints import Constraint from gboml.ast.importable import Extends -from gboml.ast.path import VarOrParam from gboml.ast.hyperedges import HyperEdge from gboml.ast.objectives import Objective from gboml.ast.variables import Definition, VariableDefinition, ScopeChange @dataclass -class Node(GBOMLObject): +class Node(NamedGBOMLObject): pass @@ -28,12 +27,13 @@ class NodeDefinition(Node): constraints: list[Constraint] = field(default_factory=list) objectives: list[Objective] = field(default_factory=list) activations: list[Activation] = field(default_factory=list) - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass class NodeGenerator(Node): - name: VarOrParam + name: str + indices: list["RValue"] loop: Loop import_from: Optional[Extends] = None parameters: list[Definition] = field(default_factory=list) @@ -43,4 +43,4 @@ class NodeGenerator(Node): constraints: list[Constraint] = field(default_factory=list) objectives: list[Objective] = field(default_factory=list) activations: list[Activation] = field(default_factory=list) - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) diff --git a/src/gboml/ast/objectives.py b/src/gboml/ast/objectives.py index 458decd69b57f60ced67a9b0dc3e29e6eadcb957..409d695e99462d01e4bbc8e68a05a51d34425199 100644 --- a/src/gboml/ast/objectives.py +++ b/src/gboml/ast/objectives.py @@ -19,7 +19,7 @@ class Objective(GBOMLObject): name: Optional[str] expression: Expression loop: Optional[Loop] = None - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass class ObjActivation(Activation): diff --git a/src/gboml/ast/variables.py b/src/gboml/ast/variables.py index 57562838df277bdf789949f1e5e624f479ef01cd..d4e1e59987ffbfddb2bf68dbef4519286916a254 100644 --- a/src/gboml/ast/variables.py +++ b/src/gboml/ast/variables.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from enum import Enum from typing import Optional -from gboml.ast.base import GBOMLObject +from gboml.ast.base import GBOMLObject, NamedGBOMLObject from gboml.ast.path import VarOrParam from gboml.ast.rvalue import RValue @@ -24,42 +24,39 @@ class DefinitionType(Enum): @dataclass -class Definition(GBOMLObject): - pass +class Definition(NamedGBOMLObject): + name: str @dataclass class ConstantDefinition(Definition): - name: str value: RValue - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass class ExpressionDefinition(Definition): - name: str value: RValue - tags: list[str] = field(default_factory=list) - + tags: set[str] = field(default_factory=set) @dataclass class FunctionDefinition(Definition): - name: str args: list[str] value: RValue - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass -class VariableDefinition(GBOMLObject): +class VariableDefinition(NamedGBOMLObject): + name: str + indices: list[str] scope: VarScope type: VarType - name: VarOrParam import_from: Optional[VarOrParam] = None - tags: list[str] = field(default_factory=list) + tags: set[str] = field(default_factory=set) @dataclass class ScopeChange(GBOMLObject): - var: str + name: str scope: VarScope \ No newline at end of file diff --git a/src/gboml/gboml.lark b/src/gboml/gboml.lark index 49597aaad8d3164f99877cb2afd3b00dcfdb1c65..a1323abb666d131f5b2fe0b1070a04e7794bd263 100644 --- a/src/gboml/gboml.lark +++ b/src/gboml/gboml.lark @@ -37,7 +37,7 @@ extends: "extends" var_or_param ["from" STRING] // NODES ?node: node_definition | node_import node_definition: _block_shortcut{_node_header, _node_content} -_node_header: "#NODE" var_or_param [extends] [loop] tags +_node_header: "#NODE" ID olist{index} [extends] [loop] tags _node_content: parameters_block program_block variables_block constraints_block objectives_block parameters_block: (_block_repeat_or_pass{_opt_param_header,definition})? @@ -57,14 +57,15 @@ variable_scope_change: ID SCOPE ";" ?hyperedge: hyperedge_definition | hyperedge_import hyperedge_definition: _block_shortcut{_hyperedge_header, _hyperedge_content} -_hyperedge_header: "#HYPEREDGE" var_or_param [extends] [loop] tags +_hyperedge_header: "#HYPEREDGE" ID olist{index} [extends] [loop] tags _hyperedge_content: parameters_block constraints_block hyperedge_import: "#HYPEREDGE" ID "=" "import" var_or_param "from" STRING hyperedge_redefs hyperedge_redefs: "with" definition* | ";" // VARIABLES -variable_definition: SCOPE [VTYPE] ":" separated_list{var_or_param,","} [_LARROW separated_list{var_or_param, ","}] tags ";" +variable_definition: SCOPE [VTYPE] ":" separated_list{variable_name,","} [_LARROW separated_list{var_or_param, ","}] tags ";" +variable_name: ID olist{index} SCOPE: "internal" | "external" VTYPE: "binary" | "continuous" | "integer" _LARROW.1: "<-" diff --git a/src/gboml/parsing.py b/src/gboml/parsing.py index 98570ab0cd4fe52dec903ac844520e63cdd50b34..8d5e301562605c5153a1d852393ea34764dc0f77 100644 --- a/src/gboml/parsing.py +++ b/src/gboml/parsing.py @@ -54,7 +54,10 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: as_list = { "objectives_block", "constraints_block", "parameters_block", "global_block", "olist", "mlist", "node_redefs", - "hyperedge_redefs", "separated_list", "separated_maybe_empty_list", + "hyperedge_redefs", "separated_list", "separated_maybe_empty_list" + } + + as_sets = { "tags" } @@ -93,12 +96,15 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: "ctr_deactivate": lambda *x, meta: CtrActivation(ActivationType.deactivate, *x, meta=meta), "obj_activate": lambda *x, meta: ObjActivation(ActivationType.activate, *x, meta=meta), "obj_deactivate": lambda *x, meta: ObjActivation(ActivationType.deactivate, *x, meta=meta), - "extends": Extends + "extends": Extends, + "variable_name": lambda *x, meta: x } def __default__(self, data, children, _): if data in self.as_list: return list(children) + if data in self.as_sets: + return set(children) if data in self.to_obj: return self.to_obj[data](*children, meta=gen_meta(data)) raise RuntimeError(f"Unknown rule {data}") @@ -125,8 +131,8 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: def program_block(self, meta: Meta, *childrens: list[Node | HyperEdge]) -> NodesAndHyperEdges: return self.NodesAndHyperEdges([x for x in childrens if isinstance(x, Node)], [x for x in childrens if isinstance(x, HyperEdge)]) - def hyperedge_definition(self, meta: Meta, name: VarOrParam, extends: Optional[Extends], - loop: Optional[Loop], tags: list[str], param_block: list[Definition] = None, + def hyperedge_definition(self, meta: Meta, name: str, indices: list[RValue], extends: Optional[Extends], + loop: Optional[Loop], tags: set[str], param_block: list[Definition] = None, constraint_block: list[Constraint | CtrActivation] = None): constraint_block = constraint_block or [] activations = [x for x in constraint_block if isinstance(x, CtrActivation)] @@ -134,16 +140,16 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: param_block = param_block or [] if loop is None: - if len(name.path) != 1 or len(name.path[0].indices) != 0: - raise Exception(f"Invalid name for node: {name}") - return HyperEdgeDefinition(name.path[0].name, extends, param_block, constraint_block, + return HyperEdgeDefinition(name, extends, param_block, constraint_block, activations, tags, meta=meta) else: - return HyperEdgeGenerator(name, loop, extends, param_block, constraint_block, + if len(indices) == 0: + raise Exception(f"Invalid name for node: {name}") + return HyperEdgeGenerator(name, indices, loop, extends, param_block, constraint_block, activations, tags, meta=meta) - def node_definition(self, meta: Meta, name: VarOrParam, extends: Optional[Extends], - loop: Optional[Loop], tags: list[str], + def node_definition(self, meta: Meta, name: str, indices: list[RValue], extends: Optional[Extends], + loop: Optional[Loop], tags: set[str], param_block: list[Definition] = None, subprogram_block: NodesAndHyperEdges = None, variable_block: list[VariableDefinition] = None, constraint_block: list[Constraint | CtrActivation] = None, @@ -159,14 +165,14 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: objectives_block = [x for x in objectives_block if isinstance(x, Objective)] if loop is None: - if len(name.path) != 1 or len(name.path[0].indices) != 0: - raise Exception(f"Invalid name for node: {name}") - return NodeDefinition(name.path[0].name, extends, param_block, + return NodeDefinition(name, extends, param_block, subprogram_block.nodes, subprogram_block.hyperedges, variable_block, constraint_block, objectives_block, activations, tags, meta=meta) else: - return NodeGenerator(name, loop, extends, param_block, + if len(indices) == 0: + raise Exception(f"Invalid name for node: {name}") + return NodeGenerator(name, indices, loop, extends, param_block, subprogram_block.nodes, subprogram_block.hyperedges, variable_block, constraint_block, objectives_block, activations, tags, meta=meta) @@ -184,13 +190,13 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: def start(self, meta: Meta, time_horizon: Optional[int], global_defs: list[Definition], nodes_hyperedges: NodesAndHyperEdges): return GBOMLGraph(time_horizon, global_defs, nodes_hyperedges.nodes, nodes_hyperedges.hyperedges, meta=meta) - def variable_definition(self, meta: Meta, scope: VarScope, type: Optional[VarType], names: list[VarOrParam], - imports_from: Optional[list[VarOrParam]], tags: list[str]): + def variable_definition(self, meta: Meta, scope: VarScope, type: Optional[VarType], names: list[(str, list[str])], + imports_from: Optional[list[VarOrParam]], tags: set[str]): if imports_from is not None and len(imports_from) != len(names): raise Exception("Invalid variable import, numbers of variables on the left and on the right-side of " "`<-` don't match") for name, import_from in zip(names, imports_from or repeat(None, len(names))): - yield VariableDefinition(scope, type or VarType.continuous, name, import_from, tags, meta=meta) + yield VariableDefinition(name[0], name[1], scope, type or VarType.continuous, import_from, tags, meta=meta) def variables_block(self, _: Meta, *defs: Tuple[Iterable[VariableDefinition]]): return [vd for iterable in defs for vd in iterable] @@ -205,7 +211,7 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: return Array(entries, meta=meta) raise Exception("An array cannot contain dictionary entries (and conversely)") - def definition(self, meta: Meta, name: str, args: Optional[list[str]], typ: DefinitionType, val: RValue, tags: list[str]): + def definition(self, meta: Meta, name: str, args: Optional[list[str]], typ: DefinitionType, val: RValue, tags: set[str]): if args is not None: if typ != DefinitionType.expression: raise Exception("Functions can only be defined as expressions (use `<-` instead of `=`)") @@ -215,4 +221,5 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: else: return ConstantDefinition(name, val, tags, meta=meta) + return GBOMLLarkTransformer().transform(tree)