Skip to content

Commit 244057b

Browse files
committed
macros
1 parent ad95805 commit 244057b

File tree

3 files changed

+164
-2
lines changed

3 files changed

+164
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ratslang"
3-
version = "0.1.0-alpha.3"
3+
version = "0.1.0-alpha.4"
44
keywords = ["config", "lang", "parser", "robotics"]
55
license = "MIT OR Apache-2.0"
66
repository = "https://github.com/stelzo/ratslang/"

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ let ast = ratslang::compile_file(&file.to_path_buf(), None, None).unwrap();
114114
Then, you can safely read the variables you need using Rust's powerful pattern matching.
115115

116116
~~~rust
117-
// "Just get the variable"
117+
// "Just get the variable" with macro and direct conversion
118+
let k_neighbors = resolve_var!(ast, "k_neighborhood", as usize, Rhs::Val(Val::NumVal(NumVal::Integer(i))) => {i})?;
119+
120+
// ... without macro and AST types
118121
let avar = ast.vars.resolve("password").unwrap();
119122
let avar = ast.vars.resolve("_my_namespace._some_var").unwrap();
120123
assert_eq!(avar, Some(ratslang::Rhs::Val(ratslang::Val::StringVal(":)".to_owned()))));

src/lib.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,165 @@ pub fn compile_code_with_state(
13601360
Err(anyhow!("Could not parse ratslang code."))
13611361
}
13621362

