#[macro_export]
macro_rules! eure {
({}) => {{
$crate::document::EureDocument::new_empty()
}};
({ $($body:tt)* }) => {{
#[allow(unused_mut)]
let mut c = $crate::document::constructor::DocumentConstructor::new();
$crate::eure!(@stmt c; $($body)*);
c.finish()
}};
($c:ident; { $($body:tt)* }) => {{
$crate::eure!(@stmt $c; $($body)*);
}};
(@value_tt null) => { $crate::value::PrimitiveValue::Null };
(@value_tt true) => { true };
(@value_tt false) => { false };
(@value_tt $v:literal) => { $v };
(@value_tt $v:expr) => { $v };
(@array_items $c:ident;) => {};
(@array_items $c:ident; , $($rest:tt)*) => {{
$crate::eure!(@array_items $c; $($rest)*);
}};
(@array_items $c:ident; @ code ($content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@array_items $c; $($rest)*);
}};
(@array_items $c:ident; @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@array_items $c; $($rest)*);
}};
(@array_items $c:ident; [$($inner:tt)*] $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$c.bind_empty_array().unwrap();
$crate::eure!(@array_items $c; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@array_items $c; $($rest)*);
}};
(@array_items $c:ident; ($($inner:tt)*) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$c.bind_empty_tuple().unwrap();
$crate::eure!(@tuple_items $c 0; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@array_items $c; $($rest)*);
}};
(@array_items $c:ident; $item:tt $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$c.bind_from($crate::eure!(@value_tt $item)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@array_items $c; $($rest)*);
}};
(@tuple_items $c:ident $idx:expr;) => {};
(@tuple_items $c:ident $idx:expr; , $($rest:tt)*) => {{
$crate::eure!(@tuple_items $c $idx; $($rest)*);
}};
(@tuple_items $c:ident $idx:expr; @ code ($content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@tuple_items $c ($idx + 1); $($rest)*);
}};
(@tuple_items $c:ident $idx:expr; @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@tuple_items $c ($idx + 1); $($rest)*);
}};
(@tuple_items $c:ident $idx:expr; [$($inner:tt)*] $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$c.bind_empty_array().unwrap();
$crate::eure!(@array_items $c; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@tuple_items $c ($idx + 1); $($rest)*);
}};
(@tuple_items $c:ident $idx:expr; ($($inner:tt)*) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$c.bind_empty_tuple().unwrap();
$crate::eure!(@tuple_items $c 0; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@tuple_items $c ($idx + 1); $($rest)*);
}};
(@tuple_items $c:ident $idx:expr; $item:tt $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$c.bind_from($crate::eure!(@value_tt $item)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@tuple_items $c ($idx + 1); $($rest)*);
}};
(@object_key $key:ident) => { stringify!($key) };
(@object_key $key:tt) => { $key };
(@object_items $c:ident;) => {};
(@object_items $c:ident; , $($rest:tt)*) => {{
$crate::eure!(@object_items $c; $($rest)*);
}};
(@object_items $c:ident; $key:tt => @ code ($content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Value($crate::eure!(@object_key $key).into())).unwrap();
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@object_items $c; $($rest)*);
}};
(@object_items $c:ident; $key:tt => @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Value($crate::eure!(@object_key $key).into())).unwrap();
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@object_items $c; $($rest)*);
}};
(@object_items $c:ident; $key:tt => [$($inner:tt)*] $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Value($crate::eure!(@object_key $key).into())).unwrap();
$c.bind_empty_array().unwrap();
$crate::eure!(@array_items $c; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@object_items $c; $($rest)*);
}};
(@object_items $c:ident; $key:tt => ($($inner:tt)*) $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Value($crate::eure!(@object_key $key).into())).unwrap();
$c.bind_empty_tuple().unwrap();
$crate::eure!(@tuple_items $c 0; $($inner)*);
$c.end_scope(scope).unwrap();
$crate::eure!(@object_items $c; $($rest)*);
}};
(@object_items $c:ident; $key:tt => $val:tt $($rest:tt)*) => {{
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Value($crate::eure!(@object_key $key).into())).unwrap();
$c.bind_from($crate::eure!(@value_tt $val)).unwrap();
$c.end_scope(scope).unwrap();
$crate::eure!(@object_items $c; $($rest)*);
}};
(@stmt $c:ident;) => {};
(@stmt $c:ident; , $($rest:tt)*) => {{
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = ! $($rest:tt)*) => {{
$c.bind_hole(None).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = - $v:literal $($rest:tt)*) => {{
$c.bind_from(-$v).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = @ code ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = @ block ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block_implicit($content)).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = @ block ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block($content, $lang)).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = [] $($rest:tt)*) => {{
$c.bind_empty_array().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = [$($items:tt)+] $($rest:tt)*) => {{
$c.bind_empty_array().unwrap();
$crate::eure!(@array_items $c; $($items)+);
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = () $($rest:tt)*) => {{
$c.bind_empty_tuple().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = ($($items:tt)+) $($rest:tt)*) => {{
$c.bind_empty_tuple().unwrap();
$crate::eure!(@tuple_items $c 0; $($items)+);
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = { $key:tt => $($inner:tt)+ } $($rest:tt)*) => {{
$c.bind_empty_map().unwrap();
$crate::eure!(@object_items $c; $key => $($inner)+);
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; = $v:tt $($rest:tt)*) => {{
$c.bind_from($crate::eure!(@value_tt $v)).unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@stmt $c:ident; @ [] $($rest:tt)*) => {{
$c.begin_section();
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@section_after_seg $c scope; $($rest)*);
}};
(@stmt $c:ident; @ [$idx:literal] $($rest:tt)*) => {{
$c.begin_section();
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@section_after_seg $c scope; $($rest)*);
}};
(@stmt $c:ident; @ $seg:ident $($rest:tt)*) => {{
$c.begin_section();
let scope = $c.begin_scope();
$c.navigate($crate::path::PathSegment::Ident(
$crate::identifier::Identifier::new_unchecked(stringify!($seg))
)).unwrap();
$crate::eure!(@section_after_seg $c scope; $($rest)*);
}};
(@stmt $c:ident; $($tokens:tt)+) => {{
$c.begin_binding();
let scope = $c.begin_scope();
$crate::eure!(@path $c scope; $($tokens)+);
}};
(@section_after_seg $c:ident $scope:ident; . $seg:ident $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Ident(
$crate::identifier::Identifier::new_unchecked(stringify!($seg))
)).unwrap();
$crate::eure!(@section_after_seg $c $scope; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; # [] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@section_after_seg $c $scope; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; # [$idx:literal] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@section_after_seg $c $scope; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; [$($arr:tt)*] $($rest:tt)*) => {{
compile_error!(
"in section headers, raw []/[N] is only allowed immediately after `@`; use #[] or #[N] for continuation"
);
}};
(@section_after_seg $c:ident $scope:ident; = $v:tt $($rest:tt)*) => {{
$c.bind_from($crate::eure!(@value_tt $v)).unwrap();
$c.begin_section_items();
$crate::eure!(@section_bindings $c $scope; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; {} $($rest:tt)*) => {{
$c.begin_eure_block();
$c.bind_empty_map().unwrap();
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_section_block().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; { $($inner:tt)+ } $($rest:tt)*) => {{
$c.begin_eure_block();
$crate::eure!(@stmt $c; $($inner)+);
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_section_block().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@section_after_seg $c:ident $scope:ident; $($rest:tt)*) => {{
$c.begin_section_items();
$crate::eure!(@section_bindings $c $scope; $($rest)*);
}};
(@section_bindings $c:ident $scope:ident;) => {{
$c.end_section_items().unwrap();
$c.end_scope($scope).unwrap();
}};
(@section_bindings $c:ident $scope:ident; , $($rest:tt)*) => {{
$crate::eure!(@section_bindings $c $scope; $($rest)*);
}};
(@section_bindings $c:ident $scope:ident; @ [] $($rest:tt)*) => {{
$c.end_section_items().unwrap();
$c.end_scope($scope).unwrap();
$crate::eure!(@stmt $c; @ [] $($rest)*);
}};
(@section_bindings $c:ident $scope:ident; @ [$idx:literal] $($rest:tt)*) => {{
$c.end_section_items().unwrap();
$c.end_scope($scope).unwrap();
$crate::eure!(@stmt $c; @ [$idx] $($rest)*);
}};
(@section_bindings $c:ident $scope:ident; @ $seg:ident $($rest:tt)*) => {{
$c.end_section_items().unwrap();
$c.end_scope($scope).unwrap();
$crate::eure!(@stmt $c; @ $seg $($rest)*);
}};
(@section_bindings $c:ident $scope:ident; $($tokens:tt)+) => {{
$c.begin_binding();
let inner_scope = $c.begin_scope();
$crate::eure!(@section_path $c $scope inner_scope; $($tokens)+);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; $seg:ident $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Ident(
$crate::identifier::Identifier::new_unchecked(stringify!($seg))
)).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; % $ext:ident $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Extension(
$crate::identifier::Identifier::new_unchecked(stringify!($ext))
)).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; % $ext:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Extension(
$ext.parse().unwrap()
)).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; # [] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@section_terminal $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; # [$idx:literal] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@section_terminal $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; # $idx:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; ($($tuple:tt)*) $($rest:tt)*) => {{
let key = $crate::eure!(@build_tuple_key; $($tuple)*);
$c.navigate($crate::path::PathSegment::Value(key)).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; $key:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Value($key.into())).unwrap();
$crate::eure!(@section_after_path $c $section_scope $scope; $($rest)*);
}};
(@section_path $c:ident $section_scope:ident $scope:ident; [$($arr:tt)*] $($rest:tt)*) => {{
compile_error!(
"in section bindings, raw []/[N] is not allowed; use #[] or #[N] for array segments"
);
}};
(@section_after_path $c:ident $section_scope:ident $scope:ident; [$($arr:tt)*] $($rest:tt)*) => {{
compile_error!(
"in section bindings, raw []/[N] is not allowed; use #[] or #[N] for array segments"
);
}};
(@section_after_path $c:ident $section_scope:ident $scope:ident; $($rest:tt)*) => {{
$crate::eure!(@section_terminal $c $section_scope $scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; . $($rest:tt)+) => {{
$crate::eure!(@section_path $c $section_scope $scope; $($rest)+);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; # [] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@section_terminal $c $section_scope $scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; # [$idx:literal] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@section_terminal $c $section_scope $scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; [$($arr:tt)*] $($rest:tt)*) => {{
compile_error!(
"in section bindings, raw []/[N] is not allowed; use #[] or #[N] for array segments"
);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = ! $($rest:tt)*) => {{
$c.bind_hole(None).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = @ code ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = @ block ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block_implicit($content)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = @ block ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block($content, $lang)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; = $v:tt $($rest:tt)*) => {{
$c.bind_from($crate::eure!(@value_tt $v)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; {} $($rest:tt)*) => {{
$c.begin_eure_block();
$c.bind_empty_map().unwrap();
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_block().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@section_terminal $c:ident $section_scope:ident $scope:ident; { $($inner:tt)+ } $($rest:tt)*) => {{
$c.begin_eure_block();
$crate::eure!(@stmt $c; $($inner)+);
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_block().unwrap();
$crate::eure!(@section_bindings $c $section_scope; $($rest)*);
}};
(@path $c:ident $scope:ident; $seg:ident $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Ident(
$crate::identifier::Identifier::new_unchecked(stringify!($seg))
)).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; % $ext:ident $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Extension(
$crate::identifier::Identifier::new_unchecked(stringify!($ext))
)).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; % $ext:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Extension(
$ext.parse().unwrap()
)).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; # [] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; # [$idx:literal] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; # $idx:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::TupleIndex($idx)).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; ($($tuple:tt)*) $($rest:tt)*) => {{
let key = $crate::eure!(@build_tuple_key; $($tuple)*);
$c.navigate($crate::path::PathSegment::Value(key)).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@path $c:ident $scope:ident; $key:literal $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::Value($key.into())).unwrap();
$crate::eure!(@after_path $c $scope; $($rest)*);
}};
(@build_tuple_key;) => {{
$crate::value::ObjectKey::Tuple($crate::value::Tuple(Default::default()))
}};
(@build_tuple_key; $($item:expr),+ $(,)?) => {{
$crate::value::ObjectKey::Tuple($crate::value::Tuple::from_iter(
[$(<_ as Into<$crate::value::ObjectKey>>::into($item)),+]
))
}};
(@after_path $c:ident $scope:ident; [$($arr:tt)*] $($rest:tt)*) => {{
$crate::eure!(@array_marker $c $scope [$($arr)*]; $($rest)*);
}};
(@after_path $c:ident $scope:ident; $($rest:tt)*) => {{
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@array_marker $c:ident $scope:ident []; $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@array_marker $c:ident $scope:ident [$idx:literal]; $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@terminal $c:ident $scope:ident; . $($rest:tt)+) => {{
$crate::eure!(@path $c $scope; $($rest)+);
}};
(@terminal $c:ident $scope:ident; # [] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(None)).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@terminal $c:ident $scope:ident; # [$idx:literal] $($rest:tt)*) => {{
$c.navigate($crate::path::PathSegment::ArrayIndex(Some($idx))).unwrap();
$crate::eure!(@terminal $c $scope; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = ! $($rest:tt)*) => {{
$c.bind_hole(None).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = - $v:literal $($rest:tt)*) => {{
$c.bind_from(-$v).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = @ code ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline_implicit($content)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = @ code ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::inline($content, $lang)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = @ block ($content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block_implicit($content)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = @ block ($lang:literal, $content:literal) $($rest:tt)*) => {{
$c.bind_from($crate::text::Text::block($content, $lang)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = [] $($rest:tt)*) => {{
$c.bind_empty_array().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = [$($items:tt)+] $($rest:tt)*) => {{
$c.bind_empty_array().unwrap();
$crate::eure!(@array_items $c; $($items)+);
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = () $($rest:tt)*) => {{
$c.bind_empty_tuple().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = ($($items:tt)+) $($rest:tt)*) => {{
$c.bind_empty_tuple().unwrap();
$crate::eure!(@tuple_items $c 0; $($items)+);
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = { $key:tt => $($inner:tt)+ } $($rest:tt)*) => {{
$c.bind_empty_map().unwrap();
$crate::eure!(@object_items $c; $key => $($inner)+);
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; = $v:tt $($rest:tt)*) => {{
$c.bind_from($crate::eure!(@value_tt $v)).unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_value().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; {} $($rest:tt)*) => {{
$c.begin_eure_block();
$c.bind_empty_map().unwrap();
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_block().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
(@terminal $c:ident $scope:ident; { $($inner:tt)+ } $($rest:tt)*) => {{
$c.begin_eure_block();
$crate::eure!(@stmt $c; $($inner)+);
$c.end_eure_block().unwrap();
$c.end_scope($scope).unwrap();
$c.end_binding_block().unwrap();
$crate::eure!(@stmt $c; $($rest)*);
}};
}
#[macro_export]
macro_rules! eure_source {
({}) => {{
$crate::source::SourceDocument::empty()
}};
({ $($body:tt)* }) => {{
#[allow(unused_mut)]
let mut c = $crate::document::source_constructor::SourceConstructor::new();
$crate::eure!(c; { $($body)* });
c.finish()
}};
}
#[cfg(test)]
mod tests {
use crate::document::EureDocument;
use alloc::vec;
#[test]
fn test_eure_empty() {
let doc = eure!({});
assert_eq!(doc, EureDocument::new_empty());
}
#[test]
fn test_eure_simple_assignment() {
let doc = eure!({ name = "Alice" });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let name_node_id = root.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name_node = doc.node(name_node_id);
let prim = name_node.as_primitive().unwrap();
assert_eq!(prim.as_str(), Some("Alice"));
}
#[test]
fn test_eure_nested_path() {
let doc = eure!({
user.name = "Bob"
user.age = 30
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
let name_id = user.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("Bob"));
let age_id = user.as_map().unwrap().get_node_id(&"age".into()).unwrap();
let age = doc.node(age_id);
assert!(matches!(
age.as_primitive(),
Some(crate::value::PrimitiveValue::Integer(_))
));
}
#[test]
fn test_eure_block() {
let doc = eure!({
user {
name = "Charlie"
active = true
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
let name_id = user.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("Charlie"));
}
#[test]
fn test_eure_extension() {
let doc = eure!({
field.%variant = @code("text")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let field_id = root.as_map().unwrap().get_node_id(&"field".into()).unwrap();
let field = doc.node(field_id);
let variant_id = field.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
let text = variant.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "text");
}
#[test]
fn test_eure_extension_with_child() {
let doc = eure!({
field.%variant.name = @code("text")
field.%variant.min_length = 3
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let field_id = root.as_map().unwrap().get_node_id(&"field".into()).unwrap();
let field = doc.node(field_id);
let variant_id = field.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
let name_id = variant
.as_map()
.unwrap()
.get_node_id(&"name".into())
.unwrap();
let name = doc.node(name_id);
let text = name.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "text");
let min_length_id = variant
.as_map()
.unwrap()
.get_node_id(&"min_length".into())
.unwrap();
let min_length = doc.node(min_length_id);
assert!(matches!(
min_length.as_primitive(),
Some(crate::value::PrimitiveValue::Integer(_))
));
}
#[test]
fn test_eure_array() {
let doc = eure!({ tags = ["a", "b", "c"] });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let tags_id = root.as_map().unwrap().get_node_id(&"tags".into()).unwrap();
let tags = doc.node(tags_id);
let array = tags.as_array().unwrap();
assert_eq!(array.len(), 3);
}
#[test]
fn test_eure_tuple() {
let doc = eure!({ point = (1.5, 2.5) });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let point_id = root.as_map().unwrap().get_node_id(&"point".into()).unwrap();
let point = doc.node(point_id);
let tuple = point.as_tuple().unwrap();
assert_eq!(tuple.len(), 2);
}
#[test]
fn test_eure_multiple_assignments() {
let doc = eure!({
a = 1
b = 2
c = 3
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let map = root.as_map().unwrap();
assert_eq!(map.len(), 3);
}
#[test]
fn test_eure_complex() {
let doc = eure!({
schema {
field.%variant = @code("text")
field.min_length = 3
field.max_length = 20
}
tags = ["required"]
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let schema_id = root
.as_map()
.unwrap()
.get_node_id(&"schema".into())
.unwrap();
let schema = doc.node(schema_id);
let field_id = schema
.as_map()
.unwrap()
.get_node_id(&"field".into())
.unwrap();
let field = doc.node(field_id);
assert!(field.get_extension(&"variant".parse().unwrap()).is_some());
let tags_id = root.as_map().unwrap().get_node_id(&"tags".into()).unwrap();
let tags = doc.node(tags_id);
assert_eq!(tags.as_array().unwrap().len(), 1);
}
#[test]
fn test_eure_array_push() {
let doc = eure!({
items[] = 1
items[] = 2
items[] = 3
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 3);
}
#[test]
fn test_eure_array_push_with_child() {
let doc = eure!({
items[].name = "first"
items[].name = "second"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
let first_id = array.get(0).unwrap();
let first = doc.node(first_id);
let name_id = first.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("first"));
}
#[test]
fn test_eure_root_extension_with_explicit_array_entries() {
let doc = eure!({
%meta { speaker = "Alice" }
#[] = "Hello"
#[] = "World"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let meta_id = root.get_extension(&"meta".parse().unwrap()).unwrap();
let meta = doc.node(meta_id);
let speaker_id = meta
.as_map()
.unwrap()
.get_node_id(&"speaker".into())
.unwrap();
let speaker = doc.node(speaker_id);
assert_eq!(speaker.as_primitive().unwrap().as_str(), Some("Alice"));
let array = root.as_array().unwrap();
assert_eq!(array.len(), 2);
let first = doc.node(array.get(0).unwrap());
assert_eq!(first.as_primitive().unwrap().as_str(), Some("Hello"));
let second = doc.node(array.get(1).unwrap());
assert_eq!(second.as_primitive().unwrap().as_str(), Some("World"));
}
#[test]
fn test_eure_tuple_index() {
let doc = eure!({
point.#0 = 1.5
point.#1 = 2.5
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let point_id = root.as_map().unwrap().get_node_id(&"point".into()).unwrap();
let point = doc.node(point_id);
let tuple = point.as_tuple().unwrap();
assert_eq!(tuple.len(), 2);
}
#[test]
fn test_eure_mixed_path_extension_array() {
let doc = eure!({
field.%items[].name = "item1"
field.%items[].name = "item2"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let field_id = root.as_map().unwrap().get_node_id(&"field".into()).unwrap();
let field = doc.node(field_id);
let items_id = field.get_extension(&"items".parse().unwrap()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
}
#[test]
fn test_eure_mixed_path_array_extension() {
let doc = eure!({
items[].%variant = @code("text")
items[].%variant = @code("number")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
let first_id = array.get(0).unwrap();
let first = doc.node(first_id);
let variant_id = first.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
assert_eq!(
variant.as_primitive().unwrap().as_text().unwrap().as_str(),
"text"
);
}
#[test]
fn test_eure_tuple_key() {
use crate::value::{ObjectKey, Tuple};
let doc = eure!({
map.(1, "key") = "value1"
map.(2, "key") = "value2"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let map_id = root.as_map().unwrap().get_node_id(&"map".into()).unwrap();
let map_node = doc.node(map_id);
let map = map_node.as_map().unwrap();
assert_eq!(map.len(), 2);
let tuple_key = ObjectKey::Tuple(Tuple(alloc::vec![1.into(), "key".into()]));
let value_id = map.get_node_id(&tuple_key).unwrap();
let value = doc.node(value_id);
assert_eq!(value.as_primitive().unwrap().as_str(), Some("value1"));
}
#[test]
fn test_eure_tuple_key_with_bool() {
use crate::value::{ObjectKey, Tuple};
let doc = eure!({
map.(true, 1) = "yes"
map.(false, 1) = "no"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let map_id = root.as_map().unwrap().get_node_id(&"map".into()).unwrap();
let map_node = doc.node(map_id);
let map = map_node.as_map().unwrap();
assert_eq!(map.len(), 2);
let tuple_key = ObjectKey::Tuple(Tuple(alloc::vec![true.into(), 1.into()]));
let value_id = map.get_node_id(&tuple_key).unwrap();
let value = doc.node(value_id);
assert_eq!(value.as_primitive().unwrap().as_str(), Some("yes"));
}
#[test]
fn test_eure_tuple_key_with_child() {
use crate::value::{ObjectKey, Tuple};
let doc = eure!({
map.(1, 2).name = "point_a"
map.(1, 2).value = 42
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let map_id = root.as_map().unwrap().get_node_id(&"map".into()).unwrap();
let map_node = doc.node(map_id);
let map = map_node.as_map().unwrap();
let tuple_key = ObjectKey::Tuple(Tuple(alloc::vec![1.into(), 2.into()]));
let entry_id = map.get_node_id(&tuple_key).unwrap();
let entry = doc.node(entry_id);
let entry_map = entry.as_map().unwrap();
let name_id = entry_map.get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("point_a"));
}
#[test]
fn test_eure_string_key() {
let doc = eure!({
field."min-length" = 3
field."max-length" = 20
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let field_id = root.as_map().unwrap().get_node_id(&"field".into()).unwrap();
let field = doc.node(field_id);
let field_map = field.as_map().unwrap();
let min_id = field_map.get_node_id(&"min-length".into()).unwrap();
let min_node = doc.node(min_id);
assert!(matches!(
min_node.as_primitive(),
Some(crate::value::PrimitiveValue::Integer(_))
));
}
#[test]
fn test_eure_object_literal() {
let doc = eure!({
variants.click = { "x" => 1.0, "y" => 2.0 }
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let variants_id = root
.as_map()
.unwrap()
.get_node_id(&"variants".into())
.unwrap();
let variants = doc.node(variants_id);
let click_id = variants
.as_map()
.unwrap()
.get_node_id(&"click".into())
.unwrap();
let click = doc.node(click_id);
let click_map = click.as_map().unwrap();
assert_eq!(click_map.len(), 2);
assert!(click_map.get(&"x".into()).is_some());
assert!(click_map.get(&"y".into()).is_some());
}
#[test]
fn test_eure_object_literal_with_string() {
let doc = eure!({
schema.variants.success = { "data" => "any" }
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let schema_id = root
.as_map()
.unwrap()
.get_node_id(&"schema".into())
.unwrap();
let schema = doc.node(schema_id);
let variants_id = schema
.as_map()
.unwrap()
.get_node_id(&"variants".into())
.unwrap();
let variants = doc.node(variants_id);
let success_id = variants
.as_map()
.unwrap()
.get_node_id(&"success".into())
.unwrap();
let success = doc.node(success_id);
let success_map = success.as_map().unwrap();
let data_id = success_map.get_node_id(&"data".into()).unwrap();
let data = doc.node(data_id);
assert_eq!(data.as_primitive().unwrap().as_str(), Some("any"));
}
#[test]
fn test_eure_value_binding() {
let doc = eure!({
= @code("hello")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "hello");
}
#[test]
fn test_eure_value_binding_with_extension() {
let doc = eure!({
= @code("any")
%variant = "literal"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "any");
let variant_id = root.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
assert_eq!(variant.as_primitive().unwrap().as_str(), Some("literal"));
}
#[test]
fn test_eure_empty_block() {
let doc = eure!({ config {} });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let config_id = root
.as_map()
.unwrap()
.get_node_id(&"config".into())
.unwrap();
let config = doc.node(config_id);
let map = config
.as_map()
.expect("Empty block should create an empty map");
assert!(map.is_empty());
}
#[test]
fn test_eure_null_literal() {
let doc = eure!({ optional = null });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let opt_id = root
.as_map()
.unwrap()
.get_node_id(&"optional".into())
.unwrap();
let opt = doc.node(opt_id);
assert!(matches!(
opt.as_primitive(),
Some(crate::value::PrimitiveValue::Null)
));
}
#[test]
fn test_eure_null_root() {
let doc = eure!({
= null
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
assert!(matches!(
root.as_primitive(),
Some(crate::value::PrimitiveValue::Null)
));
}
#[test]
fn test_eure_hole_literal() {
use crate::document::node::NodeValue;
let doc = eure!({
placeholder = !
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let placeholder_id = root
.as_map()
.unwrap()
.get_node_id(&"placeholder".into())
.unwrap();
let placeholder = doc.node(placeholder_id);
assert_eq!(placeholder.content, NodeValue::Hole(None));
}
#[test]
fn test_eure_hole_root() {
use crate::document::node::NodeValue;
let doc = eure!({
= !
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
assert_eq!(root.content, NodeValue::Hole(None));
}
#[test]
fn test_eure_code_inline_implicit() {
let doc = eure!({
snippet = @code("let x = 1")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let snippet_id = root
.as_map()
.unwrap()
.get_node_id(&"snippet".into())
.unwrap();
let snippet = doc.node(snippet_id);
let text = snippet.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "let x = 1");
assert!(text.language.is_implicit());
}
#[test]
fn test_eure_code_inline_with_language() {
let doc = eure!({
query = @code("sql", "SELECT * FROM users")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let query_id = root.as_map().unwrap().get_node_id(&"query".into()).unwrap();
let query = doc.node(query_id);
let text = query.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "SELECT * FROM users");
assert_eq!(text.language.as_str(), Some("sql"));
}
#[test]
fn test_eure_block_implicit() {
let doc = eure!({
script = @block("fn main() {}")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let script_id = root
.as_map()
.unwrap()
.get_node_id(&"script".into())
.unwrap();
let script = doc.node(script_id);
let text = script.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "fn main() {}\n");
assert!(text.language.is_implicit());
}
#[test]
fn test_eure_block_with_language() {
let doc = eure!({
code = @block("rust", "fn main() {\n println!(\"Hello\");\n}")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let code_id = root.as_map().unwrap().get_node_id(&"code".into()).unwrap();
let code = doc.node(code_id);
let text = code.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.language.as_str(), Some("rust"));
assert!(text.as_str().contains("println!"));
}
#[test]
fn test_eure_code_at_root() {
let doc = eure!({
= @code("hello")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "hello");
}
#[test]
fn test_eure_code_with_language_at_root() {
let doc = eure!({
= @code("sql", "SELECT 1")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "SELECT 1");
assert_eq!(text.language.as_str(), Some("sql"));
}
#[test]
fn test_eure_block_at_root() {
let doc = eure!({
= @block("fn main() {}")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "fn main() {}\n");
assert!(text.language.is_implicit());
}
#[test]
fn test_eure_block_with_language_at_root() {
let doc = eure!({
= @block("rust", "fn main() {}")
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let text = root.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "fn main() {}\n");
assert_eq!(text.language.as_str(), Some("rust"));
}
#[test]
fn test_eure_array_specific_index() {
let doc = eure!({
items[0] = "first"
items[1] = "second"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
let first_id = array.get(0).unwrap();
let first = doc.node(first_id);
assert_eq!(first.as_primitive().unwrap().as_str(), Some("first"));
let second_id = array.get(1).unwrap();
let second = doc.node(second_id);
assert_eq!(second.as_primitive().unwrap().as_str(), Some("second"));
}
#[test]
fn test_eure_array_index_with_child() {
let doc = eure!({
items[0].name = "first"
items[0].value = 1
items[1].name = "second"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
let first_id = array.get(0).unwrap();
let first = doc.node(first_id);
let name_id = first.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("first"));
}
#[test]
fn test_eure_nested_empty_blocks() {
let doc = eure!({
a {
b {
c {}
}
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let a_id = root.as_map().unwrap().get_node_id(&"a".into()).unwrap();
let a = doc.node(a_id);
let b_id = a.as_map().unwrap().get_node_id(&"b".into()).unwrap();
let b = doc.node(b_id);
let c_id = b.as_map().unwrap().get_node_id(&"c".into()).unwrap();
let c = doc.node(c_id);
let map = c.as_map().expect("c should be an empty map");
assert!(map.is_empty());
}
#[test]
fn test_eure_multiple_extensions() {
let doc = eure!({
field.%variant = @code("text")
field.%"variant-repr" = "internal"
field.%schema = "custom"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let field_id = root.as_map().unwrap().get_node_id(&"field".into()).unwrap();
let field = doc.node(field_id);
assert!(field.get_extension(&"variant".parse().unwrap()).is_some());
assert!(
field
.get_extension(&"variant-repr".parse().unwrap())
.is_some()
);
assert!(field.get_extension(&"schema".parse().unwrap()).is_some());
}
#[test]
fn test_eure_extension_on_array_element() {
let doc = eure!({
items[0].%variant = @code("text")
items[0].value = "first"
items[1].%variant = @code("number")
items[1].value = 42
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert_eq!(array.len(), 2);
let first_id = array.get(0).unwrap();
let first = doc.node(first_id);
let variant_id = first.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
assert_eq!(
variant.as_primitive().unwrap().as_text().unwrap().as_str(),
"text"
);
let value_id = first
.as_map()
.unwrap()
.get_node_id(&"value".into())
.unwrap();
let value = doc.node(value_id);
assert_eq!(value.as_primitive().unwrap().as_str(), Some("first"));
let second_id = array.get(1).unwrap();
let second = doc.node(second_id);
let variant_id = second.get_extension(&"variant".parse().unwrap()).unwrap();
let variant = doc.node(variant_id);
assert_eq!(
variant.as_primitive().unwrap().as_text().unwrap().as_str(),
"number"
);
}
#[test]
fn test_eure_deep_nesting() {
let doc = eure!({ a.b.c.d.e.f = "deep" });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let a_id = root.as_map().unwrap().get_node_id(&"a".into()).unwrap();
let a = doc.node(a_id);
let b_id = a.as_map().unwrap().get_node_id(&"b".into()).unwrap();
let b = doc.node(b_id);
let c_id = b.as_map().unwrap().get_node_id(&"c".into()).unwrap();
let c = doc.node(c_id);
let d_id = c.as_map().unwrap().get_node_id(&"d".into()).unwrap();
let d = doc.node(d_id);
let e_id = d.as_map().unwrap().get_node_id(&"e".into()).unwrap();
let e = doc.node(e_id);
let f_id = e.as_map().unwrap().get_node_id(&"f".into()).unwrap();
let f = doc.node(f_id);
assert_eq!(f.as_primitive().unwrap().as_str(), Some("deep"));
}
#[test]
fn test_eure_empty_array_literal() {
let doc = eure!({ items = [] });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let items_id = root.as_map().unwrap().get_node_id(&"items".into()).unwrap();
let items = doc.node(items_id);
let array = items.as_array().unwrap();
assert!(array.is_empty());
}
#[test]
fn test_eure_empty_tuple_literal() {
let doc = eure!({ point = () });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let point_id = root.as_map().unwrap().get_node_id(&"point".into()).unwrap();
let point = doc.node(point_id);
let tuple = point.as_tuple().unwrap();
assert!(tuple.is_empty());
}
#[test]
fn test_eure_empty_map_literal() {
let doc = eure!({ data {} });
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let data_id = root.as_map().unwrap().get_node_id(&"data".into()).unwrap();
let data = doc.node(data_id);
let map = data.as_map().unwrap();
assert!(map.is_empty());
}
#[test]
fn test_eure_mixed_null_and_values() {
let doc = eure!({
name = "Alice"
age = null
active = true
score = null
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let map = root.as_map().unwrap();
assert_eq!(map.len(), 4);
let age_id = map.get_node_id(&"age".into()).unwrap();
let age = doc.node(age_id);
assert!(matches!(
age.as_primitive(),
Some(crate::value::PrimitiveValue::Null)
));
}
#[test]
fn test_eure_section_basic() {
let doc = eure!({
@user
name = "Alice"
age = 30
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
let name_id = user.as_map().unwrap().get_node_id(&"name".into()).unwrap();
let name = doc.node(name_id);
assert_eq!(name.as_primitive().unwrap().as_str(), Some("Alice"));
}
#[test]
fn test_eure_section_multiple() {
let doc = eure!({
@user
name = "Alice"
@settings
theme = "dark"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
assert!(user.as_map().unwrap().get_node_id(&"name".into()).is_some());
let settings_id = root
.as_map()
.unwrap()
.get_node_id(&"settings".into())
.unwrap();
let settings = doc.node(settings_id);
assert!(
settings
.as_map()
.unwrap()
.get_node_id(&"theme".into())
.is_some()
);
}
#[test]
fn test_eure_section_dotted_path() {
let doc = eure!({
@user.profile
name = "Alice"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
let profile_id = user
.as_map()
.unwrap()
.get_node_id(&"profile".into())
.unwrap();
let profile = doc.node(profile_id);
assert!(
profile
.as_map()
.unwrap()
.get_node_id(&"name".into())
.is_some()
);
}
#[test]
fn test_eure_section_root_array_marker() {
let doc = eure!({
@[]
text = "Hello"
@[]
text = "World"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let array = root.as_array().unwrap();
assert_eq!(array.len(), 2);
let first = doc.node(array.get(0).unwrap());
let first_text_id = first.as_map().unwrap().get_node_id(&"text".into()).unwrap();
let first_text = doc.node(first_text_id);
assert_eq!(first_text.as_primitive().unwrap().as_str(), Some("Hello"));
let second = doc.node(array.get(1).unwrap());
let second_text_id = second
.as_map()
.unwrap()
.get_node_id(&"text".into())
.unwrap();
let second_text = doc.node(second_text_id);
assert_eq!(second_text.as_primitive().unwrap().as_str(), Some("World"));
}
#[test]
fn test_eure_section_explicit_array_continuation() {
let doc = eure!({
@dialog #[]
text = "Hello"
@dialog #[]
text = "World"
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let dialog_id = root
.as_map()
.unwrap()
.get_node_id(&"dialog".into())
.unwrap();
let dialog = doc.node(dialog_id);
let array = dialog.as_array().unwrap();
assert_eq!(array.len(), 2);
let first = doc.node(array.get(0).unwrap());
let first_text_id = first.as_map().unwrap().get_node_id(&"text".into()).unwrap();
let first_text = doc.node(first_text_id);
assert_eq!(first_text.as_primitive().unwrap().as_str(), Some("Hello"));
let second = doc.node(array.get(1).unwrap());
let second_text_id = second
.as_map()
.unwrap()
.get_node_id(&"text".into())
.unwrap();
let second_text = doc.node(second_text_id);
assert_eq!(second_text.as_primitive().unwrap().as_str(), Some("World"));
}
#[test]
fn test_eure_section_with_block() {
let doc = eure!({
@user {
name = "Alice"
age = 30
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
assert!(user.as_map().unwrap().get_node_id(&"name".into()).is_some());
assert!(user.as_map().unwrap().get_node_id(&"age".into()).is_some());
}
#[test]
fn test_eure_section_block_with_nested() {
let doc = eure!({
@config {
server {
host = "localhost"
port = 8080
}
debug = true
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let config_id = root
.as_map()
.unwrap()
.get_node_id(&"config".into())
.unwrap();
let config = doc.node(config_id);
assert!(
config
.as_map()
.unwrap()
.get_node_id(&"server".into())
.is_some()
);
assert!(
config
.as_map()
.unwrap()
.get_node_id(&"debug".into())
.is_some()
);
let server_id = config
.as_map()
.unwrap()
.get_node_id(&"server".into())
.unwrap();
let server = doc.node(server_id);
assert!(
server
.as_map()
.unwrap()
.get_node_id(&"host".into())
.is_some()
);
assert!(
server
.as_map()
.unwrap()
.get_node_id(&"port".into())
.is_some()
);
}
#[test]
fn test_eure_section_block_multiple() {
let doc = eure!({
@user {
name = "Alice"
}
@settings {
theme = "dark"
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
assert!(root.as_map().unwrap().get_node_id(&"user".into()).is_some());
assert!(
root.as_map()
.unwrap()
.get_node_id(&"settings".into())
.is_some()
);
}
#[test]
fn test_eure_section_block_dotted_path() {
let doc = eure!({
@server.config {
host = "localhost"
port = 8080
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let server_id = root
.as_map()
.unwrap()
.get_node_id(&"server".into())
.unwrap();
let server = doc.node(server_id);
let config_id = server
.as_map()
.unwrap()
.get_node_id(&"config".into())
.unwrap();
let config = doc.node(config_id);
assert!(
config
.as_map()
.unwrap()
.get_node_id(&"host".into())
.is_some()
);
assert!(
config
.as_map()
.unwrap()
.get_node_id(&"port".into())
.is_some()
);
}
#[test]
fn test_eure_section_block_empty() {
let doc = eure!({
@empty {}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let empty_id = root.as_map().unwrap().get_node_id(&"empty".into()).unwrap();
let empty = doc.node(empty_id);
assert!(empty.as_map().unwrap().is_empty());
}
#[test]
fn test_eure_section_mixed_styles() {
let doc = eure!({
@user {
name = "Alice"
}
@settings
theme = "dark"
debug = true
@logging {
level = "info"
}
});
let root_id = doc.get_root_id();
let root = doc.node(root_id);
let user_id = root.as_map().unwrap().get_node_id(&"user".into()).unwrap();
let user = doc.node(user_id);
assert!(user.as_map().unwrap().get_node_id(&"name".into()).is_some());
let settings_id = root
.as_map()
.unwrap()
.get_node_id(&"settings".into())
.unwrap();
let settings = doc.node(settings_id);
assert!(
settings
.as_map()
.unwrap()
.get_node_id(&"theme".into())
.is_some()
);
assert!(
settings
.as_map()
.unwrap()
.get_node_id(&"debug".into())
.is_some()
);
let logging_id = root
.as_map()
.unwrap()
.get_node_id(&"logging".into())
.unwrap();
let logging = doc.node(logging_id);
assert!(
logging
.as_map()
.unwrap()
.get_node_id(&"level".into())
.is_some()
);
}
#[test]
fn test_eure_section_in_section_block() {
let doc = eure!({
@ settings {
theme = "dark"
@ logging
level = "info"
}
});
let settings = doc
.parse_context(doc.get_root_id())
.parse_record()
.expect("Failed to parse record")
.field_record("settings")
.expect("Failed to parse settings");
let theme = settings
.parse_field::<&str>("theme")
.expect("Failed to parse theme");
let logging = settings
.field_record("logging")
.expect("Failed to parse logging")
.parse_field::<&str>("level")
.expect("Failed to parse level");
settings
.deny_unknown_fields()
.expect("Failed to deny unknown fields");
assert_eq!(theme, "dark");
assert_eq!(logging, "info");
}
#[test]
fn test_eure_variable_text() {
use crate::text::Text;
let code = Text::inline_implicit("fn main() {}");
let doc = eure!({ snippet = code });
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let snippet_ctx = root.field("snippet").unwrap();
let snippet_node = doc.node(snippet_ctx.node_id());
let text = snippet_node.as_primitive().unwrap().as_text().unwrap();
assert_eq!(text.as_str(), "fn main() {}");
}
#[test]
fn test_eure_variable_in_array() {
use alloc::vec::Vec;
let first = "one";
let second = "two";
let third = "three";
let doc = eure!({ items = [first, second, third] });
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let items = root.parse_field::<Vec<&str>>("items").unwrap();
assert_eq!(items, vec!["one", "two", "three"]);
}
#[test]
fn test_eure_variable_in_tuple() {
let x = 1.5;
let y = 2.5;
let doc = eure!({ point = (x, y) });
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let point = root.parse_field::<(f64, f64)>("point").unwrap();
assert_eq!(point, (1.5, 2.5));
}
#[test]
fn test_eure_variable_in_object_literal() {
let x_val = 10.0;
let y_val = 20.0;
let doc = eure!({
coords = {
"x" => x_val
"y" => y_val
}
});
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let coords = root.field_record("coords").unwrap();
let x = coords.parse_field::<f64>("x").unwrap();
let y = coords.parse_field::<f64>("y").unwrap();
assert_eq!(x, 10.0);
assert_eq!(y, 20.0);
}
#[test]
#[allow(clippy::bool_assert_comparison)] fn test_eure_variable_mixed_with_literals() {
let username = "bob";
let is_active = true;
let doc = eure!({
user.name = username
user.active = is_active
user.role = "admin"
user.level = 5
});
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let user = root.field_record("user").unwrap();
assert_eq!(user.parse_field::<&str>("name").unwrap(), "bob");
assert_eq!(user.parse_field::<bool>("active").unwrap(), true);
assert_eq!(user.parse_field::<&str>("role").unwrap(), "admin");
assert_eq!(user.parse_field::<i32>("level").unwrap(), 5);
}
#[test]
fn test_eure_variable_in_nested_array() {
use alloc::vec::Vec;
let tag1 = "rust";
let tag2 = "macro";
let doc = eure!({
tags[] = tag1
tags[] = tag2
});
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let tags = root.parse_field::<Vec<&str>>("tags").unwrap();
assert_eq!(tags, vec!["rust", "macro"]);
}
#[test]
fn test_eure_variable_at_root() {
let value = 42;
let doc = eure!({
= value
});
let ctx = doc.parse_context(doc.get_root_id());
let root_value = ctx.parse::<i32>().unwrap();
assert_eq!(root_value, 42);
}
#[test]
fn test_eure_variable_in_section() {
let theme_value = "dark";
let lang_value = "en";
let doc = eure!({
@settings
theme = theme_value
language = lang_value
});
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let settings = root.field_record("settings").unwrap();
assert_eq!(settings.parse_field::<&str>("theme").unwrap(), "dark");
assert_eq!(settings.parse_field::<&str>("language").unwrap(), "en");
}
#[test]
fn test_eure_variable_null_and_primitive() {
use crate::value::PrimitiveValue;
let null_value = PrimitiveValue::Null;
let doc = eure!({ optional = null_value });
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let optional_ctx = root.field("optional").unwrap();
let optional_node = doc.node(optional_ctx.node_id());
assert!(matches!(
optional_node.as_primitive().unwrap(),
PrimitiveValue::Null
));
}
#[test]
fn test_eure_source_empty() {
let source_doc = eure_source!({});
assert_eq!(source_doc.document(), &EureDocument::new_empty());
assert!(source_doc.root_source().bindings.is_empty());
assert!(source_doc.root_source().sections.is_empty());
}
#[test]
fn test_eure_source_simple_bindings() {
let source_doc = eure_source!({
name = "Alice"
age = 30
});
let doc = source_doc.document();
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
assert_eq!(root.parse_field::<&str>("name").unwrap(), "Alice");
assert_eq!(root.parse_field::<i64>("age").unwrap(), 30);
let root_source = source_doc.root_source();
assert_eq!(root_source.bindings.len(), 2);
assert!(root_source.sections.is_empty());
}
#[test]
fn test_eure_source_nested() {
use crate::source::BindSource;
let source_doc = eure_source!({
user {
name = "Bob"
active = true
}
});
let doc = source_doc.document();
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
let user = root.field_record("user").unwrap();
assert_eq!(user.parse_field::<&str>("name").unwrap(), "Bob");
assert!(user.parse_field::<bool>("active").unwrap());
let root_source = source_doc.root_source();
assert_eq!(root_source.bindings.len(), 1);
match &root_source.bindings[0].bind {
BindSource::Block(source_id) => {
let inner = source_doc.source(*source_id);
assert_eq!(inner.bindings.len(), 2);
}
_ => panic!("Expected BindSource::Block"),
}
}
#[test]
fn test_eure_generic_entry_point() {
use crate::document::constructor::DocumentConstructor;
let mut c = DocumentConstructor::new();
eure!(c; {
x = 1
y = 2
});
let doc = c.finish();
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
assert_eq!(root.parse_field::<i64>("x").unwrap(), 1);
assert_eq!(root.parse_field::<i64>("y").unwrap(), 2);
}
#[test]
fn test_eure_source_generic_entry_point() {
use crate::document::source_constructor::SourceConstructor;
let mut c = SourceConstructor::new();
eure!(c; {
message = "hello"
});
let source_doc = c.finish();
let doc = source_doc.document();
let root = doc.parse_context(doc.get_root_id()).parse_record().unwrap();
assert_eq!(root.parse_field::<&str>("message").unwrap(), "hello");
}
}