diff --git a/src/Rust/.gitignore b/src/Rust/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba
--- /dev/null
+++ b/src/Rust/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/src/Rust/Cargo.lock b/src/Rust/Cargo.lock
index cd52f7b46b829d6487fc2b226cbe1a8c12a6c663..b0f1d2f5afbda940ff3c2410e16e37e1e932624e 100644
--- a/src/Rust/Cargo.lock
+++ b/src/Rust/Cargo.lock
@@ -52,9 +52,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clap"
-version = "4.1.14"
+version = "4.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "906f7fe1da4185b7a282b2bc90172a496f9def1aca4545fe7526810741591e14"
+checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -63,9 +63,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.1.14"
+version = "4.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "351f9ad9688141ed83dfd8f5fb998a06225ef444b48ff4dc43de6d409b7fd10b"
+checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
 dependencies = [
  "bitflags",
  "clap_lex",
@@ -75,30 +75,30 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.1.6"
+version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40d3120a421cd111c43f1a6c7d0dd83bb6aaa0659c164468a1654014632a5ec6"
+checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.1.14"
+version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81d7dc0031c3a59a04fc2ba395c8e2dd463cba1859275f065d225f6122221b45"
+checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
 dependencies = [
  "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.11",
+ "syn 2.0.13",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f0807fb6f644c83f3e4ec014fec9858c1c8b26a7db8eb5f0bde5817df9c1df7"
+checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
 
 [[package]]
 name = "clap_mangen"
@@ -129,13 +129,13 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
 [[package]]
 name = "errno"
-version = "0.2.8"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
+checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
 dependencies = [
  "errno-dragonfly",
  "libc",
- "winapi",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -194,7 +194,7 @@ checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
 dependencies = [
  "hermit-abi",
  "libc",
- "windows-sys",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -220,9 +220,9 @@ checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.1.4"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
 
 [[package]]
 name = "log"
@@ -316,6 +316,12 @@ version = "1.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
+[[package]]
+name = "paste"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.26"
@@ -348,9 +354,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.54"
+version = "1.0.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
+checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
 dependencies = [
  "unicode-ident",
 ]
@@ -395,16 +401,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
 name = "rustix"
-version = "0.36.11"
+version = "0.37.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e"
+checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
 dependencies = [
  "bitflags",
  "errno",
  "io-lifetimes",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.45.0",
 ]
 
 [[package]]
@@ -430,9 +436,9 @@ dependencies = [
 
 [[package]]
 name = "scc"
-version = "1.4.1"
+version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a83e3a5d0ddb38f854c69b8387fda1ffb87bb845b88928c66653bed16fccd11"
+checksum = "9bb666a705482e987d4bbd8bb7b0a07438ecfde629044176f0a571605c03e22a"
 
 [[package]]
 name = "scopeguard"
@@ -486,9 +492,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.11"
+version = "2.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40"
+checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -497,12 +503,12 @@ dependencies = [
 
 [[package]]
 name = "terminal_size"
-version = "0.2.5"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c9afddd2cec1c0909f06b00ef33f94ab2cc0578c4a610aa208ddfec8aa2b43a"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
 dependencies = [
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -522,7 +528,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.11",
+ "syn 2.0.13",
 ]
 
 [[package]]
@@ -599,6 +605,7 @@ dependencies = [
  "log",
  "scc",
  "thiserror",
+ "vvs_procmacro",
 ]
 
 [[package]]
@@ -623,11 +630,21 @@ version = "0.4.0"
 dependencies = [
  "log",
  "mlua",
+ "paste",
  "thiserror",
  "toml",
  "vvs_ass",
 ]
 
+[[package]]
+name = "vvs_procmacro"
+version = "0.4.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.13",
+]
+
 [[package]]
 name = "vvs_repl"
 version = "0.4.0"
@@ -666,7 +683,16 @@ version = "0.45.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.0",
 ]
 
 [[package]]
@@ -675,13 +701,28 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.0",
+ "windows_aarch64_msvc 0.48.0",
+ "windows_i686_gnu 0.48.0",
+ "windows_i686_msvc 0.48.0",
+ "windows_x86_64_gnu 0.48.0",
+ "windows_x86_64_gnullvm 0.48.0",
+ "windows_x86_64_msvc 0.48.0",
 ]
 
 [[package]]
@@ -690,42 +731,84 @@ version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.42.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
 [[package]]
 name = "winnow"
 version = "0.4.1"
diff --git a/src/Rust/Cargo.toml b/src/Rust/Cargo.toml
index c2fc442acea032447fc27c65d77caa55e6913bb5..3bd837499ea359181022430d93bb5d62f29c133e 100644
--- a/src/Rust/Cargo.toml
+++ b/src/Rust/Cargo.toml
@@ -1,6 +1,6 @@
 [workspace]
 resolver = "2"
-members = ["vvs_cli", "vvs_lua", "vvs_ass", "vvs_repl"]
+members = ["vvs_cli", "vvs_lua", "vvs_ass", "vvs_repl", "vvs_procmacro"]
 
 [workspace.package]
 version = "0.4.0"
diff --git a/src/Rust/vvs_ass/Cargo.toml b/src/Rust/vvs_ass/Cargo.toml
index f0f939ba0e726c4ffe942e1abbd25bd04d372e4f..d0b222ee62931a1d5bd0c6560d6fe38d9588ba73 100644
--- a/src/Rust/vvs_ass/Cargo.toml
+++ b/src/Rust/vvs_ass/Cargo.toml
@@ -7,6 +7,8 @@ license.workspace = true
 description.workspace = true
 
 [dependencies]
+vvs_procmacro = { path = "../vvs_procmacro" }
+
 lazy_static.workspace = true
 thiserror.workspace = true
 anyhow.workspace = true
diff --git a/src/Rust/vvs_ass/src/elements/line.rs b/src/Rust/vvs_ass/src/elements/line.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3ed50825a9de0e190ccdecb86a25125ad950bf2d
--- /dev/null
+++ b/src/Rust/vvs_ass/src/elements/line.rs
@@ -0,0 +1,10 @@
+use crate::{elements::syllabes::ASSSyllabesPtr, ASSAuxTablePtr, ASSPositionPtr};
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSLine {
+    pub position: ASSPositionPtr,
+    pub content: ASSSyllabesPtr,
+    pub aux: ASSAuxTablePtr,
+}
+
+pub type ASSLinePtr = crate::Ptr<ASSLine>;
diff --git a/src/Rust/vvs_ass/src/elements/lines.rs b/src/Rust/vvs_ass/src/elements/lines.rs
new file mode 100644
index 0000000000000000000000000000000000000000..87eabd77f85dd8255d0aac9cb33e5ccc8e5e5c76
--- /dev/null
+++ b/src/Rust/vvs_ass/src/elements/lines.rs
@@ -0,0 +1,10 @@
+use crate::{elements::line::ASSLinePtr, ASSAuxTablePtr, ASSPositionPtr};
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSLines {
+    pub position: ASSPositionPtr,
+    pub content: Vec<ASSLinePtr>,
+    pub aux: ASSAuxTablePtr,
+}
+
+pub type ASSLinesPtr = crate::Ptr<ASSLines>;
diff --git a/src/Rust/vvs_ass/src/elements/mod.rs b/src/Rust/vvs_ass/src/elements/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b0808c07612abd432d5806be6c128bcfa6d61b21
--- /dev/null
+++ b/src/Rust/vvs_ass/src/elements/mod.rs
@@ -0,0 +1,9 @@
+mod line;
+mod lines;
+mod syllabe;
+mod syllabes;
+
+pub use line::*;
+pub use lines::*;
+pub use syllabe::*;
+pub use syllabes::*;
diff --git a/src/Rust/vvs_ass/src/elements/syllabe.rs b/src/Rust/vvs_ass/src/elements/syllabe.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1d412182b0be418bdb1ada8a2f4c365e777da42e
--- /dev/null
+++ b/src/Rust/vvs_ass/src/elements/syllabe.rs
@@ -0,0 +1,10 @@
+use crate::{ASSAuxTablePtr, ASSPositionPtr};
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSSyllabe {
+    pub position: ASSPositionPtr,
+    pub content: String,
+    pub aux: ASSAuxTablePtr,
+}
+
+pub type ASSSyllabePtr = crate::Ptr<ASSSyllabe>;
diff --git a/src/Rust/vvs_ass/src/elements/syllabes.rs b/src/Rust/vvs_ass/src/elements/syllabes.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f57f8a759a3ba128751e20e2ab6387c5f2c7050e
--- /dev/null
+++ b/src/Rust/vvs_ass/src/elements/syllabes.rs
@@ -0,0 +1,10 @@
+use crate::{elements::syllabe::ASSSyllabePtr, ASSAuxTablePtr, ASSPositionPtr};
+
+#[derive(Debug, Clone, PartialEq)]
+pub struct ASSSyllabes {
+    pub position: ASSPositionPtr,
+    pub content: Vec<ASSSyllabePtr>,
+    pub aux: ASSAuxTablePtr,
+}
+
+pub type ASSSyllabesPtr = crate::Ptr<ASSSyllabes>;
diff --git a/src/Rust/vvs_ass/src/lib.rs b/src/Rust/vvs_ass/src/lib.rs
index c554fadb9a1724de666c5314c19154e1613e23ac..783ab954e5a80e6e38f2809d5bf962ac62b11591 100644
--- a/src/Rust/vvs_ass/src/lib.rs
+++ b/src/Rust/vvs_ass/src/lib.rs
@@ -1,5 +1,20 @@
 //! ASS objects for Vivy.
 
-pub mod position;
-pub mod types;
-pub mod values;
+mod elements;
+mod position;
+mod types;
+mod values;
+
+pub use elements::*;
+pub use position::*;
+pub use types::*;
+pub use values::*;
+
+pub type Ptr<T> = std::rc::Rc<std::cell::RefCell<T>>;
+
+#[macro_export]
+macro_rules! ptr {
+    ($expr: expr) => {
+        std::rc::Rc::new(std::cell::RefCell::new($expr))
+    };
+}
diff --git a/src/Rust/vvs_ass/src/position.rs b/src/Rust/vvs_ass/src/position.rs
index 668e0f976d679fa425fe616bcbd930fcf37aa908..7012e97a368c3da5cdf418508d5d8d011700f2be 100644
--- a/src/Rust/vvs_ass/src/position.rs
+++ b/src/Rust/vvs_ass/src/position.rs
@@ -1,5 +1,40 @@
+/// The position of an object from the top left corner. The real position depends on the align of
+/// the object.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct Position {
-    x: i64,
-    y: i64,
+pub struct ASSPosition {
+    pub x: i64,
+    pub y: i64,
 }
+
+/// The alignement of the object.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ASSAlign {
+    /// Bottom left
+    BL = 1,
+
+    /// Bottom center
+    BC = 2,
+
+    /// Bottom right
+    BR = 3,
+
+    /// Center left
+    CL = 4,
+
+    /// Center center
+    CC = 5,
+
+    /// Center right
+    CR = 6,
+
+    /// Top left
+    TL = 7,
+
+    /// Top center
+    TC = 8,
+
+    /// Top right
+    TR = 9,
+}
+
+pub type ASSPositionPtr = crate::Ptr<ASSPosition>;
diff --git a/src/Rust/vvs_ass/src/types.rs b/src/Rust/vvs_ass/src/types.rs
index a4ea3822185d4901d1f41969ef50cb3ca4cef2c8..6d6928b2cf936f6be710cad1526b7e5569208ee9 100644
--- a/src/Rust/vvs_ass/src/types.rs
+++ b/src/Rust/vvs_ass/src/types.rs
@@ -1,9 +1,10 @@
 use std::str::FromStr;
 use thiserror::Error;
+use vvs_procmacro::{EnumVariantCount, EnumVariantIter};
 
 /// Represents the types of the ASS types that can be manipulated. By combining them we can create
 /// a tree of the ASS elements. We have:
-/// ```no_run,skip
+/// ```ignore
 /// - Lines
 ///   - pos: AssPosition
 ///   - aux: HashMap<String, AssAuxValue>
@@ -30,22 +31,54 @@ use thiserror::Error;
 ///
 /// We also derive the Ord/PartialOrd crates. The types are ordered from the Lines to the Syllabe.
 /// Thus if a type is greater than another one, the former must contains the latter.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, EnumVariantCount, EnumVariantIter)]
 pub enum ASSType {
-    Lines,
-    Line,
-    Syllabes,
-    Syllabe,
+    Lines = 0,
+    Line = 1,
+    Syllabes = 2,
+    Syllabe = 3,
+}
+
+impl ASSType {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            ASSType::Lines => "lines",
+            ASSType::Line => "line",
+            ASSType::Syllabes => "syllabes",
+            ASSType::Syllabe => "syllabe",
+        }
+    }
+
+    pub fn as_padded_str(&self) -> &'static str {
+        match self {
+            ASSType::Lines => "lines   ",
+            ASSType::Line => "line    ",
+            ASSType::Syllabes => "syllabes",
+            ASSType::Syllabe => "syllabe ",
+        }
+    }
+}
+
+impl AsRef<str> for ASSType {
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl std::fmt::Display for ASSType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(self.as_str())
+    }
 }
 
 #[derive(Debug, Error)]
