Enable more indexing expressions.
This commit is contained in:
parent
c9395049e2
commit
883f08c026
30
README.md
30
README.md
@ -11,7 +11,7 @@ Rhai's current feature set:
|
|||||||
* Support for overloaded functions
|
* Support for overloaded functions
|
||||||
* No additional dependencies
|
* No additional dependencies
|
||||||
|
|
||||||
**Note:** Currently, the version is 0.10.1, so the language and API may change before they stabilize.
|
**Note:** Currently, the version is 0.10.1, so the language and API's may change before they stabilize.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -585,7 +585,7 @@ return 123 + 456;
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
if error != "" {
|
if error != "" {
|
||||||
throw error; // `throw` takes a string to form the exception text
|
throw error; // 'throw' takes a string to form the exception text
|
||||||
}
|
}
|
||||||
|
|
||||||
throw; // no exception text
|
throw; // no exception text
|
||||||
@ -628,12 +628,19 @@ y[1] = 42;
|
|||||||
print(y[1]); // prints 42
|
print(y[1]); // prints 42
|
||||||
|
|
||||||
ts.list = y; // arrays can be assigned completely (by value copy)
|
ts.list = y; // arrays can be assigned completely (by value copy)
|
||||||
let foo = ts.list[1]; // indexing into properties is ok
|
let foo = ts.list[1];
|
||||||
foo == 42;
|
foo == 42;
|
||||||
|
|
||||||
let foo = [1, 2, 3][0]; // a syntax error (for now) - cannot index into literals
|
let foo = [1, 2, 3][0];
|
||||||
let foo = abc()[0]; // a syntax error (for now) - cannot index into function call return values
|
foo == 1;
|
||||||
let foo = y[0]; // this works
|
|
||||||
|
fn abc() { [42, 43, 44] }
|
||||||
|
|
||||||
|
let foo = abc()[0];
|
||||||
|
foo == 42;
|
||||||
|
|
||||||
|
let foo = y[0];
|
||||||
|
foo == 1;
|
||||||
|
|
||||||
y.push(4); // 4 elements
|
y.push(4); // 4 elements
|
||||||
y.push(5); // 5 elements
|
y.push(5); // 5 elements
|
||||||
@ -734,14 +741,19 @@ let c = record[4];
|
|||||||
c == 'C';
|
c == 'C';
|
||||||
|
|
||||||
ts.s = record;
|
ts.s = record;
|
||||||
let c = ts.s[4]; // indexing into properties is ok
|
|
||||||
|
let c = ts.s[4];
|
||||||
c == 'C';
|
c == 'C';
|
||||||
|
|
||||||
let c = "foo"[0]; // a syntax error (for now) - cannot index into literals
|
let c = "foo"[0];
|
||||||
|
c == 'f';
|
||||||
|
|
||||||
|
let c = ("foo" + "bar")[5];
|
||||||
|
c == 'r';
|
||||||
|
|
||||||
// Escape sequences in strings
|
// Escape sequences in strings
|
||||||
record += " \u2764\n"; // escape sequence of '❤' in Unicode
|
record += " \u2764\n"; // escape sequence of '❤' in Unicode
|
||||||
record == "Bob C. Davis: age 42 ❤\n"; // '\n' = new-line
|
record == "Bob C. Davis: age 42 ❤\n"; // '\n' = new-line
|
||||||
|
|
||||||
// Unlike Rust, Rhai strings can be modified
|
// Unlike Rust, Rhai strings can be modified
|
||||||
record[4] = '\x58'; // 0x58 = 'X'
|
record[4] = '\x58'; // 0x58 = 'X'
|
||||||
|
243
src/engine.rs
243
src/engine.rs
@ -18,6 +18,13 @@ const KEYWORD_PRINT: &'static str = "print";
|
|||||||
const KEYWORD_DEBUG: &'static str = "debug";
|
const KEYWORD_DEBUG: &'static str = "debug";
|
||||||
const KEYWORD_TYPE_OF: &'static str = "type_of";
|
const KEYWORD_TYPE_OF: &'static str = "type_of";
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
|
enum VariableType {
|
||||||
|
Array,
|
||||||
|
String,
|
||||||
|
Expression,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct FnSpec<'a> {
|
pub struct FnSpec<'a> {
|
||||||
pub name: Cow<'a, str>,
|
pub name: Cow<'a, str>,
|
||||||
@ -193,15 +200,24 @@ impl Engine<'_> {
|
|||||||
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Index(id, idx_expr, pos) => {
|
Expr::Index(lhs, idx_expr) => {
|
||||||
let idx = *self
|
let idx = *self
|
||||||
.eval_expr(scope, idx_expr)?
|
.eval_expr(scope, idx_expr)?
|
||||||
.downcast::<i64>()
|
.downcast::<i64>()
|
||||||
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))?;
|
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
let get_fn_name = format!("get${}", id);
|
let (lhs_value, pos) = match lhs.as_ref() {
|
||||||
let val = self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)?;
|
Expr::Identifier(id, pos) => {
|
||||||
Self::get_indexed_value(val, idx, *pos).map(|(v, _)| v)
|
let get_fn_name = format!("get${}", id);
|
||||||
|
(
|
||||||
|
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)?,
|
||||||
|
*pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expr => return Err(EvalAltResult::ErrorDotExpr(expr.position())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::get_indexed_value(lhs_value, idx, pos).map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Dot(inner_lhs, inner_rhs) => match inner_lhs.as_ref() {
|
Expr::Dot(inner_lhs, inner_rhs) => match inner_lhs.as_ref() {
|
||||||
@ -211,16 +227,25 @@ impl Engine<'_> {
|
|||||||
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)
|
||||||
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))
|
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))
|
||||||
}
|
}
|
||||||
Expr::Index(id, idx_expr, pos) => {
|
Expr::Index(lhs, idx_expr) => {
|
||||||
let idx = *self
|
let idx = *self
|
||||||
.eval_expr(scope, idx_expr)?
|
.eval_expr(scope, idx_expr)?
|
||||||
.downcast::<i64>()
|
.downcast::<i64>()
|
||||||
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))?;
|
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
let get_fn_name = format!("get${}", id);
|
let (lhs_value, pos) = match lhs.as_ref() {
|
||||||
let val = self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)?;
|
Expr::Identifier(id, pos) => {
|
||||||
Self::get_indexed_value(val, idx, *pos).and_then(|(mut v, _)| {
|
let get_fn_name = format!("get${}", id);
|
||||||
self.get_dot_val_helper(scope, v.as_mut(), inner_rhs)
|
(
|
||||||
|
self.call_fn_raw(&get_fn_name, vec![this_ptr], None, *pos)?,
|
||||||
|
*pos,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
expr => return Err(EvalAltResult::ErrorDotExpr(expr.position())),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::get_indexed_value(lhs_value, idx, pos).and_then(|(mut value, _)| {
|
||||||
|
self.get_dot_val_helper(scope, value.as_mut(), inner_rhs)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(EvalAltResult::ErrorDotExpr(inner_lhs.position())),
|
_ => Err(EvalAltResult::ErrorDotExpr(inner_lhs.position())),
|
||||||
@ -246,14 +271,14 @@ impl Engine<'_> {
|
|||||||
val: Dynamic,
|
val: Dynamic,
|
||||||
idx: i64,
|
idx: i64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(Dynamic, bool), EvalAltResult> {
|
) -> Result<(Dynamic, VariableType), EvalAltResult> {
|
||||||
if val.is::<Array>() {
|
if val.is::<Array>() {
|
||||||
let arr = val.downcast::<Array>().unwrap();
|
let arr = val.downcast::<Array>().unwrap();
|
||||||
|
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
arr.get(idx as usize)
|
arr.get(idx as usize)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|v| (v, true))
|
.map(|v| (v, VariableType::Array))
|
||||||
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, pos))
|
.ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), idx, pos))
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, pos))
|
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, pos))
|
||||||
@ -264,7 +289,7 @@ impl Engine<'_> {
|
|||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
s.chars()
|
s.chars()
|
||||||
.nth(idx as usize)
|
.nth(idx as usize)
|
||||||
.map(|ch| (ch.into_dynamic(), false))
|
.map(|ch| (ch.into_dynamic(), VariableType::String))
|
||||||
.ok_or_else(|| EvalAltResult::ErrorStringBounds(s.chars().count(), idx, pos))
|
.ok_or_else(|| EvalAltResult::ErrorStringBounds(s.chars().count(), idx, pos))
|
||||||
} else {
|
} else {
|
||||||
Err(EvalAltResult::ErrorStringBounds(
|
Err(EvalAltResult::ErrorStringBounds(
|
||||||
@ -281,22 +306,28 @@ impl Engine<'_> {
|
|||||||
fn eval_index_expr(
|
fn eval_index_expr(
|
||||||
&mut self,
|
&mut self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
id: &str,
|
lhs: &Expr,
|
||||||
idx: &Expr,
|
idx_expr: &Expr,
|
||||||
begin: Position,
|
) -> Result<(VariableType, Option<(String, usize)>, usize, Dynamic), EvalAltResult> {
|
||||||
) -> Result<(bool, usize, usize, Dynamic), EvalAltResult> {
|
|
||||||
let idx = *self
|
let idx = *self
|
||||||
.eval_expr(scope, idx)?
|
.eval_expr(scope, idx_expr)?
|
||||||
.downcast::<i64>()
|
.downcast::<i64>()
|
||||||
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx.position()))?;
|
.map_err(|_| EvalAltResult::ErrorIndexExpr(idx_expr.position()))?;
|
||||||
|
|
||||||
Self::search_scope(
|
match lhs {
|
||||||
scope,
|
Expr::Identifier(id, _) => Self::search_scope(
|
||||||
id,
|
scope,
|
||||||
|val| Self::get_indexed_value(val, idx, begin),
|
&id,
|
||||||
begin,
|
|val| Self::get_indexed_value(val, idx, lhs.position()),
|
||||||
)
|
lhs.position(),
|
||||||
.map(|(idx_sc, (val, is_array))| (is_array, idx_sc, idx as usize, val))
|
)
|
||||||
|
.map(|(src_idx, (val, source_type))| {
|
||||||
|
(source_type, Some((id.clone(), src_idx)), idx as usize, val)
|
||||||
|
}),
|
||||||
|
|
||||||
|
expr => Self::get_indexed_value(self.eval_expr(scope, expr)?, idx, lhs.position())
|
||||||
|
.map(|(val, _)| (VariableType::Expression, None, idx as usize, val)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
|
fn str_replace_char(s: &mut String, idx: usize, new_ch: char) {
|
||||||
@ -333,22 +364,37 @@ impl Engine<'_> {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Index(id, idx_expr, pos) => {
|
Expr::Index(lhs, idx_expr) => {
|
||||||
let (is_array, sc_idx, idx, mut target) =
|
let (source_type, src, idx, mut target) =
|
||||||
self.eval_index_expr(scope, id, idx_expr, *pos)?;
|
self.eval_index_expr(scope, lhs, idx_expr)?;
|
||||||
let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs);
|
let value = self.get_dot_val_helper(scope, target.as_mut(), dot_rhs);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to reassign it because
|
// In case the expression mutated `target`, we need to reassign it because
|
||||||
// of the above `clone`.
|
// of the above `clone`.
|
||||||
|
|
||||||
if is_array {
|
match source_type {
|
||||||
scope.get_mut(id, sc_idx).downcast_mut::<Array>().unwrap()[idx] = target;
|
VariableType::Array => {
|
||||||
} else {
|
let src = src.unwrap();
|
||||||
Self::str_replace_char(
|
scope
|
||||||
scope.get_mut(id, sc_idx).downcast_mut::<String>().unwrap(), // Root is a string
|
.get_mut(&src.0, src.1)
|
||||||
idx,
|
.downcast_mut::<Array>()
|
||||||
*target.downcast::<char>().unwrap(), // Target should be a char
|
.unwrap()[idx] = target
|
||||||
);
|
}
|
||||||
|
|
||||||
|
VariableType::String => {
|
||||||
|
let src = src.unwrap();
|
||||||
|
|
||||||
|
Self::str_replace_char(
|
||||||
|
scope
|
||||||
|
.get_mut(&src.0, src.1)
|
||||||
|
.downcast_mut::<String>()
|
||||||
|
.unwrap(), // Root is a string
|
||||||
|
idx,
|
||||||
|
*target.downcast::<char>().unwrap(), // Target should be a char
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => panic!("source_type must be either Array or String"),
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
value
|
||||||
@ -417,21 +463,38 @@ impl Engine<'_> {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Index(id, iex_expr, pos) => {
|
Expr::Index(lhs, idx_expr) => {
|
||||||
let (is_array, sc_idx, idx, mut target) =
|
let (source_type, src, idx, mut target) =
|
||||||
self.eval_index_expr(scope, id, iex_expr, *pos)?;
|
self.eval_index_expr(scope, lhs, idx_expr)?;
|
||||||
let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val);
|
let value = self.set_dot_val_helper(target.as_mut(), dot_rhs, source_val);
|
||||||
|
|
||||||
// In case the expression mutated `target`, we need to reassign it because
|
// In case the expression mutated `target`, we need to reassign it because
|
||||||
// of the above `clone`.
|
// of the above `clone`.
|
||||||
if is_array {
|
|
||||||
scope.get_mut(id, sc_idx).downcast_mut::<Array>().unwrap()[idx] = target;
|
match source_type {
|
||||||
} else {
|
VariableType::Array => {
|
||||||
Self::str_replace_char(
|
let src = src.unwrap();
|
||||||
scope.get_mut(id, sc_idx).downcast_mut::<String>().unwrap(), // Root is a string
|
let val = scope
|
||||||
idx,
|
.get_mut(&src.0, src.1)
|
||||||
*target.downcast::<char>().unwrap(), // Target should be a char
|
.downcast_mut::<Array>()
|
||||||
);
|
.unwrap();
|
||||||
|
val[idx] = target
|
||||||
|
}
|
||||||
|
|
||||||
|
VariableType::String => {
|
||||||
|
let src = src.unwrap();
|
||||||
|
|
||||||
|
Self::str_replace_char(
|
||||||
|
scope
|
||||||
|
.get_mut(&src.0, src.1)
|
||||||
|
.downcast_mut::<String>()
|
||||||
|
.unwrap(), // Root is a string
|
||||||
|
idx,
|
||||||
|
*target.downcast::<char>().unwrap(), // Target should be a char
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => panic!("source_type must be either Array or String"),
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
value
|
||||||
@ -447,20 +510,17 @@ impl Engine<'_> {
|
|||||||
Expr::FloatConstant(i, _) => Ok((*i).into_dynamic()),
|
Expr::FloatConstant(i, _) => Ok((*i).into_dynamic()),
|
||||||
Expr::StringConstant(s, _) => Ok(s.into_dynamic()),
|
Expr::StringConstant(s, _) => Ok(s.into_dynamic()),
|
||||||
Expr::CharConstant(c, _) => Ok((*c).into_dynamic()),
|
Expr::CharConstant(c, _) => Ok((*c).into_dynamic()),
|
||||||
|
Expr::Identifier(id, pos) => {
|
||||||
Expr::Identifier(id, pos) => scope
|
Self::search_scope(scope, id, Ok, *pos).map(|(_, val)| val)
|
||||||
.get(id)
|
}
|
||||||
.map(|(_, _, val)| val)
|
Expr::Index(lhs, idx_expr) => self
|
||||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.clone(), *pos)),
|
.eval_index_expr(scope, lhs, idx_expr)
|
||||||
|
|
||||||
Expr::Index(id, idx_expr, pos) => self
|
|
||||||
.eval_index_expr(scope, id, idx_expr, *pos)
|
|
||||||
.map(|(_, _, _, x)| x),
|
.map(|(_, _, _, x)| x),
|
||||||
|
|
||||||
Expr::Assignment(ref id, rhs) => {
|
Expr::Assignment(lhs, rhs) => {
|
||||||
let rhs_val = self.eval_expr(scope, rhs)?;
|
let rhs_val = self.eval_expr(scope, rhs)?;
|
||||||
|
|
||||||
match id.as_ref() {
|
match lhs.as_ref() {
|
||||||
Expr::Identifier(name, pos) => {
|
Expr::Identifier(name, pos) => {
|
||||||
if let Some((idx, _, _)) = scope.get(name) {
|
if let Some((idx, _, _)) = scope.get(name) {
|
||||||
*scope.get_mut(name, idx) = rhs_val;
|
*scope.get_mut(name, idx) = rhs_val;
|
||||||
@ -469,47 +529,44 @@ impl Engine<'_> {
|
|||||||
Err(EvalAltResult::ErrorVariableNotFound(name.clone(), *pos))
|
Err(EvalAltResult::ErrorVariableNotFound(name.clone(), *pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Index(id, idx_expr, pos) => {
|
|
||||||
let idx_pos = idx_expr.position();
|
|
||||||
|
|
||||||
let idx = *match self.eval_expr(scope, &idx_expr)?.downcast::<i64>() {
|
Expr::Index(idx_lhs, idx_expr) => {
|
||||||
Ok(x) => x,
|
let (source_type, src, idx, _) =
|
||||||
_ => return Err(EvalAltResult::ErrorIndexExpr(idx_pos)),
|
self.eval_index_expr(scope, idx_lhs, idx_expr)?;
|
||||||
};
|
|
||||||
|
|
||||||
let val = match scope.get(id) {
|
match source_type {
|
||||||
Some((idx, _, _)) => scope.get_mut(id, idx),
|
VariableType::Array => {
|
||||||
_ => {
|
let src = src.unwrap();
|
||||||
return Err(EvalAltResult::ErrorVariableNotFound(id.clone(), *pos))
|
scope
|
||||||
|
.get_mut(&src.0, src.1)
|
||||||
|
.downcast_mut::<Array>()
|
||||||
|
.map(|arr| (arr[idx as usize] = rhs_val).into_dynamic())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorIndexExpr(idx_lhs.position())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(arr) = val.downcast_mut() as Option<&mut Array> {
|
VariableType::String => {
|
||||||
if idx < 0 {
|
let src = src.unwrap();
|
||||||
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
|
scope
|
||||||
} else if idx as usize >= arr.len() {
|
.get_mut(&src.0, src.1)
|
||||||
Err(EvalAltResult::ErrorArrayBounds(arr.len(), idx, idx_pos))
|
.downcast_mut::<String>()
|
||||||
} else {
|
.map(|s| {
|
||||||
arr[idx as usize] = rhs_val;
|
Self::str_replace_char(
|
||||||
Ok(().into_dynamic())
|
s,
|
||||||
|
idx as usize,
|
||||||
|
*rhs_val.downcast::<char>().unwrap(),
|
||||||
|
)
|
||||||
|
.into_dynamic()
|
||||||
|
})
|
||||||
|
.ok_or_else(|| {
|
||||||
|
EvalAltResult::ErrorIndexExpr(idx_lhs.position())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if let Some(s) = val.downcast_mut() as Option<&mut String> {
|
|
||||||
let s_len = s.chars().count();
|
|
||||||
|
|
||||||
if idx < 0 {
|
_ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS(
|
||||||
Err(EvalAltResult::ErrorStringBounds(s_len, idx, idx_pos))
|
idx_lhs.position(),
|
||||||
} else if idx as usize >= s_len {
|
)),
|
||||||
Err(EvalAltResult::ErrorStringBounds(s_len, idx, idx_pos))
|
|
||||||
} else {
|
|
||||||
Self::str_replace_char(
|
|
||||||
s,
|
|
||||||
idx as usize,
|
|
||||||
*rhs_val.downcast::<char>().unwrap(),
|
|
||||||
);
|
|
||||||
Ok(().into_dynamic())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorIndexExpr(idx_pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,7 +574,7 @@ impl Engine<'_> {
|
|||||||
self.set_dot_val(scope, dot_lhs, dot_rhs, rhs_val)
|
self.set_dot_val(scope, dot_lhs, dot_rhs, rhs_val)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS(id.position())),
|
_ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS(lhs.position())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ pub enum Expr {
|
|||||||
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
|
FunctionCall(String, Vec<Expr>, Option<Dynamic>, Position),
|
||||||
Assignment(Box<Expr>, Box<Expr>),
|
Assignment(Box<Expr>, Box<Expr>),
|
||||||
Dot(Box<Expr>, Box<Expr>),
|
Dot(Box<Expr>, Box<Expr>),
|
||||||
Index(String, Box<Expr>, Position),
|
Index(Box<Expr>, Box<Expr>),
|
||||||
Array(Vec<Expr>, Position),
|
Array(Vec<Expr>, Position),
|
||||||
And(Box<Expr>, Box<Expr>),
|
And(Box<Expr>, Box<Expr>),
|
||||||
Or(Box<Expr>, Box<Expr>),
|
Or(Box<Expr>, Box<Expr>),
|
||||||
@ -150,15 +150,16 @@ impl Expr {
|
|||||||
| Expr::CharConstant(_, pos)
|
| Expr::CharConstant(_, pos)
|
||||||
| Expr::StringConstant(_, pos)
|
| Expr::StringConstant(_, pos)
|
||||||
| Expr::FunctionCall(_, _, _, pos)
|
| Expr::FunctionCall(_, _, _, pos)
|
||||||
| Expr::Index(_, _, pos)
|
|
||||||
| Expr::Array(_, pos)
|
| Expr::Array(_, pos)
|
||||||
| Expr::True(pos)
|
| Expr::True(pos)
|
||||||
| Expr::False(pos)
|
| Expr::False(pos)
|
||||||
| Expr::Unit(pos) => *pos,
|
| Expr::Unit(pos) => *pos,
|
||||||
|
|
||||||
Expr::Assignment(e, _) | Expr::Dot(e, _) | Expr::And(e, _) | Expr::Or(e, _) => {
|
Expr::Index(e, _)
|
||||||
e.position()
|
| Expr::Assignment(e, _)
|
||||||
}
|
| Expr::Dot(e, _)
|
||||||
|
| Expr::And(e, _)
|
||||||
|
| Expr::Or(e, _) => e.position(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1076,15 +1077,14 @@ fn parse_call_expr<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_index_expr<'a>(
|
fn parse_index_expr<'a>(
|
||||||
id: String,
|
lhs: Box<Expr>,
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
begin: Position,
|
|
||||||
) -> Result<Expr, ParseError> {
|
) -> Result<Expr, ParseError> {
|
||||||
match parse_expr(input) {
|
match parse_expr(input) {
|
||||||
Ok(idx) => match input.peek() {
|
Ok(idx_expr) => match input.peek() {
|
||||||
Some(&(Token::RightBracket, _)) => {
|
Some(&(Token::RightBracket, _)) => {
|
||||||
input.next();
|
input.next();
|
||||||
return Ok(Expr::Index(id, Box::new(idx), begin));
|
return Ok(Expr::Index(lhs, Box::new(idx_expr)));
|
||||||
}
|
}
|
||||||
Some(&(_, pos)) => return Err(ParseError::new(PERR::MalformedIndexExpr, pos)),
|
Some(&(_, pos)) => return Err(ParseError::new(PERR::MalformedIndexExpr, pos)),
|
||||||
None => return Err(ParseError::new(PERR::MalformedIndexExpr, Position::eof())),
|
None => return Err(ParseError::new(PERR::MalformedIndexExpr, Position::eof())),
|
||||||
@ -1105,7 +1105,7 @@ fn parse_ident_expr<'a>(
|
|||||||
}
|
}
|
||||||
Some(&(Token::LeftBracket, _)) => {
|
Some(&(Token::LeftBracket, _)) => {
|
||||||
input.next();
|
input.next();
|
||||||
parse_index_expr(id, input, begin)
|
parse_index_expr(Box::new(Expr::Identifier(id, begin)), input)
|
||||||
}
|
}
|
||||||
Some(_) => Ok(Expr::Identifier(id, begin)),
|
Some(_) => Ok(Expr::Identifier(id, begin)),
|
||||||
None => Ok(Expr::Identifier(id, Position::eof())),
|
None => Ok(Expr::Identifier(id, Position::eof())),
|
||||||
@ -1147,14 +1147,30 @@ fn parse_array_expr<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, ParseError> {
|
||||||
match input.next() {
|
let token = input.next();
|
||||||
|
|
||||||
|
let mut follow_on = false;
|
||||||
|
|
||||||
|
let r = match token {
|
||||||
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
|
Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)),
|
||||||
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
|
Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)),
|
||||||
Some((Token::StringConst(s), pos)) => Ok(Expr::StringConstant(s, pos)),
|
|
||||||
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
|
Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)),
|
||||||
Some((Token::Identifier(s), pos)) => parse_ident_expr(s, input, pos),
|
Some((Token::StringConst(s), pos)) => {
|
||||||
Some((Token::LeftParen, pos)) => parse_paren_expr(input, pos),
|
follow_on = true;
|
||||||
Some((Token::LeftBracket, pos)) => parse_array_expr(input, pos),
|
Ok(Expr::StringConstant(s, pos))
|
||||||
|
}
|
||||||
|
Some((Token::Identifier(s), pos)) => {
|
||||||
|
follow_on = true;
|
||||||
|
parse_ident_expr(s, input, pos)
|
||||||
|
}
|
||||||
|
Some((Token::LeftParen, pos)) => {
|
||||||
|
follow_on = true;
|
||||||
|
parse_paren_expr(input, pos)
|
||||||
|
}
|
||||||
|
Some((Token::LeftBracket, pos)) => {
|
||||||
|
follow_on = true;
|
||||||
|
parse_array_expr(input, pos)
|
||||||
|
}
|
||||||
Some((Token::True, pos)) => Ok(Expr::True(pos)),
|
Some((Token::True, pos)) => Ok(Expr::True(pos)),
|
||||||
Some((Token::False, pos)) => Ok(Expr::False(pos)),
|
Some((Token::False, pos)) => Ok(Expr::False(pos)),
|
||||||
Some((Token::LexError(le), pos)) => {
|
Some((Token::LexError(le), pos)) => {
|
||||||
@ -1165,6 +1181,20 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
|
|||||||
pos,
|
pos,
|
||||||
)),
|
)),
|
||||||
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
None => Err(ParseError::new(PERR::InputPastEndOfFile, Position::eof())),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
if !follow_on {
|
||||||
|
return Ok(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post processing
|
||||||
|
match input.peek() {
|
||||||
|
Some(&(Token::LeftBracket, _)) => {
|
||||||
|
// Possible indexing
|
||||||
|
input.next();
|
||||||
|
parse_index_expr(Box::new(r), input)
|
||||||
|
}
|
||||||
|
_ => Ok(r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,16 +132,24 @@ impl std::fmt::Display for EvalAltResult {
|
|||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||||
}
|
}
|
||||||
Self::ErrorArrayBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
Self::ErrorArrayBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorArrayBounds(max, index, pos) => {
|
Self::ErrorArrayBounds(max, index, pos) => write!(
|
||||||
write!(f, "{} (max {}): {} ({})", desc, max - 1, index, pos)
|
f,
|
||||||
}
|
"Array index {} is out of bounds: max {} elements ({})",
|
||||||
|
index,
|
||||||
|
max - 1,
|
||||||
|
pos
|
||||||
|
),
|
||||||
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
||||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||||
}
|
}
|
||||||
Self::ErrorStringBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
Self::ErrorStringBounds(max, _, pos) if *max == 0 => write!(f, "{} ({})", desc, pos),
|
||||||
Self::ErrorStringBounds(max, index, pos) => {
|
Self::ErrorStringBounds(max, index, pos) => write!(
|
||||||
write!(f, "{} (max {}): {} ({})", desc, max - 1, index, pos)
|
f,
|
||||||
}
|
"String index {} is out of bounds: max {} characters ({})",
|
||||||
|
index,
|
||||||
|
max - 1,
|
||||||
|
pos
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user