diff --git a/src/gboml/ast/constraints.py b/src/gboml/ast/constraints.py index 38ffc1fa95e8ea1e543c5c2f3cb4ead0545a121a..17cc19c3dcb86f79e019099f6a412ca7fdf796ae 100644 --- a/src/gboml/ast/constraints.py +++ b/src/gboml/ast/constraints.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum from typing import Optional @@ -24,11 +24,13 @@ class StdConstraint(Constraint): lhs: Expression op: Operator rhs: Expression - loop: Optional[Loop] + loop: Optional[Loop] = None + tags: list[str] = field(default_factory=list) @dataclass class SOSConstraint(Constraint): type: SOSType content: Array - loop: Optional[Loop] + loop: Optional[Loop] = None + tags: list[str] = field(default_factory=list) diff --git a/src/gboml/ast/hyperedges.py b/src/gboml/ast/hyperedges.py index 2f894647f133f53da9f6c542b08eb0c206592a4d..ddd209717cd161e25007ee77f7956a84e4993978 100644 --- a/src/gboml/ast/hyperedges.py +++ b/src/gboml/ast/hyperedges.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from gboml.ast import Loop from gboml.ast.base import GBOMLObject @@ -15,16 +15,18 @@ class HyperEdge(GBOMLObject): @dataclass class HyperEdgeDefinition(HyperEdge): name: str - parameters: list[Definition] - constraints: list[Constraint] + parameters: list[Definition] = field(default_factory=list) + constraints: list[Constraint] = field(default_factory=list) + tags: list[str] = field(default_factory=list) @dataclass class HyperEdgeGenerator(HyperEdge): name: VarOrParam loop: Loop - parameters: list[Definition] - constraints: list[Constraint] + parameters: list[Definition] = field(default_factory=list) + constraints: list[Constraint] = field(default_factory=list) + tags: list[str] = field(default_factory=list) @dataclass diff --git a/src/gboml/ast/nodes.py b/src/gboml/ast/nodes.py index 3b6a7d7cac40174e80fbfd4cc46692d29b9664c6..2b452b35e05bfcd596b562cd5a1cfa4cfb3427f8 100644 --- a/src/gboml/ast/nodes.py +++ b/src/gboml/ast/nodes.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from gboml.ast import Loop from gboml.ast.base import GBOMLObject @@ -17,24 +17,26 @@ class Node(GBOMLObject): @dataclass class NodeDefinition(Node): name: str - parameters: list[Definition] - nodes: list[Node] - hyperedges: list[HyperEdge] - variables: list[VariableDefinition] - constraints: list[Constraint] - objectives: list[Objective] + parameters: list[Definition] = field(default_factory=list) + nodes: list[Node] = field(default_factory=list) + hyperedges: list[HyperEdge] = field(default_factory=list) + variables: list[VariableDefinition] = field(default_factory=list) + constraints: list[Constraint] = field(default_factory=list) + objectives: list[Objective] = field(default_factory=list) + tags: list[str] = field(default_factory=list) @dataclass class NodeGenerator(Node): name: VarOrParam loop: Loop - parameters: list[Definition] - nodes: list[Node] - hyperedges: list[HyperEdge] - variables: list[VariableDefinition] - constraints: list[Constraint] - objectives: list[Objective] + parameters: list[Definition] = field(default_factory=list) + nodes: list[Node] = field(default_factory=list) + hyperedges: list[HyperEdge] = field(default_factory=list) + variables: list[VariableDefinition] = field(default_factory=list) + constraints: list[Constraint] = field(default_factory=list) + objectives: list[Objective] = field(default_factory=list) + tags: list[str] = field(default_factory=list) @dataclass @@ -42,5 +44,5 @@ class NodeImport(Node): name: str imported_name: VarOrParam imported_from: str - scope_changes: list[ScopeChange] - redefinitions: list[Definition] + scope_changes: list[ScopeChange] = field(default_factory=list) + redefinitions: list[Definition] = field(default_factory=list) diff --git a/src/gboml/ast/objectives.py b/src/gboml/ast/objectives.py index 5420e365311fb10de2ecc0881c01148482b9a667..c78f5ee12b61262b3a07c66debb4b062a74fd2da 100644 --- a/src/gboml/ast/objectives.py +++ b/src/gboml/ast/objectives.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum from typing import Optional @@ -17,4 +17,5 @@ class Objective(GBOMLObject): type: ObjType name: Optional[str] expression: Expression - loop: Optional[Loop] + loop: Optional[Loop] = None + tags: list[str] = field(default_factory=list) diff --git a/src/gboml/ast/variables.py b/src/gboml/ast/variables.py index f9fe0af274bdd3a61554d8985fb3c5d55a65cd6d..fd761cee0896a06e73321b35ffec912d0fba40a2 100644 --- a/src/gboml/ast/variables.py +++ b/src/gboml/ast/variables.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum from typing import Optional @@ -22,6 +22,7 @@ class VarType(Enum): class Definition(GBOMLObject): name: str value: RValue + tags: list[str] = field(default_factory=list) @dataclass @@ -29,7 +30,8 @@ class VariableDefinition(GBOMLObject): scope: VarScope type: VarType name: VarOrParam - import_from: Optional[VarOrParam] + import_from: Optional[VarOrParam] = None + tags: list[str] = field(default_factory=list) @dataclass diff --git a/src/gboml/gboml.lark b/src/gboml/gboml.lark index 33149d60704efdaa3baddd9899ac69c36ebd41f5..9cad8648b6498b1b2db67028377d1b0468fc6077 100644 --- a/src/gboml/gboml.lark +++ b/src/gboml/gboml.lark @@ -26,13 +26,18 @@ program_block: _program* global_block: _block_repeat_or_pass{"#GLOBAL",definition}? _program: node | hyperedge +// TAGS +tags: TAG* +TAG: /@[a-zA-Z\-_]+/ + // NODES ?node: node_definition | node_import node_definition: _block_shortcut{_node_header, _node_content} -_node_header: "#NODE" var_or_param [loop] +_node_header: "#NODE" var_or_param [loop] tags _node_content: parameters_block program_block variables_block constraints_block objectives_block -parameters_block: (_block_repeat_or_pass{"#PARAMETERS",definition})? +parameters_block: (_block_repeat_or_pass{_opt_param_header,definition})? +_opt_param_header: "#PARAMETERS"? variables_block: _block_repeat_or_pass{"#VARIABLES",variable_definition} constraints_block: (_block_repeat_or_pass{"#CONSTRAINTS",constraint})? objectives_block: (_block_repeat_or_pass{"#OBJECTIVES",objective})? @@ -46,27 +51,27 @@ variable_scope_change: ID SCOPE ";" ?hyperedge: hyperedge_definition | hyperedge_import hyperedge_definition: _block_shortcut{_hyperedge_header, _hyperedge_content} -_hyperedge_header: "#HYPEREDGE" var_or_param [loop] +_hyperedge_header: "#HYPEREDGE" var_or_param [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] ":" var_or_param [_LARROW var_or_param] ";" +variable_definition: SCOPE [VTYPE] ":" var_or_param [_LARROW var_or_param] tags ";" SCOPE: "internal" | "external" VTYPE: "binary" | "continuous" | "integer" _LARROW.1: "<-" // CONSTRAINTS ?constraint: constraint_sos | constraint_std -constraint_std: [ID ":"] expression CTR_OPERATOR expression [loop] ";" +constraint_std: [ID ":"] expression CTR_OPERATOR expression [loop] tags ";" CTR_OPERATOR: "<=" | ">=" | "==" -constraint_sos: [ID ":"] SOS_TYPE array [loop] ";" +constraint_sos: [ID ":"] SOS_TYPE array [loop] tags ";" SOS_TYPE.1: "SOS1" | "SOS2" // OBJECTIVES -objective: OBJ_TYPE [ID] ":" expression [loop] ";" +objective: OBJ_TYPE [ID] ":" expression [loop] tags ";" OBJ_TYPE: "min" | "max" // LOOPS @@ -86,7 +91,7 @@ bool_expression_comparison: expression (COMPARISON_OPERATOR | CTR_OPERATOR) expr COMPARISON_OPERATOR: "<" | ">" | "!=" // DEFINITIONS -definition: ID "=" rvalue ";" +definition: ID "=" rvalue tags ";" // ARRAYS array_or_dict: "{" separated_maybe_empty_list{_array_or_dict_entry, ","} "}" @@ -115,7 +120,7 @@ function: ID "(" separated_maybe_empty_list{generated_rvalue, ","} ")" // VAR OR PARAM LINK var_or_param: separated_list{var_or_param_leaf, "."} -var_or_param_leaf: ID plist{index} +var_or_param_leaf: (ID | TAG) plist{index} ?index: "[" rvalue "]" //rvalue: anything that can be assigned to a parameter diff --git a/src/gboml/parsing.py b/src/gboml/parsing.py index 376053be2ca9a355f5cdfdd9160fcf2e222c44cd..1612dcce28e2397f3b2ad3cdd470e886564e7283 100644 --- a/src/gboml/parsing.py +++ b/src/gboml/parsing.py @@ -53,7 +53,8 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: as_list = { "objectives_block", "constraints_block", "variables_block", "parameters_block", "global_block", "plist", "node_redefs", - "hyperedge_redefs", "separated_list", "separated_maybe_empty_list" + "hyperedge_redefs", "separated_list", "separated_maybe_empty_list", + "tags" } # @@ -105,6 +106,7 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: def INT(self, token): return int(token.value) def FLOAT(self, token): return float(token.value) def ID(self, token): return token.value + def TAG(self, token): return token.value def SCOPE(self, token): return VarScope(token.value) def SOS_TYPE(self, token): return SOSType(token.value) def CTR_OPERATOR(self, token): return Operator(token.value) @@ -118,7 +120,7 @@ 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, loop: Optional[Loop], + def hyperedge_definition(self, meta: Meta, name: VarOrParam, loop: Optional[Loop], tags: list[str], param_block: list[Definition] = None, constraint_block: list[Constraint] = None): if constraint_block is None: constraint_block = [] @@ -128,11 +130,11 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: 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, param_block, constraint_block, meta=meta) + return HyperEdgeDefinition(name.path[0].name, param_block, constraint_block, tags, meta=meta) else: - return HyperEdgeGenerator(name, loop, param_block, constraint_block, meta=meta) + return HyperEdgeGenerator(name, loop, param_block, constraint_block, tags, meta=meta) - def node_definition(self, meta: Meta, name: VarOrParam, loop: Optional[Loop], + def node_definition(self, meta: Meta, name: VarOrParam, loop: Optional[Loop], tags: list[str], param_block: list[Definition] = None, subprogram_block: NodesAndHyperEdges = None, variable_block: list[VariableDefinition] = None, constraint_block: list[Constraint] = None, objectives_block: list[Objective] = None): @@ -154,13 +156,13 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph: param_block, subprogram_block.nodes, subprogram_block.hyperedges, variable_block, constraint_block, - objectives_block, meta=meta) + objectives_block, tags, meta=meta) else: return NodeGenerator(name, loop, param_block, subprogram_block.nodes, subprogram_block.hyperedges, variable_block, constraint_block, - objectives_block, meta=meta) + objectives_block, tags, meta=meta) def node_import(self, meta: Meta, name: str, imported_name: VarOrParam, imported_from: str, redef: list[ScopeChange | Definition]): return NodeImport(name, imported_name, imported_from, @@ -170,8 +172,9 @@ 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], name: VarOrParam, import_from: Optional[VarOrParam]): - return VariableDefinition(scope, type or VarType.continuous, name, import_from, meta=meta) + def variable_definition(self, meta: Meta, scope: VarScope, type: Optional[VarType], name: VarOrParam, + import_from: Optional[VarOrParam], tags: list[str]): + return VariableDefinition(scope, type or VarType.continuous, name, import_from, tags, meta=meta) def multi_loop(self, meta: Meta, *loops: Tuple[Loop]): return MultiLoop(list(loops), meta=meta) diff --git a/tests/instances/ok/complex_parsing.txt b/tests/instances/ok/complex_parsing.txt index 2259377171f49e43423bc1d57edbee618f22e67c..a617270feb59b665283e8ae5b382cad8cdbe7bd7 100644 --- a/tests/instances/ok/complex_parsing.txt +++ b/tests/instances/ok/complex_parsing.txt @@ -13,7 +13,9 @@ a = 2; b external; c internal; -#NODE nodeL[i] for i in [0:10] +#NODE nodeL[i] for i in [0:10] @hello + x = 3; + x = @test @test; #VARIABLES pass; #HYPEREDGE E[i] for i in [0:10]