-pub enum AssTypeFromStrError {
+pub enum ASSTypeFromStrError {
     #[error("unknown ass structure type '{0}', expected: lines/line/syllabe/syllabes")]
     Unknown(String),
 }
 
 impl FromStr for ASSType {
-    type Err = AssTypeFromStrError;
+    type Err = ASSTypeFromStrError;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match s {
@@ -53,7 +86,7 @@ impl FromStr for ASSType {
             "lines" => Ok(Self::Lines),
             "syllabe" => Ok(Self::Syllabe),
             "syllabes" => Ok(Self::Syllabes),
-            _ => Err(AssTypeFromStrError::Unknown(s.to_string())),
+            _ => Err(ASSTypeFromStrError::Unknown(s.to_string())),
         }
     }
 }
diff --git a/src/Rust/vvs_ass/src/values.rs b/src/Rust/vvs_ass/src/values.rs
index 7b64de9e989915ca547af2c4beab1b0fc94464a0..7c656b477963aac846ffb1b7e00fcbfa0e4b444b 100644
--- a/src/Rust/vvs_ass/src/values.rs
+++ b/src/Rust/vvs_ass/src/values.rs
@@ -1,7 +1,113 @@
+use std::{cell::RefCell, collections::HashMap, convert::TryFrom, rc::Rc};
+
+/// The values that can be added to an ASS element.
 #[derive(Debug, Clone, PartialEq)]
-pub enum AssAuxValue {
+pub enum ASSAuxValue {
     Integer(i64),
     Floating(f64),
     Boolean(bool),
     String(String),
 }
+
+/// The auxiliary table of user values associated to ASS elements.
+#[derive(Debug, Default, Clone, PartialEq)]
+pub struct ASSAuxTable(HashMap<String, ASSAuxValue>);
+
+pub type ASSAuxTablePtr = Rc<RefCell<ASSAuxTable>>;
+
+impl ASSAuxValue {
+    pub fn type_str(&self) -> &'static str {
+        match self {
+            ASSAuxValue::Integer(_) => "integer",
+            ASSAuxValue::Floating(_) => "floating",
+            ASSAuxValue::Boolean(_) => "boolean",
+            ASSAuxValue::String(_) => "string",
+        }
+    }
+
+    pub fn coerce_like(self, like: &ASSAuxValue) -> Option<ASSAuxValue> {
+        use ASSAuxValue::*;
+        match (&self, like) {
+            (Floating(_), Floating(_))
+            | (Integer(_), Integer(_))
+            | (Boolean(_), Boolean(_))
+            | (String(_), String(_)) => Some(self),
+
+            (Integer(v), Floating(_)) => Some(Floating(i32::try_from(*v).ok()? as f64)),
+            (Integer(0), Boolean(_)) => Some(Boolean(false)),
+            (Integer(_), Boolean(_)) => Some(Boolean(true)),
+
+            (Boolean(v), String(_)) => Some(String(format!("{v}"))),
+            (Integer(v), String(_)) => Some(String(format!("{v}"))),
+            (Floating(v), String(_)) => Some(String(format!("{v}"))),
+
+            (Boolean(v), Integer(_)) => Some(Integer(*v as i64)),
+
+            (String(_), Integer(_)) => todo!(),
+            (String(_), Floating(_)) => todo!(),
+            (String(_), Boolean(_)) => todo!(),
+
+            (Floating(_), Integer(_)) | (Floating(_), Boolean(_)) | (Boolean(_), Floating(_)) => {
+                log::error!(target: "lua", "invalid convertion from type `{}` to `{}`", self.type_str(), like.type_str());
+                None
+            }
+        }
+    }
+}
+
+impl std::fmt::Display for ASSAuxValue {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            ASSAuxValue::Floating(val) => write!(f, "{val}"),
+            ASSAuxValue::Integer(val) => write!(f, "{val}"),
+            ASSAuxValue::Boolean(val) => write!(f, "{val}"),
+            ASSAuxValue::String(val) => f.write_str(&val),
+        }
+    }
+}
+
+impl ASSAuxTable {
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    pub fn into_ptr(self) -> ASSAuxTablePtr {
+        crate::ptr! { self }
+    }
+
+    pub fn set(&mut self, name: impl AsRef<str>, value: ASSAuxValue) {
+        let name = name.as_ref();
+        let new = value.type_str();
+        match self.0.get_mut(name) {
+            Some(old) => match value.coerce_like(old) {
+                Some(new) => *old = new,
+                None => log::error!(
+                    target: "lua",
+                    "can't set new value for `{name}`, old value was of type `{}` and new one is of type `{new}`",
+                    old.type_str()
+                ),
+            },
+            None => {
+                let _ = self.0.insert(name.to_string(), value);
+            }
+        }
+    }
+
+    pub fn get_copy(&self, name: impl AsRef<str>) -> Option<ASSAuxValue> {
+        self.0.get(name.as_ref()).cloned()
+    }
+
+    pub fn get(&self, name: impl AsRef<str>) -> Option<&ASSAuxValue> {
+        self.0.get(name.as_ref())
+    }
+
+    pub fn get_mut(&mut self, name: impl AsRef<str>) -> Option<&mut ASSAuxValue> {
+        self.0.get_mut(name.as_ref())
+    }
+}
+
+impl FromIterator<(String, ASSAuxValue)> for ASSAuxTable {
+    fn from_iter<T: IntoIterator<Item = (String, ASSAuxValue)>>(iter: T) -> Self {
+        Self(HashMap::from_iter(iter))
+    }
+}
diff --git a/src/Rust/vvs_lua/Cargo.toml b/src/Rust/vvs_lua/Cargo.toml
index 05d8712fcd0edc1a5374b386f45db959bfaecdc9..7b7368080ded4fe8d48e003490ecd36ff1d105f7 100644
--- a/src/Rust/vvs_lua/Cargo.toml
+++ b/src/Rust/vvs_lua/Cargo.toml
@@ -10,6 +10,7 @@ description.workspace = true
 vvs_ass = { path = "../vvs_ass" }
 
 thiserror.workspace = true
