diff --git a/src/gboml/ast/__init__.py b/src/gboml/ast/__init__.py
index be4da11911fda64d808d7a09d793473f7633fa02..fa27dbea0206990074b5f599883fdbea3f82f96f 100644
--- a/src/gboml/ast/__init__.py
+++ b/src/gboml/ast/__init__.py
@@ -8,7 +8,7 @@ __all__ = [
     "ExpressionOp", "GBOMLGraph", "ImplicitLoop", "RValue", "RValueWithGen", "GeneratedRValue",
     "Range", "MultiLoop", "DictEntry", "Dictionary", "NodeGenerator", "HyperEdgeGenerator",
     "DefinitionType", "FunctionDefinition", "ConstantDefinition", "ExpressionDefinition",
-    "CtrActivation", "ObjActivation", "ActivationType", "Activation"
+    "CtrActivation", "ObjActivation", "ActivationType", "Activation", "Extends"
 ]
 
 from gboml.ast.arrays import *
diff --git a/src/gboml/ast/hyperedges.py b/src/gboml/ast/hyperedges.py
index f6fdeb73e2f0a0d930890590925e8ee552edc3f2..bbeeedddf26df94a7b4bc3fa5e236e462e86bdf9 100644
--- a/src/gboml/ast/hyperedges.py
+++ b/src/gboml/ast/hyperedges.py
@@ -1,6 +1,8 @@
 from dataclasses import dataclass, field
+from typing import Optional
 
-from gboml.ast import Loop
+from gboml.ast.importable import Extends
+from gboml.ast.loops import Loop
 from gboml.ast.base import GBOMLObject
 from gboml.ast.constraints import Constraint, CtrActivation
 from gboml.ast.path import VarOrParam
@@ -15,6 +17,7 @@ class HyperEdge(GBOMLObject):
 @dataclass
 class HyperEdgeDefinition(HyperEdge):
     name: str
+    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)
@@ -25,6 +28,7 @@ class HyperEdgeDefinition(HyperEdge):
 class HyperEdgeGenerator(HyperEdge):
     name: VarOrParam
     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)
diff --git a/src/gboml/ast/importable.py b/src/gboml/ast/importable.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a3c16421d3b60779dc8235c541a3eb5ff45d314
--- /dev/null
+++ b/src/gboml/ast/importable.py
@@ -0,0 +1,11 @@
+from dataclasses import dataclass
+from typing import Optional
+
+from gboml.ast.base import GBOMLObject
+from gboml.ast.variables import VarOrParam
+
+
+@dataclass
+class Extends(GBOMLObject):
+    name: VarOrParam
+    filename: Optional[str]
diff --git a/src/gboml/ast/nodes.py b/src/gboml/ast/nodes.py
index 25dbef1db3652c7532e3317a8af6a66db77e8696..1ed435a8b1cdcfa8995af8f9f74b5c57eb9ab96a 100644
--- a/src/gboml/ast/nodes.py
+++ b/src/gboml/ast/nodes.py
@@ -1,8 +1,11 @@
 from dataclasses import dataclass, field
+from typing import Optional
 
-from gboml.ast import Loop, Activation
+from gboml.ast.loops import Loop
+from gboml.ast.activation import Activation
 from gboml.ast.base import GBOMLObject
 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
@@ -17,6 +20,7 @@ class Node(GBOMLObject):
 @dataclass
 class NodeDefinition(Node):
     name: str
+    import_from: Optional[Extends] = None
     parameters: list[Definition] = field(default_factory=list)
     nodes: list[Node] = field(default_factory=list)
     hyperedges: list[HyperEdge] = field(default_factory=list)
@@ -31,6 +35,7 @@ class NodeDefinition(Node):
 class NodeGenerator(Node):
     name: VarOrParam
     loop: Loop
+    import_from: Optional[Extends] = None
     parameters: list[Definition] = field(default_factory=list)
     nodes: list[Node] = field(default_factory=list)
     hyperedges: list[HyperEdge] = field(default_factory=list)
diff --git a/src/gboml/gboml.lark b/src/gboml/gboml.lark
index 361c1f10609b23dce6ac0f4d2aa32998321587cc..49597aaad8d3164f99877cb2afd3b00dcfdb1c65 100644
--- a/src/gboml/gboml.lark
+++ b/src/gboml/gboml.lark
@@ -31,10 +31,13 @@ _program: node | hyperedge
 tags: TAG*
 TAG: /@[a-zA-Z\-_]+/
 
+// Extends
+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 [loop] tags
+_node_header: "#NODE" var_or_param [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})?
@@ -54,7 +57,7 @@ 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] tags
+_hyperedge_header: "#HYPEREDGE" var_or_param [extends] [loop] tags
 _hyperedge_content: parameters_block constraints_block
 
 hyperedge_import: "#HYPEREDGE" ID "=" "import" var_or_param "from" STRING hyperedge_redefs
diff --git a/src/gboml/parsing.py b/src/gboml/parsing.py
index c37a3413adc8582f502671c42a5a9d764e77ef31..2cff83189cf6da690165b3e4b31dee81c0c6f6ea 100644
--- a/src/gboml/parsing.py
+++ b/src/gboml/parsing.py
@@ -93,7 +93,8 @@ def _lark_to_gboml(tree: Tree, filename: Optional[str] = None) -> GBOMLGraph:
             "ctr_activate": lambda *x, meta: CtrActivation(ActivationType.activate, *x, meta=meta),
             "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)
+            "obj_deactivate": lambda *x, meta: ObjActivation(ActivationType.deactivate, *x, meta=meta),
+            "extends": Extends
         }
 
         def __default__(self, data, children, _):
@@ -125,26 +126,25 @@ 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], tags: list[str],
-                                 param_block: list[Definition] = None,
+        def hyperedge_definition(self, meta: Meta, name: VarOrParam, extends: Optional[Extends],
+                                 loop: Optional[Loop], tags: list[str], param_block: list[Definition] = None,
                                  constraint_block: list[Constraint | CtrActivation] = None):
-            if constraint_block is None:
-                constraint_block = []
+            constraint_block = constraint_block or []
             activations = [x for x in constraint_block if isinstance(x, CtrActivation)]
             constraint_block = [x for x in constraint_block if isinstance(x, Constraint)]
-            if param_block is None:
-                param_block = []
+            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, param_block, constraint_block,
+                return HyperEdgeDefinition(name.path[0].name, extends, param_block, constraint_block,
                                            activations, tags, meta=meta)
             else:
-                return HyperEdgeGenerator(name, loop, param_block, constraint_block,
+                return HyperEdgeGenerator(name, loop, extends, param_block, constraint_block,
                                           activations, tags, meta=meta)
 
-        def node_definition(self, meta: Meta, name: VarOrParam, loop: Optional[Loop], tags: list[str],
+        def node_definition(self, meta: Meta, name: VarOrParam, extends: Optional[Extends],
+                            loop: Optional[Loop], tags: list[str],
                             param_block: list[Definition] = None, subprogram_block: NodesAndHyperEdges = None,
                             variable_block: list[VariableDefinition] = None,
                             constraint_block: list[Constraint | CtrActivation] = None,
@@ -162,14 +162,12 @@ 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 NodeDefinition(name.path[0].name,
-                                      param_block,
+                return NodeDefinition(name.path[0].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,
-                                     param_block,
+                return NodeGenerator(name, loop, extends, param_block,
                                      subprogram_block.nodes, subprogram_block.hyperedges,
                                      variable_block, constraint_block,
                                      objectives_block, activations, tags, meta=meta)