Merge branch 'master' into plugins
This commit is contained in:
commit
a5d8ce2e49
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.15.0"
|
version = "0.16.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
@ -37,7 +37,7 @@ Features
|
|||||||
to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are
|
to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are
|
||||||
pulled in to provide for functionalities that used to be in `std`.
|
pulled in to provide for functionalities that used to be in `std`.
|
||||||
|
|
||||||
**Note:** Currently, the version is `0.15.0`, so the language and API's may change before they stabilize.
|
**Note:** Currently, the version is `0.16.0`, so the language and API's may change before they stabilize.
|
||||||
|
|
||||||
What Rhai doesn't do
|
What Rhai doesn't do
|
||||||
--------------------
|
--------------------
|
||||||
@ -71,7 +71,7 @@ Install the Rhai crate on [`crates.io`](https::/crates.io/crates/rhai/) by addin
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rhai = "0.15.0"
|
rhai = "0.16.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the latest released crate version on [`crates.io`](https::/crates.io/crates/rhai/):
|
Use the latest released crate version on [`crates.io`](https::/crates.io/crates/rhai/):
|
||||||
@ -427,7 +427,7 @@ are supported.
|
|||||||
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` |
|
| `+`, | `+=` | `INT`, `FLOAT` (if not [`no_float`]), `ImmutableString` |
|
||||||
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
| `-`, `*`, `/`, `%`, `~`, | `-=`, `*=`, `/=`, `%=`, `~=` | `INT`, `FLOAT` (if not [`no_float`]) |
|
||||||
| `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` |
|
| `<<`, `>>`, `^`, | `<<=`, `>>=`, `^=` | `INT` |
|
||||||
| `&`, `\|`, | `&=`, `|=` | `INT`, `bool` |
|
| `&`, `\|`, | `&=`, `\|=` | `INT`, `bool` |
|
||||||
| `&&`, `\|\|` | | `bool` |
|
| `&&`, `\|\|` | | `bool` |
|
||||||
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
|
| `==`, `!=` | | `INT`, `FLOAT` (if not [`no_float`]), `bool`, `char`, `()`, `ImmutableString` |
|
||||||
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |
|
| `>`, `>=`, `<`, `<=` | | `INT`, `FLOAT` (if not [`no_float`]), `char`, `()`, `ImmutableString` |
|
||||||
@ -496,7 +496,7 @@ In these cases, use the `compile_expression` and `eval_expression` methods or th
|
|||||||
let result = engine.eval_expression::<i64>("2 + (10 + 10) * 2")?;
|
let result = engine.eval_expression::<i64>("2 + (10 + 10) * 2")?;
|
||||||
```
|
```
|
||||||
|
|
||||||
When evaluation _expressions_, no full-blown statement (e.g. `if`, `while`, `for`) - not even variable assignments -
|
When evaluating _expressions_, no full-blown statement (e.g. `if`, `while`, `for`) - not even variable assignments -
|
||||||
is supported and will be considered parse errors when encountered.
|
is supported and will be considered parse errors when encountered.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Version 0.14.2
|
Version 0.16.0
|
||||||
|
==============
|
||||||
|
|
||||||
|
|
||||||
|
Version 0.15.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Regression fix
|
Regression fix
|
||||||
|
205
src/parser.rs
205
src/parser.rs
@ -622,28 +622,33 @@ impl Expr {
|
|||||||
| Self::Or(_)
|
| Self::Or(_)
|
||||||
| Self::True(_)
|
| Self::True(_)
|
||||||
| Self::False(_)
|
| Self::False(_)
|
||||||
| Self::Unit(_) => false,
|
| Self::Unit(_)
|
||||||
|
| Self::Assignment(_) => false,
|
||||||
|
|
||||||
Self::StringConstant(_)
|
Self::StringConstant(_)
|
||||||
| Self::Stmt(_)
|
| Self::Stmt(_)
|
||||||
| Self::FnCall(_)
|
| Self::FnCall(_)
|
||||||
| Self::Assignment(_)
|
|
||||||
| Self::Dot(_)
|
| Self::Dot(_)
|
||||||
| Self::Index(_)
|
| Self::Index(_)
|
||||||
| Self::Array(_)
|
| Self::Array(_)
|
||||||
| Self::Map(_) => match token {
|
| Self::Map(_) => match token {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
Token::LeftBracket => true,
|
Token::LeftBracket => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Variable(_) => match token {
|
Self::Variable(_) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Token::LeftBracket => true,
|
||||||
|
Token::LeftParen => true,
|
||||||
Token::DoubleColon => true,
|
Token::DoubleColon => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Property(_) => match token {
|
Self::Property(_) => match token {
|
||||||
Token::LeftBracket | Token::LeftParen => true,
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Token::LeftBracket => true,
|
||||||
|
Token::LeftParen => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -695,7 +700,8 @@ fn parse_paren_expr<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if level > state.max_expr_depth {
|
if level > state.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(pos));
|
return Err(PERR::ExprTooDeep.into_err(pos));
|
||||||
@ -705,7 +711,7 @@ fn parse_paren_expr<'a>(
|
|||||||
return Ok(Expr::Unit(pos));
|
return Ok(Expr::Unit(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
// ( xxx )
|
// ( xxx )
|
||||||
@ -729,7 +735,8 @@ fn parse_call_expr<'a>(
|
|||||||
mut modules: Option<Box<ModuleRef>>,
|
mut modules: Option<Box<ModuleRef>>,
|
||||||
begin: Position,
|
begin: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, pos) = input.peek().unwrap();
|
let (token, pos) = input.peek().unwrap();
|
||||||
|
|
||||||
@ -783,7 +790,7 @@ fn parse_call_expr<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
args.push(parse_expr(input, state, level + 1, allow_stmt_expr)?);
|
args.push(parse_expr(input, state, level + 1, if_expr, stmt_expr)?);
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
// id(...args)
|
// id(...args)
|
||||||
@ -850,13 +857,14 @@ fn parse_index_chain<'a>(
|
|||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if level > state.max_expr_depth {
|
if level > state.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(pos));
|
return Err(PERR::ExprTooDeep.into_err(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx_expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let idx_expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
// Check type of indexing - must be integer or string
|
// Check type of indexing - must be integer or string
|
||||||
match &idx_expr {
|
match &idx_expr {
|
||||||
@ -1003,7 +1011,8 @@ fn parse_index_chain<'a>(
|
|||||||
idx_expr,
|
idx_expr,
|
||||||
idx_pos,
|
idx_pos,
|
||||||
level + 1,
|
level + 1,
|
||||||
allow_stmt_expr,
|
if_expr,
|
||||||
|
stmt_expr,
|
||||||
)?;
|
)?;
|
||||||
// Indexing binds to right
|
// Indexing binds to right
|
||||||
Ok(Expr::Index(Box::new((lhs, idx_expr, pos))))
|
Ok(Expr::Index(Box::new((lhs, idx_expr, pos))))
|
||||||
@ -1037,7 +1046,8 @@ fn parse_array_literal<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if level > state.max_expr_depth {
|
if level > state.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(pos));
|
return Err(PERR::ExprTooDeep.into_err(pos));
|
||||||
@ -1047,7 +1057,8 @@ fn parse_array_literal<'a>(
|
|||||||
|
|
||||||
if !match_token(input, Token::RightBracket)? {
|
if !match_token(input, Token::RightBracket)? {
|
||||||
while !input.peek().unwrap().0.is_eof() {
|
while !input.peek().unwrap().0.is_eof() {
|
||||||
arr.push(parse_expr(input, state, level + 1, allow_stmt_expr)?);
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
arr.push(expr);
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Comma, _) => eat_token(input, Token::Comma),
|
(Token::Comma, _) => eat_token(input, Token::Comma),
|
||||||
@ -1085,7 +1096,8 @@ fn parse_map_literal<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if level > state.max_expr_depth {
|
if level > state.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(pos));
|
return Err(PERR::ExprTooDeep.into_err(pos));
|
||||||
@ -1135,7 +1147,7 @@ fn parse_map_literal<'a>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
map.push(((name, pos), expr));
|
map.push(((name, pos), expr));
|
||||||
|
|
||||||
@ -1186,7 +1198,8 @@ fn parse_primary<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, pos) = input.peek().unwrap();
|
let (token, pos) = input.peek().unwrap();
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
@ -1197,8 +1210,8 @@ fn parse_primary<'a>(
|
|||||||
|
|
||||||
let (token, _) = match token {
|
let (token, _) = match token {
|
||||||
// { - block statement as expression
|
// { - block statement as expression
|
||||||
Token::LeftBrace if allow_stmt_expr => {
|
Token::LeftBrace if stmt_expr => {
|
||||||
return parse_block(input, state, false, level + 1, allow_stmt_expr)
|
return parse_block(input, state, false, level + 1, if_expr, stmt_expr)
|
||||||
.map(|block| Expr::Stmt(Box::new((block, pos))));
|
.map(|block| Expr::Stmt(Box::new((block, pos))));
|
||||||
}
|
}
|
||||||
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(pos)),
|
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(pos)),
|
||||||
@ -1215,11 +1228,13 @@ fn parse_primary<'a>(
|
|||||||
let index = state.find(&s);
|
let index = state.find(&s);
|
||||||
Expr::Variable(Box::new(((s, pos), None, 0, index)))
|
Expr::Variable(Box::new(((s, pos), None, 0, index)))
|
||||||
}
|
}
|
||||||
Token::LeftParen => parse_paren_expr(input, state, pos, level + 1, allow_stmt_expr)?,
|
Token::LeftParen => parse_paren_expr(input, state, pos, level + 1, if_expr, stmt_expr)?,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Token::LeftBracket => parse_array_literal(input, state, pos, level + 1, allow_stmt_expr)?,
|
Token::LeftBracket => {
|
||||||
|
parse_array_literal(input, state, pos, level + 1, if_expr, stmt_expr)?
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::MapStart => parse_map_literal(input, state, pos, level + 1, allow_stmt_expr)?,
|
Token::MapStart => parse_map_literal(input, state, pos, level + 1, if_expr, stmt_expr)?,
|
||||||
Token::True => Expr::True(pos),
|
Token::True => Expr::True(pos),
|
||||||
Token::False => Expr::False(pos),
|
Token::False => Expr::False(pos),
|
||||||
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(pos)),
|
||||||
@ -1242,7 +1257,16 @@ fn parse_primary<'a>(
|
|||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(x), Token::LeftParen) => {
|
(Expr::Variable(x), Token::LeftParen) => {
|
||||||
let ((name, pos), modules, _, _) = *x;
|
let ((name, pos), modules, _, _) = *x;
|
||||||
parse_call_expr(input, state, name, modules, pos, level + 1, allow_stmt_expr)?
|
parse_call_expr(
|
||||||
|
input,
|
||||||
|
state,
|
||||||
|
name,
|
||||||
|
modules,
|
||||||
|
pos,
|
||||||
|
level + 1,
|
||||||
|
if_expr,
|
||||||
|
stmt_expr,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
(Expr::Property(_), _) => unreachable!(),
|
(Expr::Property(_), _) => unreachable!(),
|
||||||
// module access
|
// module access
|
||||||
@ -1264,10 +1288,14 @@ fn parse_primary<'a>(
|
|||||||
// Indexing
|
// Indexing
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
(expr, Token::LeftBracket) => {
|
(expr, Token::LeftBracket) => {
|
||||||
parse_index_chain(input, state, expr, token_pos, level + 1, allow_stmt_expr)?
|
parse_index_chain(input, state, expr, token_pos, level + 1, if_expr, stmt_expr)?
|
||||||
}
|
}
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
(expr, token) => panic!("unknown postfix operator {:?} for {:?}", token, expr),
|
(expr, token) => panic!(
|
||||||
|
"unknown postfix operator '{}' for {:?}",
|
||||||
|
token.syntax(),
|
||||||
|
expr
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1292,7 +1320,8 @@ fn parse_unary<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, pos) = input.peek().unwrap();
|
let (token, pos) = input.peek().unwrap();
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
@ -1303,15 +1332,15 @@ fn parse_unary<'a>(
|
|||||||
|
|
||||||
match token {
|
match token {
|
||||||
// If statement is allowed to act as expressions
|
// If statement is allowed to act as expressions
|
||||||
Token::If => Ok(Expr::Stmt(Box::new((
|
Token::If if if_expr => Ok(Expr::Stmt(Box::new((
|
||||||
parse_if(input, state, false, level + 1, allow_stmt_expr)?,
|
parse_if(input, state, false, level + 1, if_expr, stmt_expr)?,
|
||||||
pos,
|
pos,
|
||||||
)))),
|
)))),
|
||||||
// -expr
|
// -expr
|
||||||
Token::UnaryMinus => {
|
Token::UnaryMinus => {
|
||||||
let pos = eat_token(input, Token::UnaryMinus);
|
let pos = eat_token(input, Token::UnaryMinus);
|
||||||
|
|
||||||
match parse_unary(input, state, level + 1, allow_stmt_expr)? {
|
match parse_unary(input, state, level + 1, if_expr, stmt_expr)? {
|
||||||
// Negative integer
|
// Negative integer
|
||||||
Expr::IntegerConstant(x) => {
|
Expr::IntegerConstant(x) => {
|
||||||
let (num, pos) = *x;
|
let (num, pos) = *x;
|
||||||
@ -1360,13 +1389,14 @@ fn parse_unary<'a>(
|
|||||||
// +expr
|
// +expr
|
||||||
Token::UnaryPlus => {
|
Token::UnaryPlus => {
|
||||||
eat_token(input, Token::UnaryPlus);
|
eat_token(input, Token::UnaryPlus);
|
||||||
parse_unary(input, state, level + 1, allow_stmt_expr)
|
parse_unary(input, state, level + 1, if_expr, stmt_expr)
|
||||||
}
|
}
|
||||||
// !expr
|
// !expr
|
||||||
Token::Bang => {
|
Token::Bang => {
|
||||||
let pos = eat_token(input, Token::Bang);
|
let pos = eat_token(input, Token::Bang);
|
||||||
let mut args = StaticVec::new();
|
let mut args = StaticVec::new();
|
||||||
args.push(parse_primary(input, state, level + 1, allow_stmt_expr)?);
|
let expr = parse_primary(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
args.push(expr);
|
||||||
|
|
||||||
let op = "!";
|
let op = "!";
|
||||||
let hash = calc_fn_hash(empty(), op, 2, empty());
|
let hash = calc_fn_hash(empty(), op, 2, empty());
|
||||||
@ -1382,7 +1412,7 @@ fn parse_unary<'a>(
|
|||||||
// <EOF>
|
// <EOF>
|
||||||
Token::EOF => Err(PERR::UnexpectedEOF.into_err(pos)),
|
Token::EOF => Err(PERR::UnexpectedEOF.into_err(pos)),
|
||||||
// All other tokens
|
// All other tokens
|
||||||
_ => parse_primary(input, state, level + 1, allow_stmt_expr),
|
_ => parse_primary(input, state, level + 1, if_expr, stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1442,7 +1472,8 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (token, pos) = input.peek().unwrap();
|
let (token, pos) = input.peek().unwrap();
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
@ -1470,7 +1501,7 @@ fn parse_op_assignment_stmt<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (_, pos) = input.next().unwrap();
|
let (_, pos) = input.next().unwrap();
|
||||||
let rhs = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let rhs = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
make_assignment_stmt(op, state, lhs, rhs, pos)
|
make_assignment_stmt(op, state, lhs, rhs, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1685,7 +1716,8 @@ fn parse_binary_op<'a>(
|
|||||||
parent_precedence: u8,
|
parent_precedence: u8,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
mut level: usize,
|
mut level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
if level > state.max_expr_depth {
|
if level > state.max_expr_depth {
|
||||||
return Err(PERR::ExprTooDeep.into_err(lhs.position()));
|
return Err(PERR::ExprTooDeep.into_err(lhs.position()));
|
||||||
@ -1706,14 +1738,14 @@ fn parse_binary_op<'a>(
|
|||||||
|
|
||||||
let (op_token, pos) = input.next().unwrap();
|
let (op_token, pos) = input.next().unwrap();
|
||||||
|
|
||||||
let rhs = parse_unary(input, state, level, allow_stmt_expr)?;
|
let rhs = parse_unary(input, state, level, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
let next_precedence = input.peek().unwrap().0.precedence();
|
let next_precedence = input.peek().unwrap().0.precedence();
|
||||||
|
|
||||||
// Bind to right if the next operator has higher precedence
|
// Bind to right if the next operator has higher precedence
|
||||||
// If same precedence, then check if the operator binds right
|
// If same precedence, then check if the operator binds right
|
||||||
let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence {
|
let rhs = if (precedence == next_precedence && bind_right) || precedence < next_precedence {
|
||||||
parse_binary_op(input, state, precedence, rhs, level, allow_stmt_expr)?
|
parse_binary_op(input, state, precedence, rhs, level, if_expr, stmt_expr)?
|
||||||
} else {
|
} else {
|
||||||
// Otherwise bind to left (even if next operator has the same precedence)
|
// Otherwise bind to left (even if next operator has the same precedence)
|
||||||
rhs
|
rhs
|
||||||
@ -1801,7 +1833,8 @@ fn parse_expr<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
let (_, pos) = input.peek().unwrap();
|
let (_, pos) = input.peek().unwrap();
|
||||||
|
|
||||||
@ -1809,8 +1842,8 @@ fn parse_expr<'a>(
|
|||||||
return Err(PERR::ExprTooDeep.into_err(*pos));
|
return Err(PERR::ExprTooDeep.into_err(*pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let lhs = parse_unary(input, state, level + 1, allow_stmt_expr)?;
|
let lhs = parse_unary(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
parse_binary_op(input, state, 1, lhs, level + 1, allow_stmt_expr)
|
parse_binary_op(input, state, 1, lhs, level + 1, if_expr, stmt_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
|
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
|
||||||
@ -1861,7 +1894,8 @@ fn parse_if<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// if ...
|
// if ...
|
||||||
let pos = eat_token(input, Token::If);
|
let pos = eat_token(input, Token::If);
|
||||||
@ -1872,18 +1906,18 @@ fn parse_if<'a>(
|
|||||||
|
|
||||||
// if guard { if_body }
|
// if guard { if_body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let guard = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let if_body = parse_block(input, state, breakable, level + 1, allow_stmt_expr)?;
|
let if_body = parse_block(input, state, breakable, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
// if guard { if_body } else ...
|
// if guard { if_body } else ...
|
||||||
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
let else_body = if match_token(input, Token::Else).unwrap_or(false) {
|
||||||
Some(if let (Token::If, _) = input.peek().unwrap() {
|
Some(if let (Token::If, _) = input.peek().unwrap() {
|
||||||
// if guard { if_body } else if ...
|
// if guard { if_body } else if ...
|
||||||
parse_if(input, state, breakable, level + 1, allow_stmt_expr)?
|
parse_if(input, state, breakable, level + 1, if_expr, stmt_expr)?
|
||||||
} else {
|
} else {
|
||||||
// if guard { if_body } else { else-body }
|
// if guard { if_body } else { else-body }
|
||||||
parse_block(input, state, breakable, level + 1, allow_stmt_expr)?
|
parse_block(input, state, breakable, level + 1, if_expr, stmt_expr)?
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -1897,7 +1931,8 @@ fn parse_while<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// while ...
|
// while ...
|
||||||
let pos = eat_token(input, Token::While);
|
let pos = eat_token(input, Token::While);
|
||||||
@ -1908,9 +1943,9 @@ fn parse_while<'a>(
|
|||||||
|
|
||||||
// while guard { body }
|
// while guard { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let guard = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let guard = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
ensure_not_assignment(input)?;
|
ensure_not_assignment(input)?;
|
||||||
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
|
let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
Ok(Stmt::While(Box::new((guard, body))))
|
Ok(Stmt::While(Box::new((guard, body))))
|
||||||
}
|
}
|
||||||
@ -1920,7 +1955,8 @@ fn parse_loop<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// loop ...
|
// loop ...
|
||||||
let pos = eat_token(input, Token::Loop);
|
let pos = eat_token(input, Token::Loop);
|
||||||
@ -1930,7 +1966,7 @@ fn parse_loop<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loop { body }
|
// loop { body }
|
||||||
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
|
let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
Ok(Stmt::Loop(Box::new(body)))
|
Ok(Stmt::Loop(Box::new(body)))
|
||||||
}
|
}
|
||||||
@ -1940,7 +1976,8 @@ fn parse_for<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// for ...
|
// for ...
|
||||||
let pos = eat_token(input, Token::For);
|
let pos = eat_token(input, Token::For);
|
||||||
@ -1975,12 +2012,12 @@ fn parse_for<'a>(
|
|||||||
|
|
||||||
// for name in expr { body }
|
// for name in expr { body }
|
||||||
ensure_not_statement_expr(input, "a boolean")?;
|
ensure_not_statement_expr(input, "a boolean")?;
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
let prev_len = state.len();
|
let prev_len = state.len();
|
||||||
state.push((name.clone(), ScopeEntryType::Normal));
|
state.push((name.clone(), ScopeEntryType::Normal));
|
||||||
|
|
||||||
let body = parse_block(input, state, true, level + 1, allow_stmt_expr)?;
|
let body = parse_block(input, state, true, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
state.truncate(prev_len);
|
state.truncate(prev_len);
|
||||||
|
|
||||||
@ -1993,7 +2030,8 @@ fn parse_let<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
var_type: ScopeEntryType,
|
var_type: ScopeEntryType,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// let/const... (specified in `var_type`)
|
// let/const... (specified in `var_type`)
|
||||||
let (_, pos) = input.next().unwrap();
|
let (_, pos) = input.next().unwrap();
|
||||||
@ -2012,7 +2050,7 @@ fn parse_let<'a>(
|
|||||||
// let name = ...
|
// let name = ...
|
||||||
if match_token(input, Token::Equals)? {
|
if match_token(input, Token::Equals)? {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
let init_value = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let init_value = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
@ -2054,7 +2092,8 @@ fn parse_import<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// import ...
|
// import ...
|
||||||
let pos = eat_token(input, Token::Import);
|
let pos = eat_token(input, Token::Import);
|
||||||
@ -2064,7 +2103,7 @@ fn parse_import<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// import expr ...
|
// import expr ...
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
|
|
||||||
// import expr as ...
|
// import expr as ...
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
@ -2160,7 +2199,8 @@ fn parse_block<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
breakable: bool,
|
breakable: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
// Must start with {
|
// Must start with {
|
||||||
let pos = match input.next().unwrap() {
|
let pos = match input.next().unwrap() {
|
||||||
@ -2184,7 +2224,15 @@ fn parse_block<'a>(
|
|||||||
|
|
||||||
while !match_token(input, Token::RightBrace)? {
|
while !match_token(input, Token::RightBrace)? {
|
||||||
// Parse statements inside the block
|
// Parse statements inside the block
|
||||||
let stmt = parse_stmt(input, state, breakable, false, level + 1, allow_stmt_expr)?;
|
let stmt = parse_stmt(
|
||||||
|
input,
|
||||||
|
state,
|
||||||
|
breakable,
|
||||||
|
false,
|
||||||
|
level + 1,
|
||||||
|
if_expr,
|
||||||
|
stmt_expr,
|
||||||
|
)?;
|
||||||
|
|
||||||
// See if it needs a terminating semicolon
|
// See if it needs a terminating semicolon
|
||||||
let need_semicolon = !stmt.is_self_terminated();
|
let need_semicolon = !stmt.is_self_terminated();
|
||||||
@ -2231,7 +2279,8 @@ fn parse_expr_stmt<'a>(
|
|||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
let (_, pos) = input.peek().unwrap();
|
let (_, pos) = input.peek().unwrap();
|
||||||
|
|
||||||
@ -2239,8 +2288,8 @@ fn parse_expr_stmt<'a>(
|
|||||||
return Err(PERR::ExprTooDeep.into_err(*pos));
|
return Err(PERR::ExprTooDeep.into_err(*pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
let expr = parse_op_assignment_stmt(input, state, expr, level + 1, allow_stmt_expr)?;
|
let expr = parse_op_assignment_stmt(input, state, expr, level + 1, if_expr, stmt_expr)?;
|
||||||
Ok(Stmt::Expr(Box::new(expr)))
|
Ok(Stmt::Expr(Box::new(expr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2251,7 +2300,8 @@ fn parse_stmt<'a>(
|
|||||||
breakable: bool,
|
breakable: bool,
|
||||||
is_global: bool,
|
is_global: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
use ScopeEntryType::{Constant, Normal};
|
use ScopeEntryType::{Constant, Normal};
|
||||||
|
|
||||||
@ -2268,7 +2318,7 @@ fn parse_stmt<'a>(
|
|||||||
// Semicolon - empty statement
|
// Semicolon - empty statement
|
||||||
Token::SemiColon => Ok(Stmt::Noop(*pos)),
|
Token::SemiColon => Ok(Stmt::Noop(*pos)),
|
||||||
|
|
||||||
Token::LeftBrace => parse_block(input, state, breakable, level + 1, allow_stmt_expr),
|
Token::LeftBrace => parse_block(input, state, breakable, level + 1, if_expr, stmt_expr),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -2276,10 +2326,10 @@ fn parse_stmt<'a>(
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Fn => unreachable!(),
|
Token::Fn => unreachable!(),
|
||||||
|
|
||||||
Token::If => parse_if(input, state, breakable, level + 1, allow_stmt_expr),
|
Token::If => parse_if(input, state, breakable, level + 1, if_expr, stmt_expr),
|
||||||
Token::While => parse_while(input, state, level + 1, allow_stmt_expr),
|
Token::While => parse_while(input, state, level + 1, if_expr, stmt_expr),
|
||||||
Token::Loop => parse_loop(input, state, level + 1, allow_stmt_expr),
|
Token::Loop => parse_loop(input, state, level + 1, if_expr, stmt_expr),
|
||||||
Token::For => parse_for(input, state, level + 1, allow_stmt_expr),
|
Token::For => parse_for(input, state, level + 1, if_expr, stmt_expr),
|
||||||
|
|
||||||
Token::Continue if breakable => {
|
Token::Continue if breakable => {
|
||||||
let pos = eat_token(input, Token::Continue);
|
let pos = eat_token(input, Token::Continue);
|
||||||
@ -2309,7 +2359,7 @@ fn parse_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// `return` or `throw` with expression
|
// `return` or `throw` with expression
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
let expr = parse_expr(input, state, level + 1, allow_stmt_expr)?;
|
let expr = parse_expr(input, state, level + 1, if_expr, stmt_expr)?;
|
||||||
let pos = expr.position();
|
let pos = expr.position();
|
||||||
|
|
||||||
Ok(Stmt::ReturnWithVal(Box::new((
|
Ok(Stmt::ReturnWithVal(Box::new((
|
||||||
@ -2320,9 +2370,9 @@ fn parse_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Token::Let => parse_let(input, state, Normal, level + 1, allow_stmt_expr),
|
Token::Let => parse_let(input, state, Normal, level + 1, if_expr, stmt_expr),
|
||||||
Token::Const => parse_let(input, state, Constant, level + 1, allow_stmt_expr),
|
Token::Const => parse_let(input, state, Constant, level + 1, if_expr, stmt_expr),
|
||||||
Token::Import => parse_import(input, state, level + 1, allow_stmt_expr),
|
Token::Import => parse_import(input, state, level + 1, if_expr, stmt_expr),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Token::Export if !is_global => Err(PERR::WrongExport.into_err(*pos)),
|
Token::Export if !is_global => Err(PERR::WrongExport.into_err(*pos)),
|
||||||
@ -2330,7 +2380,7 @@ fn parse_stmt<'a>(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Token::Export => parse_export(input, state, level + 1),
|
Token::Export => parse_export(input, state, level + 1),
|
||||||
|
|
||||||
_ => parse_expr_stmt(input, state, level + 1, allow_stmt_expr),
|
_ => parse_expr_stmt(input, state, level + 1, if_expr, stmt_expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2341,7 +2391,8 @@ fn parse_fn<'a>(
|
|||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
access: FnAccess,
|
access: FnAccess,
|
||||||
level: usize,
|
level: usize,
|
||||||
allow_stmt_expr: bool,
|
if_expr: bool,
|
||||||
|
stmt_expr: bool,
|
||||||
) -> Result<FnDef, ParseError> {
|
) -> Result<FnDef, ParseError> {
|
||||||
let pos = eat_token(input, Token::Fn);
|
let pos = eat_token(input, Token::Fn);
|
||||||
|
|
||||||
@ -2412,7 +2463,7 @@ fn parse_fn<'a>(
|
|||||||
|
|
||||||
// Parse function body
|
// Parse function body
|
||||||
let body = match input.peek().unwrap() {
|
let body = match input.peek().unwrap() {
|
||||||
(Token::LeftBrace, _) => parse_block(input, state, false, level + 1, allow_stmt_expr)?,
|
(Token::LeftBrace, _) => parse_block(input, state, false, level + 1, if_expr, stmt_expr)?,
|
||||||
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
(_, pos) => return Err(PERR::FnMissingBody(name).into_err(*pos)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2435,7 +2486,7 @@ pub fn parse_global_expr<'a>(
|
|||||||
max_expr_depth: usize,
|
max_expr_depth: usize,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let mut state = ParseState::new(max_expr_depth);
|
let mut state = ParseState::new(max_expr_depth);
|
||||||
let expr = parse_expr(input, &mut state, 0, false)?;
|
let expr = parse_expr(input, &mut state, 0, false, false)?;
|
||||||
|
|
||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::EOF, _) => (),
|
(Token::EOF, _) => (),
|
||||||
@ -2480,7 +2531,7 @@ fn parse_global_level<'a>(
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
(Token::Fn, _) => {
|
(Token::Fn, _) => {
|
||||||
let mut state = ParseState::new(max_expr_depth.1);
|
let mut state = ParseState::new(max_expr_depth.1);
|
||||||
let func = parse_fn(input, &mut state, access, 0, true)?;
|
let func = parse_fn(input, &mut state, access, 0, true, true)?;
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
// Qualifiers (none) + function name + number of arguments.
|
||||||
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
|
let hash = calc_fn_hash(empty(), &func.name, func.params.len(), empty());
|
||||||
@ -2499,7 +2550,7 @@ fn parse_global_level<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Actual statement
|
// Actual statement
|
||||||
let stmt = parse_stmt(input, &mut state, false, true, 0, true)?;
|
let stmt = parse_stmt(input, &mut state, false, true, 0, true, true)?;
|
||||||
|
|
||||||
let need_semicolon = !stmt.is_self_terminated();
|
let need_semicolon = !stmt.is_self_terminated();
|
||||||
|
|
||||||
|
@ -12,10 +12,9 @@ fn test_expressions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval_expression_with_scope::<INT>(&mut scope, "2 + (x + 10) * 2")?,
|
engine.eval_expression_with_scope::<INT>(&mut scope, "2 + (x + 10) * 2")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(engine
|
||||||
engine.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }")?,
|
.eval_expression_with_scope::<INT>(&mut scope, "if x > 0 { 42 } else { 123 }")
|
||||||
42
|
.is_err());
|
||||||
);
|
|
||||||
|
|
||||||
assert!(engine.eval_expression::<()>("40 + 2;").is_err());
|
assert!(engine.eval_expression::<()>("40 + 2;").is_err());
|
||||||
assert!(engine.eval_expression::<()>("40 + { 2 }").is_err());
|
assert!(engine.eval_expression::<()>("40 + { 2 }").is_err());
|
||||||
|
@ -23,9 +23,24 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<String>(r#""foo" + 123"#)?, "foo123");
|
assert_eq!(engine.eval::<String>(r#""foo" + 123"#)?, "foo123");
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert_eq!(engine.eval::<String>("(42).to_string()")?, "42");
|
assert_eq!(engine.eval::<String>("to_string(42)")?, "42");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
assert_eq!(engine.eval::<char>(r#"let y = "hello"; y[1]"#)?, 'e');
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"let y = "hello"; y.len"#)?, 5);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r#"let y = "hello"; y.clear(); y.len"#)?,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"let y = "hello"; len(y)"#)?, 5);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(engine.eval::<char>(r#"let y = "hello"; y[y.len-1]"#)?, 'o');
|
assert_eq!(engine.eval::<char>(r#"let y = "hello"; y[y.len-1]"#)?, 'o');
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Loading…
Reference in New Issue
Block a user