+paste.workspace = true
 toml.workspace = true
 log.workspace = true
 
diff --git a/src/Rust/vvs_lua/src/data/actions.rs b/src/Rust/vvs_lua/src/data/actions.rs
new file mode 100644
index 0000000000000000000000000000000000000000..67e46f1a997615345fd89a72bdaa1bbe8f824aa0
--- /dev/null
+++ b/src/Rust/vvs_lua/src/data/actions.rs
@@ -0,0 +1,77 @@
+use crate::{data::lua_wrapper::LuaAssAuxValue, traits::TypedValue, types::Type};
+use mlua::prelude::*;
+use vvs_ass::ASSAuxValue;
+
+/// Structure used to register auxiliary data in an ASS element by typing either:
+/// - `data "line:a"`
+/// - `data { "line:a", type = "number", default = 0, doc = "..." }`
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct RegisterDataValue {
+    pub ty: Type,
+    pub value: LuaAssAuxValue,
+    pub doc: Option<String>,
+}
+
+impl<'lua> FromLua<'lua> for RegisterDataValue {
+    fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
+        match lua_value {
+            LuaValue::Table(table) => {
+                let value: LuaAssAuxValue = match table.get::<_, LuaValue>("value").ok() {
+                    None | Some(LuaValue::Nil) => {
+                        return Err(LuaError::RuntimeError(format!(
+                            "you must specify a default value in a data declaration"
+                        )))
+                    }
+                    Some(value) => match value {
+                        LuaValue::Boolean(v) => LuaAssAuxValue::from(ASSAuxValue::Boolean(v)),
+                        LuaValue::Integer(v) => LuaAssAuxValue::from(ASSAuxValue::Integer(v)),
+                        LuaValue::Number(v) => LuaAssAuxValue::from(ASSAuxValue::Floating(v)),
+                        LuaValue::String(v) => LuaAssAuxValue::from(ASSAuxValue::String(
+                            v.to_string_lossy().to_string(),
+                        )),
+                        value => {
+                            return Err(LuaError::FromLuaConversionError {
+                                from: value.type_name(),
+                                to: "LuaAssAuxValue",
+                                message: None,
+                            })
+                        }
+                    },
+                };
+                let ty = match table.get::<_, LuaValue>("type").ok() {
+                    None | Some(LuaValue::Nil) => {
+                        return Err(LuaError::RuntimeError(format!(
+                            "you must specify a type in a data declaration"
+                        )))
+                    }
+                    Some(ty) => Type::from_lua(ty, lua)?,
+                };
+                let doc = match table.get::<_, LuaValue>("doc").ok() {
+                    None | Some(LuaValue::Nil) => None,
+                    Some(LuaValue::String(doc)) => Some(doc.to_string_lossy().to_string()),
+                    Some(value) => {
+                        return Err(LuaError::RuntimeError(format!(
+                            "value of type '{}' can't be a docstring, expected a 'string'",
+                            value.type_name()
+                        )))
+                    }
+                };
+                if value.ty().ne(&ty) {
+                    return Err(LuaError::RuntimeError(format!(
+                            "tried to register a value of type '{}' when declaring the option of type '{ty}'",
+                            value.ty()
+                        )));
+                }
+                Ok(Self { doc, ty, value })
+            }
+
+            _ => Err(LuaError::FromLuaConversionError {
+                from: lua_value.type_name(),
+                to: "RegisterDataValue",
+                message: Some(
+                    "expected a string or a table with the 'data' instruction".to_string(),
+                ),
+            }),
+        }
+    }
+}
diff --git a/src/Rust/vvs_lua/src/data/lua_wrapper.rs b/src/Rust/vvs_lua/src/data/lua_wrapper.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e0cb563e5ddf2b1c42351f12634d7f94ed7ec303
--- /dev/null
+++ b/src/Rust/vvs_lua/src/data/lua_wrapper.rs
@@ -0,0 +1,120 @@
+//! Wraps the ASS element types and implement some helper traits.
+
+use crate::traits::TypedValue;
+use mlua::prelude::*;
+use vvs_ass::{
+    ASSAuxTablePtr, ASSAuxValue, ASSLinePtr, ASSLinesPtr, ASSPositionPtr, ASSSyllabePtr,
+    ASSSyllabesPtr,
+};
+
+macro_rules! wrap_ass2lua {
+    ($($type: ident
+      $(, $fields: ident -> $add_fields: expr
+        , $methods: ident -> $add_methods: expr)?
+    );+ $(;)?) => {
+        $(wrap_ass2lua!(@@private $type $(; $fields -> $add_fields ; $methods -> $add_methods)?);)+
+    };
+
+    (@@decl $type: ident) => {
+        paste::paste! {
+            #[derive(Debug, Clone, PartialEq)]
+            #[repr(transparent)]
+            pub(crate) struct [< LuaAss $type >] ([< ASS $type >]);
+
+            impl [< LuaAss $type >] {
+                #[allow(dead_code)]
+                pub fn into_inner(self) -> [< ASS $type >] {
+                    self.0
+                }
+
+                #[allow(dead_code)]
+                pub fn as_inner(&self) -> &[< ASS $type >] {
+                    &self.0
+                }
+            }
+
+            impl From< [< ASS $type >] > for [< LuaAss $type >] {
+                fn from(value: [< ASS $type >]) -> Self {
+                    Self(value)
+                }
+            }
+        }
+    };
+
+    (@@private $type: ident) => {
+        wrap_ass2lua!(@@decl $type);
+        paste::paste! { impl LuaUserData for [< LuaAss $type >] { } }
+    };
+
+    (@@private $type: ident
+     ; $fields : ident -> $add_fields : expr
+     ; $methods: ident -> $add_methods: expr) => {
+        wrap_ass2lua!(@@decl $type);
+        paste::paste! {
+            impl LuaUserData for [< LuaAss $type >] {
+                fn add_fields <'lua, F: LuaUserDataFields <'lua, Self>>($fields:  &mut F) { $add_fields  }
+                fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>($methods: &mut M) { $add_methods }
+            }
+        }
+    };
+}
+
+macro_rules! add_method {
+    ($fields: expr => @aux) => {
+        add_method! { $fields => aux @ LuaAssAuxTablePtr }
+    };
+
+    ($fields: expr => $field: ident @ $wrapper: ident) => {
+        add_method! { get $fields => $field @ $wrapper }
+        add_method! { set $fields => $field @ $wrapper }
+    };
+
+    (get $fields: expr => $field: ident @ $wrapper: ident) => {
+        $fields.add_field_method_get(stringify!($field), |_, Self(this)| {
+            Ok($wrapper(this.borrow().$field.clone()))
+        });
+    };
+
+    (set $fields: expr => $field: ident @ $wrapper: ident) => {
+        $fields.add_field_method_set(stringify!($field), |_, Self(this), $wrapper(val)| {
+            this.borrow_mut().$field = val;
+            Ok(())
+        });
+    };
+}
+
+wrap_ass2lua! {
+    AuxValue;
+    AuxTablePtr;
+    PositionPtr;
+    LinePtr,
+        f -> {
+            add_method! { f => @aux }
+            add_method! { f => position @ LuaAssPositionPtr }
+        },
+        _m -> {};
+    LinesPtr,
+        f -> {
+            add_method! { f => @aux }
+            add_method! { f => position @ LuaAssPositionPtr }
+        },
+        _m -> {};
+    SyllabePtr,
+        f -> {
+            add_method! { f => @aux }
+            add_method! { f => position @ LuaAssPositionPtr }
+        },
+        _m -> {};
+    SyllabesPtr,
+        f -> {
+            add_method! { f => @aux }
+            add_method! { f => position @ LuaAssPositionPtr }
+        },
+        _m -> {};
+}
+
+impl TypedValue for LuaAssAuxValue {
+    fn ty(&self) -> crate::types::Type {
+        self.0.ty()
+    }
+}
diff --git a/src/Rust/vvs_lua/src/datas/mod.rs b/src/Rust/vvs_lua/src/data/mod.rs
similarity index 100%
rename from src/Rust/vvs_lua/src/datas/mod.rs
rename to src/Rust/vvs_lua/src/data/mod.rs
diff --git a/src/Rust/vvs_lua/src/data/register.rs b/src/Rust/vvs_lua/src/data/register.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c2095987b01b156980f4e45488db39dbf2f5ce8f
--- /dev/null
+++ b/src/Rust/vvs_lua/src/data/register.rs
@@ -0,0 +1,223 @@
+use crate::data::{actions::RegisterDataValue, lua_wrapper::LuaAssAuxTablePtr};
+use mlua::prelude::*;
+use std::{
+    cell::RefCell,
+    collections::{HashMap, HashSet},
+    rc::Rc,
+};
+use thiserror::Error;
+use vvs_ass::{ASSAuxTable, ASSType, ASSTYPE_LENGTH, ASSTYPE_VALUES};
+
+#[derive(Debug, Clone, PartialEq)]
+struct VivyDataValueHashMap {
+    ass_type: ASSType,
+    table: HashMap<String, RegisterDataValue>,
+    cached_table: LuaAssAuxTablePtr,
+}
+
+#[derive(Debug, Error)]
+pub(crate) enum VivyDataRegisterError {
+    #[error("{0}")]
+    Lua(LuaError),
+
+    #[error("vivy data already marked as to register")]
+    AlreadyMarkedAsRegister,
+
+    #[error("vivy data already registered")]
+    AlreadyRegistered,
+}
+
+/// The structure used to register auxiliary fields in ASS structures.
+#[derive(Debug, Clone, PartialEq)]
+pub(crate) struct VivyDataRegister {
+    registered: [VivyDataValueHashMap; ASSTYPE_LENGTH],
+    will_register: HashSet<String>,
+}
+
+pub(crate) type VivyDataRegisterPtr = Rc<RefCell<VivyDataRegister>>;
+
+fn decode_name(name: &str) -> LuaResult<(&str, ASSType)> {
+    let Some((ass_type, name)) = name.split_once(':') else {
+            return Err(LuaError::RuntimeError(format!(
+                "invalid data name, got '{name}', expected: 'ass_type:data_name'"
+            )));
+        };
+    let ass_type = ass_type.parse::<ASSType>().map_err(|err| {
+        LuaError::RuntimeError(format!(
+            "failed to convert type '{ass_type}' to an vvs_ass::ASSType\n{err}"
+        ))
+    })?;
+    Ok((name, ass_type))
+}
+
+impl VivyDataValueHashMap {
+    fn new(ty: ASSType) -> Self {
+        Self {
+            ass_type: ty,
+            table: Default::default(),
+            cached_table: LuaAssAuxTablePtr::from(ASSAuxTable::default().into_ptr()),
+        }
+    }
+}
+
+impl Default for VivyDataRegister {
+    fn default() -> Self {
+        Self {
+            will_register: Default::default(),
+            registered: [
+                VivyDataValueHashMap::new(ASSType::Lines),
+                VivyDataValueHashMap::new(ASSType::Line),
+                VivyDataValueHashMap::new(ASSType::Syllabes),
+                VivyDataValueHashMap::new(ASSType::Syllabe),
+            ],
+        }
+    }
+}
+
+impl VivyDataRegister {
+    pub fn new() -> Rc<RefCell<VivyDataRegister>> {
+        Default::default()
+    }
+
+    pub fn compute_cached_tables(&mut self) {
+        for ty in ASSTYPE_VALUES {
+            let register = self.get_register_mut(*ty);
+            let table = LuaAssAuxTablePtr::from(
+                ASSAuxTable::from_iter(
+                    register
+                        .table
+                        .iter()
+                        .map(|(name, data)| (name.clone(), data.value.clone().into_inner())),
+                )
+                .into_ptr(),
+            );
+            register.cached_table = table;
+        }
+    }
+
+    pub fn get_orphan_data_declarations(&self) -> impl Iterator<Item = (ASSType, &str)> {
+        self.will_register.iter().filter_map(|data| {
+            let (data, ass_type) = decode_name(data).expect("vivy internal error");
+            (!self
+                .registered
+                .get(ass_type as usize)
+                .expect("vivy internal error")
+                .table
+                .contains_key(data))
+            .then_some((ass_type, data))
+        })
+    }
+
+    /// Get the default aux table for an ass element type. Returns the cached table. After
+    /// registering all the wanted values, you may use the
+    /// [VivyDataRegister::compute_cached_tables] before calling this function.
+    pub fn get_table(&self, ass_type: ASSType) -> LuaAssAuxTablePtr {
+        self.get_register(ass_type).cached_table.clone()
+    }
+
+    /// Iter over the registered data names and types.
+    pub fn iter_registered(&self) -> impl Iterator<Item = (ASSType, &String)> {
+        self.registered.iter().flat_map(
+            |VivyDataValueHashMap {
+                 ass_type, table, ..
+             }| { std::iter::repeat(*ass_type).zip(table.keys()) },
+        )
+    }
+
+    /// Get the register data for a specific [ASSType]. If the ass type is not correct the function
+    /// will panic, but if you pass as correctly formed type it should be fine.
+    fn get_register_mut(&mut self, ass_type: ASSType) -> &mut VivyDataValueHashMap {
+        self.registered
+            .get_mut(ass_type as usize)
+            .expect("invalid ass element type passed")
+    }
+
+    /// Get the register data for a specific [ASSType]. If the ass type is not correct the function
+    /// will panic, but if you pass as correctly formed type it should be fine.
+    fn get_register(&self, ass_type: ASSType) -> &VivyDataValueHashMap {
+        self.registered
+            .get(ass_type as usize)
+            .expect("invalid ass element type passed")
+    }
+
+    /// Call to say that we will register a named option. If the option was already registered or
+    /// flagged as 'to register', raise an error. If the name is not valid, raise an error.
+    fn will_register_data(&mut self, name: String) -> Result<(), VivyDataRegisterError> {
+        let (data_name, ass_type) = decode_name(&name).map_err(VivyDataRegisterError::Lua)?;
+        if self.get_register(ass_type).table.get(data_name).is_some() {
+            Err(VivyDataRegisterError::AlreadyRegistered)
+        } else if !self.will_register.insert(name) {
+            Err(VivyDataRegisterError::AlreadyMarkedAsRegister)
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Register the data, returns an error if the data was already registered. If the data was not
+    /// flagged as 'to register', also raise an error.
+    fn register_data(
+        &mut self,
+        name: impl AsRef<str>,
+        value: RegisterDataValue,
+    ) -> Result<(), VivyDataRegisterError> {
+        let (name, ass_type) = decode_name(name.as_ref()).map_err(VivyDataRegisterError::Lua)?;
+        let table = &mut self.get_register_mut(ass_type).table;
+        match table.get(name) {
+            Some(_) => Err(VivyDataRegisterError::AlreadyRegistered),
+            None => {
+                table.insert(name.to_string(), value);
+                Ok(())
+            }
+        }
+    }
+}
+
+impl LuaUserData for VivyDataRegister {
+    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        methods.add_method_mut("will_register", |_, this, name: String| {
+            this.will_register_data(name.clone())
+                .map_err(|err| match err {
+                    VivyDataRegisterError::Lua(lua) => lua,
+                    err => LuaError::RuntimeError(format!(
+                        "failed to mark as 'to register' data '{name}': {err}"
+                    )),
+                })
+        });
+
+        methods.add_method_mut(
+            "register",
+            |_, this, (name, value): (String, RegisterDataValue)| {
+                this.register_data(&name, value).map_err(|err| match err {
+                    VivyDataRegisterError::Lua(lua) => lua,
+                    err => {
+                        LuaError::RuntimeError(format!("failed to register data '{name}': {err}"))
+                    }
+                })
+            },
+        )
+    }
+}
+
+#[test]
+fn test_data_register_creation() {
+    let reg = VivyDataRegister::new();
+    let reg: &VivyDataRegister = &*reg.borrow();
+    assert_eq!(*reg, Default::default());
+
+    macro_rules! test_item {
+        ($item: ident) => {
+            assert_eq!(
+                reg.registered
+                    .get(ASSType::$item as usize)
+                    .unwrap()
+                    .ass_type,
+                ASSType::$item
+            );
+        };
+    }
+
+    test_item! { Lines }
+    test_item! { Line }
+    test_item! { Syllabes }
+    test_item! { Syllabe }
+}
diff --git a/src/Rust/vvs_lua/src/datas/actions.rs b/src/Rust/vvs_lua/src/datas/actions.rs
deleted file mode 100644
index 7730007d40bdd05c7273a488cbda1ebbe49424ec..0000000000000000000000000000000000000000
--- a/src/Rust/vvs_lua/src/datas/actions.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use crate::{datas::lua_wrapper::LuaAssAuxValue, traits::TypedValue, types::Type};
-use mlua::prelude::*;
-
-/// Structure used to register auxiliary data in an ASS element by typing either:
-/// - `data "line:a"`
-/// - `data { "line:a", type = "number", default = 0, doc = "..." }`
-#[derive(Debug, Clone, PartialEq)]
-pub(crate) struct RegisterDataValue {
-    pub name: String,
-    pub ass_type: vvs_ass::types::ASSType,
-    pub doc: Option<String>,
-    pub ty: Option<Type>,
-    pub value: Option<LuaAssAuxValue>,
-}
-
-impl RegisterDataValue {
-    fn decode_name(name: &str) -> LuaResult<(&str, vvs_ass::types::ASSType)> {
-        let Some((ass_type, name)) = name.split_once(':') else {
-            return Err(LuaError::RuntimeError(format!(
-                "invalid data name, got '{name}', expected: 'ass_type:data_name'"
-            )));
-        };
-        let ass_type = ass_type.parse::<vvs_ass::types::ASSType>().map_err(|err| {
-            LuaError::RuntimeError(format!(
-                "failed to convert type '{ass_type}' to an vvs_ass::ASSType\n{err}"
-            ))
-        })?;
-        Ok((name, ass_type))
-    }
-}
-
-impl<'lua> FromLua<'lua> for RegisterDataValue {
-    fn from_lua(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult<Self> {
-        match lua_value {
-            LuaValue::String(name) => {
-                let name = name.to_string_lossy();
-                let (name, ass_type) = Self::decode_name(&name)?;
-                Ok(Self {
-                    name: name.to_string(),
-                    ass_type,
-                    doc: None,
-                    ty: None,
-                    value: None,
-                })
-            }
-
-            LuaValue::Table(table) => {
-                let name: String = table.get(1).map_err(|_| {
-                    LuaError::RuntimeError(
-                        "failed to get the name in the 'data' instruction".to_string(),
-                    )
-                })?;
-                let (name, ass_type) = Self::decode_name(&name)?;
-                let value = match table.get::<_, LuaValue>("value").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(value) => Some(LuaAssAuxValue::from_lua(value, lua)?),
-                };
-                let ty = match table.get::<_, LuaValue>("type").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(ty) => Some(Type::from_lua(ty, lua)?),
-                };
-                let doc = match table.get::<_, LuaValue>("doc").ok() {
-                    None | Some(LuaValue::Nil) => None,
-                    Some(LuaValue::String(doc)) => Some(doc.to_string_lossy().to_string()),
-                    Some(value) => {
-                        return Err(LuaError::RuntimeError(format!(
-                            "value of type '{}' can't be a docstring, expected a 'string'",
-                            value.type_name()
-                        )))
-                    }
-                };
-                if let (Some(value), Some(ty)) = (value.as_ref(), ty.as_ref()) {
-                    if value.ty().ne(ty) {
-                        return Err(LuaError::RuntimeError(format!(
-                            "tried to register a value of type '{}' when declaring the option of type '{ty}'",
-                            value.ty()
-                        )));
-                    }
-                }
-                Ok(Self {
-                    name: name.to_string(),
-                    ass_type,
-                    doc,
-                    ty,
-                    value,
-                })
-            }
-
-            _ => Err(LuaError::FromLuaConversionError {
-                from: lua_value.type_name(),
-                to: "RegisterDataValue",
-                message: Some(
-                    "expected a string or a table with the 'data' instruction".to_string(),
-                ),
-            }),
-        }
-    }
-}
diff --git a/src/Rust/vvs_lua/src/datas/lua_wrapper.rs b/src/Rust/vvs_lua/src/datas/lua_wrapper.rs
deleted file mode 100644
index b4ba36976c5fc6a5f09c179786d610cccc60e0a7..0000000000000000000000000000000000000000
--- a/src/Rust/vvs_lua/src/datas/lua_wrapper.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-//! Wraps the ASS element types and implement some helper traits.
-
-use crate::traits::TypedValue;
-use mlua::prelude::*;
-use vvs_ass::values::AssAuxValue;
-
-#[derive(Debug, Clone, PartialEq)]
-pub(crate) struct LuaAssAuxValue(AssAuxValue);
-
-impl LuaUserData for LuaAssAuxValue {
-    fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(_fields: &mut F) {}
-
-    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(_methods: &mut M) {}
-}
-
-impl TypedValue for LuaAssAuxValue {
-    fn ty(&self) -> crate::types::Type {
-        self.0.ty()
-    }
-}
diff --git a/src/Rust/vvs_lua/src/datas/register.rs b/src/Rust/vvs_lua/src/datas/register.rs
deleted file mode 100644
index d082ac77d2e319fc6fb52fff02f53946e16f4e36..0000000000000000000000000000000000000000
--- a/src/Rust/vvs_lua/src/datas/register.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use crate::datas::actions::RegisterDataValue;
-use mlua::prelude::*;
-use std::{cell::RefCell, collections::HashMap, rc::Rc};
-
-/// The structure used to register auxiliary fields in ASS structures.
-#[derive(Debug, Clone, PartialEq, Default)]
-pub(crate) struct VivyDataRegister {
-    registered: HashMap<String, RegisterDataValue>,
-}
-
-pub(crate) type VivyDataRegisterPtr = Rc<RefCell<VivyDataRegister>>;
-
-impl VivyDataRegister {
-    pub fn new() -> Rc<RefCell<VivyDataRegister>> {
-        Default::default()
-    }
-
-    fn register_data(&mut self, value: RegisterDataValue) -> Result<(), String> {
-        todo!()
-    }
-}
-
-impl LuaUserData for VivyDataRegister {
-    fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
-        methods.add_method_mut("register", |_, this, value: RegisterDataValue| {
-            this.register_data(value).map_err(LuaError::RuntimeError)
-        })
-    }
-}
diff --git a/src/Rust/vvs_lua/src/functions.rs b/src/Rust/vvs_lua/src/functions.rs
index 24586a6df9e14ad5137976b6ccb2676ca20aaa43..6834fc369da8c88d52b7af7dc822f6dd53bb021b 100644
--- a/src/Rust/vvs_lua/src/functions.rs
+++ b/src/Rust/vvs_lua/src/functions.rs
@@ -1,11 +1,11 @@
-use crate::{datas::VivyDataRegister, libs, options::VivyOptionRegister, TomlOptions};
+use crate::{data::VivyDataRegister, libs, options::VivyOptionRegister, TomlOptions};
 use mlua::{chunk, prelude::*};
 use std::path::Path;
 
 pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
     let options = VivyOptionRegister::new(options.unwrap_or_default());
     let version = env!("CARGO_PKG_VERSION");
