-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
268 additions
and
234 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
/// A liquid_value::Value literal. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// # #[macro_use] | ||
/// # extern crate liquid_value; | ||
/// # | ||
/// # fn main() { | ||
/// liquid_value!(5) | ||
/// .as_scalar().unwrap() | ||
/// .to_integer().unwrap(); | ||
/// liquid_value!("foo") | ||
/// .as_scalar().unwrap() | ||
/// .to_str(); | ||
/// liquid_value!([1, 2, 3]) | ||
/// .as_array().unwrap(); | ||
/// liquid_value!({"foo": 5}) | ||
/// .as_object().unwrap(); | ||
/// # } | ||
/// ``` | ||
#[macro_export(local_inner_macros)] | ||
macro_rules! liquid_value { | ||
($($value:tt)+) => { | ||
value_internal!($($value)+) | ||
}; | ||
} | ||
|
||
#[macro_export(local_inner_macros)] | ||
#[doc(hidden)] | ||
macro_rules! value_internal { | ||
// Done with trailing comma. | ||
(@array [$($elems:expr,)*]) => { | ||
value_internal_vec![$($elems,)*] | ||
}; | ||
|
||
// Done without trailing comma. | ||
(@array [$($elems:expr),*]) => { | ||
value_internal_vec![$($elems),*] | ||
}; | ||
|
||
// Next element is `nil`. | ||
(@array [$($elems:expr,)*] nil $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!(nil)] $($rest)*) | ||
}; | ||
|
||
// Next element is `true`. | ||
(@array [$($elems:expr,)*] true $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!(true)] $($rest)*) | ||
}; | ||
|
||
// Next element is `false`. | ||
(@array [$($elems:expr,)*] false $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!(false)] $($rest)*) | ||
}; | ||
|
||
// Next element is an array. | ||
(@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!([$($array)*])] $($rest)*) | ||
}; | ||
|
||
// Next element is a map. | ||
(@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!({$($map)*})] $($rest)*) | ||
}; | ||
|
||
// Next element is an expression followed by comma. | ||
(@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)* value_internal!($next),] $($rest)*) | ||
}; | ||
|
||
// Last element is an expression with no trailing comma. | ||
(@array [$($elems:expr,)*] $last:expr) => { | ||
value_internal!(@array [$($elems,)* value_internal!($last)]) | ||
}; | ||
|
||
// Comma after the most recent element. | ||
(@array [$($elems:expr),*] , $($rest:tt)*) => { | ||
value_internal!(@array [$($elems,)*] $($rest)*) | ||
}; | ||
|
||
// Unexpected token after most recent element. | ||
(@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { | ||
value_unexpected!($unexpected) | ||
}; | ||
|
||
////////////////////////////////////////////////////////////////////////// | ||
// TT muncher for parsing the inside of an object {...}. Each entry is | ||
// inserted into the given map variable. | ||
// | ||
// Must be invoked as: value_internal!(@object $map () ($($tt)*) ($($tt)*)) | ||
// | ||
// We require two copies of the input tokens so that we can match on one | ||
// copy and trigger errors on the other copy. | ||
////////////////////////////////////////////////////////////////////////// | ||
|
||
// Done. | ||
(@object $object:ident () () ()) => {}; | ||
|
||
// Insert the current entry followed by trailing comma. | ||
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { | ||
let _ = $object.insert(($($key)+).into(), $value); | ||
value_internal!(@object $object () ($($rest)*) ($($rest)*)); | ||
}; | ||
|
||
// Current entry followed by unexpected token. | ||
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { | ||
value_unexpected!($unexpected); | ||
}; | ||
|
||
// Insert the last entry without trailing comma. | ||
(@object $object:ident [$($key:tt)+] ($value:expr)) => { | ||
let _ = $object.insert(($($key)+).into(), $value); | ||
}; | ||
|
||
// Next value is `nil`. | ||
(@object $object:ident ($($key:tt)+) (: nil $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!(nil)) $($rest)*); | ||
}; | ||
|
||
// Next value is `true`. | ||
(@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!(true)) $($rest)*); | ||
}; | ||
|
||
// Next value is `false`. | ||
(@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!(false)) $($rest)*); | ||
}; | ||
|
||
// Next value is an array. | ||
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!([$($array)*])) $($rest)*); | ||
}; | ||
|
||
// Next value is a map. | ||
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!({$($map)*})) $($rest)*); | ||
}; | ||
|
||
// Next value is an expression followed by comma. | ||
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!($value)) , $($rest)*); | ||
}; | ||
|
||
// Last value is an expression with no trailing comma. | ||
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { | ||
value_internal!(@object $object [$($key)+] (value_internal!($value))); | ||
}; | ||
|
||
// Missing value for last entry. Trigger a reasonable error message. | ||
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => { | ||
// "unexpected end of macro invocation" | ||
value_internal!(); | ||
}; | ||
|
||
// Missing colon and value for last entry. Trigger a reasonable error | ||
// message. | ||
(@object $object:ident ($($key:tt)+) () $copy:tt) => { | ||
// "unexpected end of macro invocation" | ||
value_internal!(); | ||
}; | ||
|
||
// Misplaced colon. Trigger a reasonable error message. | ||
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { | ||
// Takes no arguments so "no rules expected the token `:`". | ||
value_unexpected!($colon); | ||
}; | ||
|
||
// Found a comma inside a key. Trigger a reasonable error message. | ||
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { | ||
// Takes no arguments so "no rules expected the token `,`". | ||
value_unexpected!($comma); | ||
}; | ||
|
||
// Key is fully parenthesized. This avoids clippy double_parens false | ||
// positives because the parenthesization may be necessary here. | ||
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); | ||
}; | ||
|
||
// Munch a token into the current key. | ||
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { | ||
value_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); | ||
}; | ||
|
||
////////////////////////////////////////////////////////////////////////// | ||
// The main implementation. | ||
// | ||
// Must be invoked as: value_internal!($($value)+) | ||
////////////////////////////////////////////////////////////////////////// | ||
|
||
(nil) => { | ||
::liquid_value::Value::Nil | ||
}; | ||
|
||
(true) => { | ||
::liquid_value::Value::scalar(true) | ||
}; | ||
|
||
(false) => { | ||
::liquid_value::Value::scalar(false) | ||
}; | ||
|
||
([]) => { | ||
::liquid_value::Value::Array(value_internal_vec![]) | ||
}; | ||
|
||
([ $($tt:tt)+ ]) => { | ||
::liquid_value::Value::Array(value_internal!(@array [] $($tt)+)) | ||
}; | ||
|
||
({}) => { | ||
::liquid_value::Value::Object(Default::default()) | ||
}; | ||
|
||
({ $($tt:tt)+ }) => { | ||
::liquid_value::Value::Object({ | ||
let mut object = ::liquid_value::Object::new(); | ||
value_internal!(@object object () ($($tt)+) ($($tt)+)); | ||
object | ||
}) | ||
}; | ||
|
||
($other:ident) => { | ||
$other | ||
}; | ||
|
||
// Any Serialize type: numbers, strings, struct literals, variables etc. | ||
// Must be below every other rule. | ||
($other:expr) => { | ||
::liquid_value::to_value($other).unwrap() | ||
}; | ||
} | ||
|
||
#[macro_export] | ||
#[doc(hidden)] | ||
macro_rules! value_internal_vec { | ||
($($content:tt)*) => { | ||
vec![$($content)*] | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.