1363+
// -- Macros --
1364+
1365+
/// Gets a length range.
1366+
/// It handles `..` and `100mm..2000mm` syntax, converting all values to meters (f32).
1367+
#[macro_export]
1368+
macro_rules! resolve_length_range_meters_float {
1369+
($ns:expr, $field:expr, $default_from:expr, $default_to:expr) => {{
1370+
let convert_mm_to_meters = |val: &Val| -> anyhow::Result<f32> {
1371+
match val {
1372+
Val::UnitedVal(uv) if uv.unit == Unit::WayMillimeter => {
1373+
Ok(Length::new::<millimeter>(uv.val as f32).get::<meter>())
1374+
},
1375+
_ => Err(anyhow!("Expected a united value with 'mm' unit for length range '{}'", $field))
1376+
}
1377+
};
1378+
1379+
let rhs_val = resolve_var!($ns, $field, as &Rhs, r => {r})?;
1380+
match rhs_val {
1381+
Rhs::EmptyRange => Ok(($default_from, $default_to)),
1382+
Rhs::Range { from, to } => {
1383+
let from_val = from.as_ref().map_or(Ok($default_from), |v| convert_mm_to_meters(v))?;
1384+
let to_val = to.as_ref().map_or(Ok($default_to), |v| convert_mm_to_meters(v))?;
1385+
Ok((from_val, to_val))
1386+
}
1387+
_ => Err(anyhow!("Expected a range expression for field '{}'", $field))
1388+
}
1389+
}};
1390+
}
1391+
1392+
#[macro_export]
1393+
macro_rules! resolve_var {
1394+
// Arm for variable names passed as identifiers
1395+
($asts:expr, $var_name_ident:ident, as $target_type:ty, $($pattern:pat_param)|+ => $extraction_block:block) => {{
1396+
let __var_name_str = stringify!($var_name_ident);
1397+
let __resolved_opt = match $asts.user.resolve(__var_name_str)? {
1398+
Some(val) => Some(val),
1399+
None => $asts.defaults.resolve(__var_name_str)?,
1400+
};
1401+
1402+
match __resolved_opt {
1403+
Some(__resolved_val) => match __resolved_val {
1404+
$($pattern)|+ => {
1405+
let __extracted_val = $extraction_block;
1406+
Ok(__extracted_val)
1407+
}
1408+
_ => Err(anyhow!(format!(
1409+
"Pattern mismatch for '{}'. Expected pattern for type `{}`.",
1410+
__var_name_str,
1411+
stringify!($target_type)
1412+
))),
1413+
}?
1414+
.try_into()
1415+
.map_err(|e| {
1416+
anyhow!(
1417+
"Failed to convert '{}' to type `{}`: {:?}",
1418+
__var_name_str,
1419+
stringify!($target_type),
1420+
e
1421+
)
1422+
}),
1423+
None => Err(anyhow!(concat!(
1424+
"Required variable '",
1425+
stringify!($var_name_ident),
1426+
"' not found in any configuration."
1427+
))),
1428+
}
1429+
}};
1430+
1431+
// Arm for variable names passed as string expressions
1432+
($asts:expr, $var_name_expr:expr, as $target_type:ty, $($pattern:pat_param)|+ => $extraction_block:block) => {{
1433+
let __var_name_str = $var_name_expr;
1434+
let __resolved_opt = match $asts.user.resolve(__var_name_str)? {
1435+
Some(val) => Some(val),
1436+
None => $asts.defaults.resolve(__var_name_str)?,
1437+
};
1438+
1439+
match __resolved_opt {
1440+
Some(__resolved_val) => match __resolved_val {
1441+
$($pattern)|+ => {
1442+
let __extracted_val = $extraction_block;
1443+
Ok(__extracted_val)
1444+
}
1445+
_ => Err(anyhow!(format!(
1446+
"Pattern mismatch for '{}'. Expected pattern for type `{}`.",
1447+
__var_name_str,
1448+
stringify!($target_type)
1449+
))),
1450+
}?
1451+
.try_into()
1452+
.map_err(|e| {
1453+
anyhow!(
1454+
"Failed to convert '{}' to type `{}`: {:?}",
1455+
__var_name_str,
1456+
stringify!($target_type),
1457+
e
1458+
)
1459+
}),
1460+
None => Err(anyhow!(format!(
1461+
"Required variable '{}' not found in any configuration.",
1462+
__var_name_str
1463+
))),
1464+
}
1465+
}};
1466+
}
1467+
1468+
/// Gets a range from floating numbers while also accepting integers by converting them to floats.
1469+
#[macro_export]
1470+
macro_rules! resolve_float_force_range {
1471+
($ns:expr, $field:expr, $default_from:expr, $default_to:expr) => {{
1472+
let rhs_val = resolve_var!($ns, $field, as &Rhs, r => {r})?;
1473+
match rhs_val {
1474+
Rhs::EmptyRange => Ok(($default_from, $default_to)),
1475+
Rhs::Range { from, to } => {
1476+
let from_val = match from {
1477+
Some(Val::NumVal(NumVal::Floating(f))) => f as f32,
1478+
Some(Val::NumVal(NumVal::Integer(i))) => i as f32,
1479+
None => $default_from,
1480+
_ => return Err(anyhow!(format!("Expected numeric value for 'from' in float range '{}'", $field))),
1481+
};
1482+
let to_val = match to {
1483+
Some(Val::NumVal(NumVal::Floating(f))) => f as f32,
1484+
Some(Val::NumVal(NumVal::Integer(i))) => i as f32,
1485+
None => $default_to,
1486+
_ => return Err(anyhow!(format!("Expected numeric value for 'to' in float range '{}'", $field))),
1487+
};
1488+
Ok((from_val, to_val))
1489+
},
1490+
_ => Err(anyhow!(format!("Expected a range expression for field '{}'", $field)))
1491+
}
1492+
}};
1493+
}
1494+
1495+
/// Gets a range from floating numbers without implicit conversions.
1496+
#[macro_export]
1497+
macro_rules! resolve_float_range {
1498+
($ns:expr, $field:expr, $default_from:expr, $default_to:expr) => {{
1499+
let rhs_val = resolve_var!($ns, $field, as &Rhs, r => {r})?;
1500+
match rhs_val {
1501+
Rhs::EmptyRange => Ok(($default_from, $default_to)),
1502+
Rhs::Range { from, to } => {
1503+
let from_val = match from {
1504+
Some(Val::NumVal(NumVal::Floating(f))) => f as f32,
1505+
Some(Val::NumVal(NumVal::Integer(_))) => return Err(anyhow!(format!("Expected floating value for 'from' in float range '{}'", $field))),
1506+
None => $default_from,
1507+
_ => return Err(anyhow!(format!("Expected numeric value for 'from' in float range '{}'", $field))),
1508+
};
1509+
let to_val = match to {
1510+
Some(Val::NumVal(NumVal::Floating(f))) => f as f32,
1511+
Some(Val::NumVal(NumVal::Integer(_))) => return Err(anyhow!(format!("Expected floating value for 'from' in float range '{}'", $field))),
1512+
None => $default_to,
1513+
_ => return Err(anyhow!(format!("Expected numeric value for 'to' in float range '{}'", $field))),
1514+
};
1515+
Ok((from_val, to_val))
1516+
},
1517+
_ => Err(anyhow!(format!("Expected a range expression for field '{}'", $field)))
1518+
}
1519+
}};
1520+
}
1521+
13631522
#[cfg(test)]
13641523
mod tests {
13651524
use super::*;

0 commit comments

Comments
 (0)