-    let datas = VivyDataRegister::new();
+    let data = VivyDataRegister::new();
     let lua = Lua::new_with(
         LuaStdLib::MATH | LuaStdLib::TABLE | LuaStdLib::STRING | LuaStdLib::PACKAGE,
         LuaOptions::new().catch_rust_panics(true),
@@ -29,7 +29,7 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         module           = nil
         _VERSION         = nil
         local options    = $options
-        local datas      = $datas
+        local data       = $data
         local ReadOnly   = {
             package   = nil,
             coroutine = nil,
@@ -42,7 +42,7 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
         // Import Vivy
         $requires "vivy"
         vivy:___set_options(options)
-        vivy:___set_datas(datas)
+        vivy:___set_data(data)
         ReadOnly.vivy = vivy
         vivy          = nil
 
@@ -70,8 +70,11 @@ pub fn setup(options: Option<TomlOptions>) -> LuaResult<Lua> {
             error("not implemented", 2)
         end
 
-        function ReadOnly.data (table)
-            error("not implemented", 2)
+        function ReadOnly.data (name)
+            data:will_register(name)
+            return function (table)
+                data:register(name, table)
+            end
         end
 
         function ReadOnly.set (name)
diff --git a/src/Rust/vvs_lua/src/lib.rs b/src/Rust/vvs_lua/src/lib.rs
index 79489340bb289e501a6968432ecf9fba964b97db..20eec63bba725fd4d1315263d577d944df014178 100644
--- a/src/Rust/vvs_lua/src/lib.rs
+++ b/src/Rust/vvs_lua/src/lib.rs
@@ -3,7 +3,7 @@
 /// Re-export mlua
 pub use mlua;
 
-mod datas;
+mod data;
 mod error;
 mod functions;
 mod libs;
diff --git a/src/Rust/vvs_lua/src/libs/vivy.rs b/src/Rust/vvs_lua/src/libs/vivy.rs
index a0dccd54826135de610ca7abec4b11c0cfc5eccd..8f8bdf26f14e1ccc2d79eab9dd60d722a01dd9e8 100644
--- a/src/Rust/vvs_lua/src/libs/vivy.rs
+++ b/src/Rust/vvs_lua/src/libs/vivy.rs
@@ -1,13 +1,13 @@
 //! Provides the vivy runtime.
 
-use crate::{datas::VivyDataRegisterPtr, options::VivyOptionRegisterPtr, traits::TypedValue};
+use crate::{data::VivyDataRegisterPtr, options::VivyOptionRegisterPtr, traits::TypedValue};
 use mlua::prelude::*;
 use std::path::PathBuf;
 
 #[derive(Default, Debug)]
 pub struct Vivy {
     options: Option<VivyOptionRegisterPtr>,
-    datas: Option<VivyDataRegisterPtr>,
+    data: Option<VivyDataRegisterPtr>,
     path: Vec<String>,
     loaded: Vec<String>,
 }
@@ -63,8 +63,8 @@ impl LuaUserData for Vivy {
             |_, this, options: VivyOptionRegisterPtr| Ok(this.options = Some(options)),
         );
 
-        methods.add_method_mut("___set_datas", |_, this, datas: VivyDataRegisterPtr| {
-            Ok(this.datas = Some(datas))
+        methods.add_method_mut("___set_data", |_, this, data: VivyDataRegisterPtr| {
+            Ok(this.data = Some(data))
         });
 
         methods.add_method_mut("___finish_setup", |lua, this, ()| {
@@ -78,6 +78,11 @@ impl LuaUserData for Vivy {
                     lua.globals().raw_set(option.as_str(), value)?;
                 }
             }
+
+            if let Some(ref data) = this.data {
+                data.borrow_mut().compute_cached_tables()
+            }
+
             Ok(())
         });
 
@@ -130,6 +135,32 @@ impl LuaUserData for Vivy {
                 }
             }
 
+            if let Some(data) = this.data.as_ref() {
+                let data = data.borrow();
+                let added: Vec<_> = data
+                    .iter_registered()
+                    .map(|(ty, name)| (ty, name, format!("{ty}.aux.{name}")))
+                    .collect();
+                if !added.is_empty() {
+                    println!(" # Data");
+                    let padding = added.iter().map(|(_, _, name)| name.len()).max();
+                    let padding = padding.unwrap_or_default();
+                    for (ty, data_name, format_name) in added {
+                        println!(
+                            "   - data {format_name:padding$} = {}",
+                            data.get_table(ty)
+                                .as_inner()
+                                .borrow()
+                                .get(data_name)
+                                .expect("vivy internal error"),
+                        );
+                    }
+                }
+
+                let orphans: Vec<_> = data.get_orphan_data_declarations().collect();
+                assert!(orphans.is_empty());
+            }
+
             Ok(())
         })
     }
