diff --git a/src/Lib/Script/Ast/IrElement.cc b/src/Lib/Script/Ast/IrElement.cc
index 3793c8c1dcca4af7c21468dc859feb5c17c2bf2e..94dfb5f3926f6721b80335acd2597b8690cc0fd6 100644
--- a/src/Lib/Script/Ast/IrElement.cc
+++ b/src/Lib/Script/Ast/IrElement.cc
@@ -4,10 +4,7 @@
 
 namespace Vivy::Script
 {
-IrElement::IrElement(IrElement *p) noexcept
-    : parentElement(p != nullptr ? p : this)
-{
-}
+IrElement::IrElement(IrElement *p) noexcept { setParent(p); }
 
 IrElement::~IrElement() noexcept
 {
diff --git a/src/Lib/Script/Ast/IrExpression.cc b/src/Lib/Script/Ast/IrExpression.cc
index b47559e6d056da8e62e9ae4cfbd4bdca8d21f783..1d519fb1c9974aeefc4c6eb99e2d2703324ee904 100644
--- a/src/Lib/Script/Ast/IrExpression.cc
+++ b/src/Lib/Script/Ast/IrExpression.cc
@@ -7,6 +7,12 @@
 
 namespace Vivy::Script
 {
+IrExpression::IrExpression(IrElement *p, Type type) noexcept
+    : IrElement(p)
+    , selfType(type)
+{
+}
+
 std::string
 IrExpression::toString() const noexcept
 {
@@ -58,6 +64,12 @@ IrEVariableRef::parse(std::vector<Token> *)
 
 namespace Vivy::Script
 {
+IrEConstExpr::IrEConstExpr(const Token &singleElement)
+    : IrExpression(nullptr, Type::ConstExpr)
+{
+    throw std::logic_error("IrEConstExpr " + singleElement.toString());
+}
+
 std::string
 IrEConstExpr::toString() const noexcept
 {
diff --git a/src/Lib/Script/Ast/IrExpression.hh b/src/Lib/Script/Ast/IrExpression.hh
index 5b49cb5801089412fd0bc9e5788e4353cacfad5e..9261515bdd9e2ce296235e14cb9722538fc35618 100644
--- a/src/Lib/Script/Ast/IrExpression.hh
+++ b/src/Lib/Script/Ast/IrExpression.hh
@@ -14,6 +14,9 @@ private:
     Type selfType;
     IrType *selfInnerType = nullptr;
 
+protected:
+    IrExpression(IrElement *p, Type) noexcept;
+
 public:
     std::string toString() const noexcept override;
     void parse(std::vector<Token> *) override;
@@ -25,6 +28,8 @@ public:
 class IrEConstExpr : public IrExpression {
     VIVY_IR_ELEMENT(IrEConstExpr)
 
+    IrEConstExpr(const Token &);
+
 public:
     std::string toString() const noexcept override;
     void parse(std::vector<Token> *) override;
diff --git a/src/Lib/Script/Ast/IrOption.cc b/src/Lib/Script/Ast/IrOption.cc
index 78aec6976e6893923b72cea76ccca04a62d2a82c..5d4dfcaa96fea27c2748af81e299352036c691e9 100644
--- a/src/Lib/Script/Ast/IrOption.cc
+++ b/src/Lib/Script/Ast/IrOption.cc
@@ -1,4 +1,6 @@
 #include "IrOption.hh"
+#include "IrExpression.hh"
+#include "IrType.hh"
 #include "Lib/Script/FrontEnd/Lexer.hh"
 
 namespace Vivy::Script
@@ -89,6 +91,8 @@ IrOption::match(const std::vector<IrOption *> &others) const noexcept
 void
 IrOption::parse(std::vector<Token> *tokens)
 {
+    IrModule *sourceModule = parent();
+
     const Token firstDeclToken = getInnerTokenOpt(tokenListPop(tokens));
     firstDeclToken.assertTypeSimple(TOKEN_OPTION);
 
@@ -112,13 +116,19 @@ IrOption::parse(std::vector<Token> *tokens)
         getInnerTokenOpt(tokenListPop(tokens)).assertTypeSimple(TOKEN_ASSIGN);
         const Token defaultValueToken = getInnerTokenOpt(tokenListPop(tokens));
 
-        std::cerr << "Found option: " << shapeName.toStdString() << "."
-                  << fieldNameToken.asQName().toStdString() << " : "
-                  << typeNameToken.asSimple().toStdString() << " = " << defaultValueToken.toString()
-                  << ";\n";
+        IrEConstExpr *defaultValue = IrElement::create<IrEConstExpr>(this, defaultValueToken);
+        IrType *optionType         = IrElement::create<IrTPrimitive>(this, typeNameToken);
+
+        const ShapeElement::Content content = {
+            .fieldName    = fieldNameToken.asQName(),
+            .type         = optionType,
+            .defaultValue = defaultValue,
+        };
+
+        addShape(sourceModule, content);
 
-        const Token continuationToken = getInnerTokenOpt(tokenListPop(tokens));
-        if (continuationToken.isSimple(TOKEN_COMMA))
+        if (const Token continuationToken = getInnerTokenOpt(tokenListPop(tokens));
+            continuationToken.isSimple(TOKEN_COMMA))
             continue;
         else if (continuationToken.isSimple(TOKEN_CURLY_BRACKET_RIGHT))
             break;
diff --git a/src/Lib/Script/Ast/IrType.cc b/src/Lib/Script/Ast/IrType.cc
index 71138e375fa86f5dde1930a4907ba98632bb5a36..009a1f0ee1b81c10db1f10d44574261b23ed6f83 100644
--- a/src/Lib/Script/Ast/IrType.cc
+++ b/src/Lib/Script/Ast/IrType.cc
@@ -78,6 +78,11 @@ IrTOption::parse(std::vector<Token> *)
 
 namespace Vivy::Script
 {
+IrTPrimitive::IrTPrimitive(const Token &name)
+{
+    throw std::logic_error("IrTPrimitive " + name.toString());
+}
+
 IrType::Type
 IrTPrimitive::type() const noexcept
 {
diff --git a/src/Lib/Script/Ast/IrType.hh b/src/Lib/Script/Ast/IrType.hh
index ef0da35db5c141b78e17ffc8b5b6e88f6011585e..d74b60250dcc6b8ff7e3b3f5740da5a9cfb69de4 100644
--- a/src/Lib/Script/Ast/IrType.hh
+++ b/src/Lib/Script/Ast/IrType.hh
@@ -53,6 +53,8 @@ class IrTPrimitive : public IrType {
     PrimitiveType selfInnerType;
     unsigned int selfArraySize;
 
+    IrTPrimitive(const Token &);
+
 public:
     Type type() const noexcept override;
     PrimitiveType innerType() const noexcept;