Support for trailing commas.
This commit is contained in:
parent
dc540755e7
commit
1adf3cc39a
11
README.md
11
README.md
@ -1607,6 +1607,8 @@ The following methods (mostly defined in the [`BasicArrayPackage`](#packages) bu
|
|||||||
```rust
|
```rust
|
||||||
let y = [2, 3]; // array literal with 2 elements
|
let y = [2, 3]; // array literal with 2 elements
|
||||||
|
|
||||||
|
let y = [2, 3,]; // trailing comma is OK
|
||||||
|
|
||||||
y.insert(0, 1); // insert element at the beginning
|
y.insert(0, 1); // insert element at the beginning
|
||||||
y.insert(999, 4); // insert element at the end
|
y.insert(999, 4); // insert element at the end
|
||||||
|
|
||||||
@ -1751,6 +1753,8 @@ ts.obj = y; // object maps can be assigned completely (by value copy
|
|||||||
let foo = ts.list.a;
|
let foo = ts.list.a;
|
||||||
foo == 42;
|
foo == 42;
|
||||||
|
|
||||||
|
let foo = #{ a:1,}; // trailing comma is OK
|
||||||
|
|
||||||
let foo = #{ a:1, b:2, c:3 }["a"];
|
let foo = #{ a:1, b:2, c:3 }["a"];
|
||||||
foo == 1;
|
foo == 1;
|
||||||
|
|
||||||
@ -2115,7 +2119,12 @@ fn add(x, y) {
|
|||||||
return x + y;
|
return x + y;
|
||||||
}
|
}
|
||||||
|
|
||||||
print(add(2, 3));
|
fn sub(x, y,) { // trailing comma in parameters list is OK
|
||||||
|
return x - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(add(2, 3)); // prints 5
|
||||||
|
print(sub(2, 3,)); // prints -1 - trailing comma in arguments list is OK
|
||||||
```
|
```
|
||||||
|
|
||||||
### Implicit return
|
### Implicit return
|
||||||
|
@ -22,6 +22,7 @@ New features
|
|||||||
* Indexers are now split into getters and setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a shorthand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added.
|
* Indexers are now split into getters and setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a shorthand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added.
|
||||||
* `Engine:register_fn` and `Engine:register_result_fn` accepts functions that take parameters of type `&str` (immutable string slice), which maps directly to `ImmutableString`. This is to avoid needing wrappers for functions taking string parameters.
|
* `Engine:register_fn` and `Engine:register_result_fn` accepts functions that take parameters of type `&str` (immutable string slice), which maps directly to `ImmutableString`. This is to avoid needing wrappers for functions taking string parameters.
|
||||||
* Set maximum limit on data sizes: `Engine::set_max_string_size`, `Engine::set_max_array_size` and `Engine::set_max_map_size`.
|
* Set maximum limit on data sizes: `Engine::set_max_string_size`, `Engine::set_max_array_size` and `Engine::set_max_map_size`.
|
||||||
|
* Supports trailing commas on array literals, object map literals, function definitions and function calls.
|
||||||
|
|
||||||
|
|
||||||
Version 0.15.0
|
Version 0.15.0
|
||||||
|
@ -126,7 +126,7 @@ impl CallableFunction {
|
|||||||
pub fn get_native_fn(&self) -> &FnAny {
|
pub fn get_native_fn(&self) -> &FnAny {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(f) | Self::Method(f) => f.as_ref(),
|
Self::Pure(f) | Self::Method(f) => f.as_ref(),
|
||||||
Self::Iterator(_) | Self::Script(_) => panic!(),
|
Self::Iterator(_) | Self::Script(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a shared reference to a script-defined function definition.
|
/// Get a shared reference to a script-defined function definition.
|
||||||
@ -136,7 +136,7 @@ impl CallableFunction {
|
|||||||
/// Panics if the `CallableFunction` is not `Script`.
|
/// Panics if the `CallableFunction` is not `Script`.
|
||||||
pub fn get_shared_fn_def(&self) -> Shared<ScriptFnDef> {
|
pub fn get_shared_fn_def(&self) -> Shared<ScriptFnDef> {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(),
|
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(),
|
||||||
Self::Script(f) => f.clone(),
|
Self::Script(f) => f.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ impl CallableFunction {
|
|||||||
/// Panics if the `CallableFunction` is not `Script`.
|
/// Panics if the `CallableFunction` is not `Script`.
|
||||||
pub fn get_fn_def(&self) -> &ScriptFnDef {
|
pub fn get_fn_def(&self) -> &ScriptFnDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(),
|
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(),
|
||||||
Self::Script(f) => f,
|
Self::Script(f) => f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ impl CallableFunction {
|
|||||||
pub fn get_iter_fn(&self) -> IteratorFn {
|
pub fn get_iter_fn(&self) -> IteratorFn {
|
||||||
match self {
|
match self {
|
||||||
Self::Iterator(f) => *f,
|
Self::Iterator(f) => *f,
|
||||||
Self::Pure(_) | Self::Method(_) | Self::Script(_) => panic!(),
|
Self::Pure(_) | Self::Method(_) | Self::Script(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a new `CallableFunction::Pure`.
|
/// Create a new `CallableFunction::Pure`.
|
||||||
|
249
src/parser.rs
249
src/parser.rs
@ -518,7 +518,7 @@ impl Expr {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("cannot get value of non-constant expression"),
|
_ => unreachable!("cannot get value of non-constant expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +543,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Array(x) if x.0.iter().all(Self::is_constant) => "array".to_string(),
|
Self::Array(x) if x.0.iter().all(Self::is_constant) => "array".to_string(),
|
||||||
|
|
||||||
_ => panic!("cannot get value of non-constant expression"),
|
_ => unreachable!("cannot get value of non-constant expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +728,7 @@ fn eat_token(input: &mut TokenStream, token: Token) -> Position {
|
|||||||
let (t, pos) = input.next().unwrap();
|
let (t, pos) = input.next().unwrap();
|
||||||
|
|
||||||
if t != token {
|
if t != token {
|
||||||
panic!(
|
unreachable!(
|
||||||
"expecting {} (found {}) at {}",
|
"expecting {} (found {}) at {}",
|
||||||
token.syntax(),
|
token.syntax(),
|
||||||
t.syntax(),
|
t.syntax(),
|
||||||
@ -836,7 +836,11 @@ fn parse_call_expr(
|
|||||||
let settings = settings.level_up();
|
let settings = settings.level_up();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
args.push(parse_expr(input, state, settings)?);
|
match input.peek().unwrap() {
|
||||||
|
// id(...args, ) - handle trailing comma
|
||||||
|
(Token::RightParen, _) => (),
|
||||||
|
_ => args.push(parse_expr(input, state, settings)?),
|
||||||
|
}
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// id(...args)
|
// id(...args)
|
||||||
@ -1082,42 +1086,47 @@ fn parse_array_literal(
|
|||||||
|
|
||||||
let mut arr = StaticVec::new();
|
let mut arr = StaticVec::new();
|
||||||
|
|
||||||
if !match_token(input, Token::RightBracket)? {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
if state.max_array_size > 0 && arr.len() >= state.max_array_size {
|
||||||
if state.max_array_size > 0 && arr.len() >= state.max_array_size {
|
return Err(PERR::LiteralTooLarge(
|
||||||
return Err(PERR::LiteralTooLarge(
|
"Size of array literal".to_string(),
|
||||||
"Size of array literal".to_string(),
|
state.max_array_size,
|
||||||
state.max_array_size,
|
)
|
||||||
)
|
.into_err(input.peek().unwrap().1));
|
||||||
.into_err(input.peek().unwrap().1));
|
|
||||||
}
|
|
||||||
|
|
||||||
let expr = parse_expr(input, state, settings.level_up())?;
|
|
||||||
arr.push(expr);
|
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
|
||||||
(Token::Comma, _) => eat_token(input, Token::Comma),
|
|
||||||
(Token::RightBracket, _) => {
|
|
||||||
eat_token(input, Token::RightBracket);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
(Token::EOF, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::RightBracket.into(),
|
|
||||||
"to end this array literal".into(),
|
|
||||||
)
|
|
||||||
.into_err(*pos))
|
|
||||||
}
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
|
|
||||||
(_, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::Comma.into(),
|
|
||||||
"to separate the items of this array literal".into(),
|
|
||||||
)
|
|
||||||
.into_err(*pos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match input.peek().unwrap() {
|
||||||
|
(Token::RightBracket, _) => {
|
||||||
|
eat_token(input, Token::RightBracket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let expr = parse_expr(input, state, settings.level_up())?;
|
||||||
|
arr.push(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match input.peek().unwrap() {
|
||||||
|
(Token::Comma, _) => {
|
||||||
|
eat_token(input, Token::Comma);
|
||||||
|
}
|
||||||
|
(Token::RightBracket, _) => (),
|
||||||
|
(Token::EOF, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::RightBracket.into(),
|
||||||
|
"to end this array literal".into(),
|
||||||
|
)
|
||||||
|
.into_err(*pos))
|
||||||
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Comma.into(),
|
||||||
|
"to separate the items of this array literal".into(),
|
||||||
|
)
|
||||||
|
.into_err(*pos))
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Array(Box::new((arr, settings.pos))))
|
Ok(Expr::Array(Box::new((arr, settings.pos))))
|
||||||
@ -1133,77 +1142,82 @@ fn parse_map_literal(
|
|||||||
|
|
||||||
let mut map = StaticVec::new();
|
let mut map = StaticVec::new();
|
||||||
|
|
||||||
if !match_token(input, Token::RightBrace)? {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
const MISSING_RBRACE: &str = "to end this object map literal";
|
||||||
const MISSING_RBRACE: &str = "to end this object map literal";
|
|
||||||
|
|
||||||
let (name, pos) = match input.next().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Identifier(s), pos) => (s, pos),
|
(Token::RightBrace, _) => {
|
||||||
(Token::StringConst(s), pos) => (s, pos),
|
eat_token(input, Token::RightBrace);
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
break;
|
||||||
(_, pos) if map.is_empty() => {
|
|
||||||
return Err(
|
|
||||||
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
|
||||||
.into_err(pos),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
(Token::EOF, pos) => {
|
|
||||||
return Err(
|
|
||||||
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
|
||||||
.into_err(pos),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
|
||||||
};
|
|
||||||
|
|
||||||
match input.next().unwrap() {
|
|
||||||
(Token::Colon, _) => (),
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
|
||||||
(_, pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::Colon.into(),
|
|
||||||
format!(
|
|
||||||
"to follow the property '{}' in this object map literal",
|
|
||||||
name
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_err(pos))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if state.max_map_size > 0 && map.len() >= state.max_map_size {
|
|
||||||
return Err(PERR::LiteralTooLarge(
|
|
||||||
"Number of properties in object map literal".to_string(),
|
|
||||||
state.max_map_size,
|
|
||||||
)
|
|
||||||
.into_err(input.peek().unwrap().1));
|
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
let (name, pos) = match input.next().unwrap() {
|
||||||
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
|
(Token::StringConst(s), pos) => (s, pos),
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) if map.is_empty() => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::RightBrace.into(),
|
||||||
|
MISSING_RBRACE.into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
(Token::EOF, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::RightBrace.into(),
|
||||||
|
MISSING_RBRACE.into(),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
||||||
|
};
|
||||||
|
|
||||||
let expr = parse_expr(input, state, settings.level_up())?;
|
match input.next().unwrap() {
|
||||||
map.push(((name, pos), expr));
|
(Token::Colon, _) => (),
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Colon.into(),
|
||||||
|
format!(
|
||||||
|
"to follow the property '{}' in this object map literal",
|
||||||
|
name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_err(pos))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
if state.max_map_size > 0 && map.len() >= state.max_map_size {
|
||||||
(Token::Comma, _) => {
|
return Err(PERR::LiteralTooLarge(
|
||||||
eat_token(input, Token::Comma);
|
"Number of properties in object map literal".to_string(),
|
||||||
}
|
state.max_map_size,
|
||||||
(Token::RightBrace, _) => {
|
|
||||||
eat_token(input, Token::RightBrace);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
(Token::Identifier(_), pos) => {
|
|
||||||
return Err(PERR::MissingToken(
|
|
||||||
Token::Comma.into(),
|
|
||||||
"to separate the items of this object map literal".into(),
|
|
||||||
)
|
|
||||||
.into_err(*pos))
|
|
||||||
}
|
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
|
|
||||||
(_, pos) => {
|
|
||||||
return Err(
|
|
||||||
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
|
||||||
.into_err(*pos),
|
|
||||||
)
|
)
|
||||||
|
.into_err(input.peek().unwrap().1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let expr = parse_expr(input, state, settings.level_up())?;
|
||||||
|
map.push(((name, pos), expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match input.peek().unwrap() {
|
||||||
|
(Token::Comma, _) => {
|
||||||
|
eat_token(input, Token::Comma);
|
||||||
|
}
|
||||||
|
(Token::RightBrace, _) => (),
|
||||||
|
(Token::Identifier(_), pos) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Comma.into(),
|
||||||
|
"to separate the items of this object map literal".into(),
|
||||||
|
)
|
||||||
|
.into_err(*pos))
|
||||||
|
}
|
||||||
|
(Token::LexError(err), pos) => return Err(err.into_err(*pos)),
|
||||||
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
|
.into_err(*pos),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1309,7 +1323,7 @@ fn parse_primary(
|
|||||||
parse_index_chain(input, state, expr, settings.level_up())?
|
parse_index_chain(input, state, expr, settings.level_up())?
|
||||||
}
|
}
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
(expr, token) => panic!(
|
(expr, token) => unreachable!(
|
||||||
"unknown postfix operator '{}' for {:?}",
|
"unknown postfix operator '{}' for {:?}",
|
||||||
token.syntax(),
|
token.syntax(),
|
||||||
expr
|
expr
|
||||||
@ -2290,7 +2304,7 @@ fn parse_stmt(
|
|||||||
let return_type = match input.next().unwrap() {
|
let return_type = match input.next().unwrap() {
|
||||||
(Token::Return, _) => ReturnType::Return,
|
(Token::Return, _) => ReturnType::Return,
|
||||||
(Token::Throw, _) => ReturnType::Exception,
|
(Token::Throw, _) => ReturnType::Exception,
|
||||||
_ => panic!("token should be return or throw"),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
@ -2356,15 +2370,20 @@ fn parse_fn(
|
|||||||
let sep_err = format!("to separate the parameters of function '{}'", name);
|
let sep_err = format!("to separate the parameters of function '{}'", name);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match input.next().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Identifier(s), pos) => {
|
(Token::RightParen, _) => (),
|
||||||
state.push((s.clone(), ScopeEntryType::Normal));
|
_ => match input.next().unwrap() {
|
||||||
params.push((s, pos))
|
(Token::Identifier(s), pos) => {
|
||||||
}
|
state.push((s.clone(), ScopeEntryType::Normal));
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
params.push((s, pos))
|
||||||
(_, pos) => {
|
}
|
||||||
return Err(PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos))
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
}
|
(_, pos) => {
|
||||||
|
return Err(
|
||||||
|
PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
|
@ -417,7 +417,7 @@ impl<'a> Scope<'a> {
|
|||||||
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
pub fn set_value<T: Variant + Clone>(&mut self, name: &'a str, value: T) {
|
||||||
match self.get_index(name) {
|
match self.get_index(name) {
|
||||||
None => self.push(name, value),
|
None => self.push(name, value),
|
||||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
Some((_, EntryType::Constant)) => unreachable!("variable {} is constant", name),
|
||||||
Some((index, EntryType::Normal)) => {
|
Some((index, EntryType::Normal)) => {
|
||||||
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
self.0.get_mut(index).unwrap().value = Dynamic::from(value)
|
||||||
}
|
}
|
||||||
|
10
src/token.rs
10
src/token.rs
@ -294,7 +294,7 @@ impl Token {
|
|||||||
Export => "export",
|
Export => "export",
|
||||||
As => "as",
|
As => "as",
|
||||||
EOF => "{EOF}",
|
EOF => "{EOF}",
|
||||||
_ => panic!("operator should be match in outer scope"),
|
_ => unreachable!("operator should be match in outer scope"),
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
@ -548,7 +548,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
'x' => 2,
|
'x' => 2,
|
||||||
'u' => 4,
|
'u' => 4,
|
||||||
'U' => 8,
|
'U' => 8,
|
||||||
_ => panic!("should be 'x', 'u' or 'U'"),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
@ -667,14 +667,14 @@ impl<'a> TokenIterator<'a> {
|
|||||||
'0', '1', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
'0', '1', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
||||||
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
'_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_',
|
||||||
],
|
],
|
||||||
_ => panic!("unexpected character {}", ch),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
radix_base = Some(match ch {
|
radix_base = Some(match ch {
|
||||||
'x' | 'X' => 16,
|
'x' | 'X' => 16,
|
||||||
'o' | 'O' => 8,
|
'o' | 'O' => 8,
|
||||||
'b' | 'B' => 2,
|
'b' | 'B' => 2,
|
||||||
_ => panic!("unexpected character {}", ch),
|
_ => unreachable!(),
|
||||||
});
|
});
|
||||||
|
|
||||||
while let Some(next_char_in_hex) = self.peek_next() {
|
while let Some(next_char_in_hex) = self.peek_next() {
|
||||||
@ -1077,7 +1077,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
('~', _) => return Some((Token::PowerOf, pos)),
|
('~', _) => return Some((Token::PowerOf, pos)),
|
||||||
|
|
||||||
('\0', _) => panic!("should not be EOF"),
|
('\0', _) => unreachable!(),
|
||||||
|
|
||||||
(ch, _) if ch.is_whitespace() => (),
|
(ch, _) if ch.is_whitespace() => (),
|
||||||
(ch, _) => return Some((Token::LexError(Box::new(LERR::UnexpectedChar(ch))), pos)),
|
(ch, _) => return Some((Token::LexError(Box::new(LERR::UnexpectedChar(ch))), pos)),
|
||||||
|
@ -6,6 +6,7 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
|
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3]; x[1]")?, 2);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = [1, 2, 3,]; x[1]")?, 2);
|
||||||
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5);
|
assert_eq!(engine.eval::<INT>("let y = [1, 2, 3]; y[1] = 5; y[1]")?, 5);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<char>(r#"let y = [1, [ 42, 88, "93" ], 3]; y[1][2][1]"#)?,
|
engine.eval::<char>(r#"let y = [1, [ 42, 88, "93" ], 3]; y[1][2][1]"#)?,
|
||||||
|
@ -68,7 +68,7 @@ fn test_call_fn_private() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let r: INT = engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT))?;
|
let r: INT = engine.call_fn(&mut scope, &ast, "add", (40 as INT, 2 as INT))?;
|
||||||
assert_eq!(r, 42);
|
assert_eq!(r, 42);
|
||||||
|
|
||||||
let ast = engine.compile("private fn add(x, n) { x + n }")?;
|
let ast = engine.compile("private fn add(x, n, ) { x + n }")?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.call_fn::<_, INT>(&mut scope, &ast, "add", (40 as INT, 2 as INT))
|
*engine.call_fn::<_, INT>(&mut scope, &ast, "add", (40 as INT, 2 as INT))
|
||||||
@ -83,7 +83,7 @@ fn test_call_fn_private() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
fn test_anonymous_fn() -> Result<(), Box<EvalAltResult>> {
|
||||||
let calc_func = Func::<(INT, INT, INT), INT>::create_from_script(
|
let calc_func = Func::<(INT, INT, INT), INT>::create_from_script(
|
||||||
Engine::new(),
|
Engine::new(),
|
||||||
"fn calc(x, y, z) { (x + y) * z }",
|
"fn calc(x, y, z,) { (x + y) * z }",
|
||||||
"calc",
|
"calc",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn add(x, n) { x + n } add(40, 2)")?, 42);
|
assert_eq!(engine.eval::<INT>("fn add(x, n) { x + n } add(40, 2)")?, 42);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add(x, n,) { x + n } add(40, 2,)")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("fn add(x, n) { x + n } let a = 40; add(a, 2); a")?,
|
engine.eval::<INT>("fn add(x, n) { x + n } let a = 40; add(a, 2); a")?,
|
||||||
40
|
40
|
||||||
|
@ -10,6 +10,12 @@ fn test_internal_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>("fn add_me(a, b) { a+b } add_me(3, 4)")?,
|
engine.eval::<INT>("fn add_me(a, b) { a+b } add_me(3, 4)")?,
|
||||||
7
|
7
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add_me(a, b,) { a+b } add_me(3, 4,)")?,
|
||||||
|
7
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
|
assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -12,6 +12,10 @@ fn test_map_indexing() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(r#"let x = #{a: 1, b: 2, c: 3}; x["b"]"#)?,
|
engine.eval::<INT>(r#"let x = #{a: 1, b: 2, c: 3}; x["b"]"#)?,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r#"let x = #{a: 1, b: 2, c: 3,}; x["b"]"#)?,
|
||||||
|
2
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<char>(
|
engine.eval::<char>(
|
||||||
r#"
|
r#"
|
||||||
|
Loading…
Reference in New Issue
Block a user