diff --git a/src/Rust/vvs_lua/src/traits.rs b/src/Rust/vvs_lua/src/traits.rs
index 33af6770b61c6551aa46513189223ad8ac8bbc17..91e1f21a338390da8c00901965a70502c0014a2a 100644
--- a/src/Rust/vvs_lua/src/traits.rs
+++ b/src/Rust/vvs_lua/src/traits.rs
@@ -20,14 +20,14 @@ impl TypedValue for toml::Value {
     }
 }
 
-impl TypedValue for vvs_ass::values::AssAuxValue {
+impl TypedValue for vvs_ass::ASSAuxValue {
     fn ty(&self) -> Type {
-        use vvs_ass::values::AssAuxValue;
+        use vvs_ass::ASSAuxValue;
         match self {
-            AssAuxValue::Integer(_) => Type::Base(BaseType::Number),
-            AssAuxValue::Floating(_) => Type::Base(BaseType::Number),
-            AssAuxValue::Boolean(_) => Type::Base(BaseType::Boolean),
-            AssAuxValue::String(_) => Type::Base(BaseType::String),
+            ASSAuxValue::Integer(_) => Type::Base(BaseType::Number),
+            ASSAuxValue::Floating(_) => Type::Base(BaseType::Number),
+            ASSAuxValue::Boolean(_) => Type::Base(BaseType::Boolean),
+            ASSAuxValue::String(_) => Type::Base(BaseType::String),
         }
     }
 }
diff --git a/src/Rust/vvs_procmacro/Cargo.toml b/src/Rust/vvs_procmacro/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..c7239a82228f3616d2bcba45a2802edc67b44601
--- /dev/null
+++ b/src/Rust/vvs_procmacro/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "vvs_procmacro"
+version.workspace = true
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+description.workspace = true
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "2.0", features = ["full"] }
+quote = "1.0"
+proc-macro2 = "1.0"
diff --git a/src/Rust/vvs_procmacro/src/lib.rs b/src/Rust/vvs_procmacro/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..50a2d6fba57c54f530808e2b7e011690da5f933c
--- /dev/null
+++ b/src/Rust/vvs_procmacro/src/lib.rs
@@ -0,0 +1,45 @@
+use proc_macro::TokenStream;
+use proc_macro2::*;
+use quote::*;
+
+#[proc_macro_derive(EnumVariantCount)]
+pub fn derive_enum_variant_count(input: TokenStream) -> TokenStream {
+    let syn_item = syn::parse::<syn::DeriveInput>(input).expect("failed to parse input with syn");
+    let (len, name) = match syn_item.data {
+        syn::Data::Enum(enum_item) => (
+            enum_item.variants.len(),
+            Ident::new(
+                &format!("{}_LENGTH", syn_item.ident.to_string().to_uppercase()),
+                syn_item.ident.span(),
+            ),
+        ),
+        _ => panic!("EnumVariantCount only works on Enums"),
+    };
+    quote! { pub const #name : usize = #len; }.into()
+}
+
+#[proc_macro_derive(EnumVariantIter)]
+pub fn derive_enum_variant_iter(input: TokenStream) -> TokenStream {
+    let syn_item = syn::parse::<syn::DeriveInput>(input).expect("failed to parse input with syn");
+    let (enum_name, name, content) = match syn_item.data {
+        syn::Data::Enum(enum_item) => (
+            syn_item.ident.clone(),
+            Ident::new(
+                &format!("{}_VALUES", syn_item.ident.to_string().to_uppercase()),
+                syn_item.ident.span(),
+            ),
+            syn::parse_str::<syn::Expr>(&format!(
+                "&[ {} ]",
+                enum_item
+                    .variants
+                    .into_iter()
+                    .map(|variant| format!("{}::{}", syn_item.ident, variant.ident.to_string()))
+                    .collect::<Vec<_>>()
+                    .join(", "),
+            ))
+            .expect("failed generation of enum's variant list"),
+        ),
+        _ => panic!("EnumVariantIter only works on Enums"),
+    };
+    quote! { pub const #name : &[#enum_name] = #content; }.into()
+}
diff --git a/utils/vvs/test_data.vvs b/utils/vvs/test_data.vvs
new file mode 100644
index 0000000000000000000000000000000000000000..01553af81813ddbb4092976593c7f3fe24ebe670
--- /dev/null
+++ b/utils/vvs/test_data.vvs
@@ -0,0 +1,3 @@
+-- vim: ft=lua
+
+data "line:number" { type = "number", value = 0 }