Add support for string literal property names in object maps.
This commit is contained in:
parent
45ee51874f
commit
fce51758d1
28
README.md
28
README.md
@ -729,6 +729,8 @@ let a = { 40 + 2 }; // 'a' is set to the value of the statement block, which
|
|||||||
Variables
|
Variables
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
[variables]: #variables
|
||||||
|
|
||||||
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
|
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
|
||||||
|
|
||||||
Variable names must start with an ASCII letter or an underscore '`_`', must contain at least one ASCII letter, and must start with an ASCII letter before a digit.
|
Variable names must start with an ASCII letter or an underscore '`_`', must contain at least one ASCII letter, and must start with an ASCII letter before a digit.
|
||||||
@ -761,7 +763,7 @@ x == 42; // the parent block's 'x' is not changed
|
|||||||
Constants
|
Constants
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Constants can be defined using the `const` keyword and are immutable. Constants follow the same naming rules as [variables](#variables).
|
Constants can be defined using the `const` keyword and are immutable. Constants follow the same naming rules as [variables].
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
const x = 42;
|
const x = 42;
|
||||||
@ -1046,7 +1048,14 @@ Object maps
|
|||||||
|
|
||||||
Object maps are dictionaries. Properties of any type (`Dynamic`) can be freely added and retrieved.
|
Object maps are dictionaries. Properties of any type (`Dynamic`) can be freely added and retrieved.
|
||||||
Object map literals are built within braces '`${`' ... '`}`' (_name_ `:` _value_ syntax similar to Rust)
|
Object map literals are built within braces '`${`' ... '`}`' (_name_ `:` _value_ syntax similar to Rust)
|
||||||
and separated by commas '`,`'.
|
and separated by commas '`,`'. The property _name_ can be a simple variable name following the same
|
||||||
|
naming rules as [variables], or an arbitrary string literal.
|
||||||
|
|
||||||
|
Property values can be accessed via the dot notation (_object_ `.` _property_) or index notation (_object_ `[` _property_ `]`).
|
||||||
|
The dot notation allows only property names that follow the same naming rules as [variables].
|
||||||
|
The index notation allows setting/getting properties of arbitrary names (even the empty string).
|
||||||
|
|
||||||
|
**Important:** Trying to read a non-existent property returns `()` instead of causing an error.
|
||||||
|
|
||||||
The Rust type of a Rhai object map is `rhai::Map`.
|
The Rust type of a Rhai object map is `rhai::Map`.
|
||||||
|
|
||||||
@ -1068,13 +1077,19 @@ Examples:
|
|||||||
let y = ${ // object map literal with 3 properties
|
let y = ${ // object map literal with 3 properties
|
||||||
a: 1,
|
a: 1,
|
||||||
bar: "hello",
|
bar: "hello",
|
||||||
baz: 123.456
|
"baz!$@": 123.456, // like JS, you can use any string as property names...
|
||||||
|
"": false, // even the empty string!
|
||||||
|
|
||||||
|
a: 42 // <- syntax error: duplicated property name
|
||||||
};
|
};
|
||||||
y.a = 42;
|
|
||||||
|
y.a = 42; // access via dot notation
|
||||||
|
y.baz!$@ = 42; // <- syntax error: only proper variable names allowed in dot notation
|
||||||
|
y."baz!$@" = 42; // <- syntax error: strings not allowed in dot notation
|
||||||
|
|
||||||
print(y.a); // prints 42
|
print(y.a); // prints 42
|
||||||
|
|
||||||
print(y["bar"]); // prints "hello" - access via string index
|
print(y["baz!$@"]); // prints 123.456 - access via index notation
|
||||||
|
|
||||||
ts.obj = y; // object maps can be assigned completely (by value copy)
|
ts.obj = y; // object maps can be assigned completely (by value copy)
|
||||||
let foo = ts.list.a;
|
let foo = ts.list.a;
|
||||||
@ -1096,6 +1111,9 @@ foo == 42;
|
|||||||
y.has("a") == true;
|
y.has("a") == true;
|
||||||
y.has("xyz") == false;
|
y.has("xyz") == false;
|
||||||
|
|
||||||
|
y.xyz == (); // A non-existing property returns '()'
|
||||||
|
y["xyz"] == ();
|
||||||
|
|
||||||
print(y.len()); // prints 3
|
print(y.len()); // prints 3
|
||||||
|
|
||||||
y.clear(); // empty the object map
|
y.clear(); // empty the object map
|
||||||
|
@ -59,7 +59,7 @@ pub enum ParseErrorType {
|
|||||||
DuplicatedProperty(String),
|
DuplicatedProperty(String),
|
||||||
/// Invalid expression assigned to constant.
|
/// Invalid expression assigned to constant.
|
||||||
ForbiddenConstantExpr(String),
|
ForbiddenConstantExpr(String),
|
||||||
/// Missing a property name for maps.
|
/// Missing a property name for custom types and maps.
|
||||||
PropertyExpected,
|
PropertyExpected,
|
||||||
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
/// Missing a variable name after the `let`, `const` or `for` keywords.
|
||||||
VariableExpected,
|
VariableExpected,
|
||||||
|
@ -1677,6 +1677,7 @@ fn parse_map_literal<'a>(
|
|||||||
.into_err_eof()
|
.into_err_eof()
|
||||||
})? {
|
})? {
|
||||||
(Token::Identifier(s), pos) => (s.clone(), pos),
|
(Token::Identifier(s), pos) => (s.clone(), pos),
|
||||||
|
(Token::StringConst(s), pos) => (s.clone(), pos),
|
||||||
(_, pos) if map.is_empty() => {
|
(_, pos) if map.is_empty() => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
"}".into(),
|
"}".into(),
|
||||||
@ -2053,27 +2054,30 @@ fn parse_binary_op<'a>(
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Period => {
|
Token::Period => {
|
||||||
fn change_var_to_property(expr: Expr) -> Expr {
|
fn check_property(expr: Expr) -> Result<Expr, ParseError> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Dot(lhs, rhs, pos) => Expr::Dot(
|
// xxx.lhs.rhs
|
||||||
Box::new(change_var_to_property(*lhs)),
|
Expr::Dot(lhs, rhs, pos) => Ok(Expr::Dot(
|
||||||
Box::new(change_var_to_property(*rhs)),
|
Box::new(check_property(*lhs)?),
|
||||||
|
Box::new(check_property(*rhs)?),
|
||||||
pos,
|
pos,
|
||||||
),
|
)),
|
||||||
|
// xxx.lhs[idx]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, idx, pos) => {
|
Expr::Index(lhs, idx, pos) => {
|
||||||
Expr::Index(Box::new(change_var_to_property(*lhs)), idx, pos)
|
Ok(Expr::Index(Box::new(check_property(*lhs)?), idx, pos))
|
||||||
}
|
}
|
||||||
Expr::Variable(s, pos) => Expr::Property(s, pos),
|
// xxx.id
|
||||||
expr => expr,
|
Expr::Variable(id, pos) => Ok(Expr::Property(id, pos)),
|
||||||
|
// xxx.prop
|
||||||
|
expr @ Expr::Property(_, _) => Ok(expr),
|
||||||
|
// xxx.fn()
|
||||||
|
expr @ Expr::FunctionCall(_, _, _, _) => Ok(expr),
|
||||||
|
expr => Err(PERR::PropertyExpected.into_err(expr.position())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Dot(
|
Expr::Dot(Box::new(current_lhs), Box::new(check_property(rhs)?), pos)
|
||||||
Box::new(current_lhs),
|
|
||||||
Box::new(change_var_to_property(rhs)),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comparison operators default to false when passed invalid operands
|
// Comparison operators default to false when passed invalid operands
|
||||||
@ -2258,12 +2262,16 @@ fn parse_for<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// for name in ...
|
// for name in ...
|
||||||
match input
|
match input.next().ok_or_else(|| {
|
||||||
.next()
|
PERR::MissingToken("in".into(), "after the iteration variable".into()).into_err_eof()
|
||||||
.ok_or_else(|| PERR::MissingToken("in".into(), "here".into()).into_err_eof())?
|
})? {
|
||||||
{
|
|
||||||
(Token::In, _) => (),
|
(Token::In, _) => (),
|
||||||
(_, pos) => return Err(PERR::MissingToken("in".into(), "here".into()).into_err(pos)),
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken("in".into(), "after the iteration variable".into())
|
||||||
|
.into_err(pos),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
|
@ -18,8 +18,8 @@ fn test_map_indexing() -> Result<(), EvalAltResult> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<char>(
|
engine.eval::<char>(
|
||||||
r#"
|
r#"
|
||||||
let y = ${d: 1, e: ${a: 42, b: 88, c: "93"}, x: 9};
|
let y = ${d: 1, "e": ${a: 42, b: 88, "": "93"}, " 123 xyz": 9};
|
||||||
y.e["c"][1]
|
y.e[""][1]
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
'3'
|
'3'
|
||||||
|
Loading…
Reference in New Issue
Block a user