Allow AST optimization based on external Scope.
This commit is contained in:
parent
9844ae8665
commit
b3a22d942a
@ -73,7 +73,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = engine
|
if let Err(err) = engine
|
||||||
.compile(&input)
|
.compile_with_scope(&scope, &input)
|
||||||
.map_err(EvalAltResult::ErrorParsing)
|
.map_err(EvalAltResult::ErrorParsing)
|
||||||
.and_then(|r| {
|
.and_then(|r| {
|
||||||
ast = Some(r);
|
ast = Some(r);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// This is a script to calculate prime numbers.
|
// This is a script to calculate prime numbers.
|
||||||
|
|
||||||
let MAX_NUMBER_TO_CHECK = 10000; // 1229 primes
|
const MAX_NUMBER_TO_CHECK = 10000; // 1229 primes
|
||||||
|
|
||||||
let prime_mask = [];
|
let prime_mask = [];
|
||||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
||||||
|
26
src/api.rs
26
src/api.rs
@ -100,8 +100,14 @@ impl<'e> Engine<'e> {
|
|||||||
|
|
||||||
/// Compile a string into an AST.
|
/// Compile a string into an AST.
|
||||||
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
|
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
|
||||||
|
self.compile_with_scope(&Scope::new(), input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile a string into an AST using own scope.
|
||||||
|
/// The scope is useful for passing constants into the script for optimization.
|
||||||
|
pub fn compile_with_scope(&self, scope: &Scope, input: &str) -> Result<AST, ParseError> {
|
||||||
let tokens_stream = lex(input);
|
let tokens_stream = lex(input);
|
||||||
parse(&mut tokens_stream.peekable(), self.optimize)
|
parse(&mut tokens_stream.peekable(), scope, self.optimize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file(filename: &str) -> Result<String, EvalAltResult> {
|
fn read_file(filename: &str) -> Result<String, EvalAltResult> {
|
||||||
@ -117,8 +123,20 @@ impl<'e> Engine<'e> {
|
|||||||
|
|
||||||
/// Compile a file into an AST.
|
/// Compile a file into an AST.
|
||||||
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
|
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
|
||||||
Self::read_file(filename)
|
self.compile_file_with_scope(&Scope::new(), filename)
|
||||||
.and_then(|contents| self.compile(&contents).map_err(|err| err.into()))
|
}
|
||||||
|
|
||||||
|
/// Compile a file into an AST using own scope.
|
||||||
|
/// The scope is useful for passing constants into the script for optimization.
|
||||||
|
pub fn compile_file_with_scope(
|
||||||
|
&self,
|
||||||
|
scope: &Scope,
|
||||||
|
filename: &str,
|
||||||
|
) -> Result<AST, EvalAltResult> {
|
||||||
|
Self::read_file(filename).and_then(|contents| {
|
||||||
|
self.compile_with_scope(scope, &contents)
|
||||||
|
.map_err(|err| err.into())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a file.
|
/// Evaluate a file.
|
||||||
@ -241,7 +259,7 @@ impl<'e> Engine<'e> {
|
|||||||
) -> Result<(), EvalAltResult> {
|
) -> Result<(), EvalAltResult> {
|
||||||
let tokens_stream = lex(input);
|
let tokens_stream = lex(input);
|
||||||
|
|
||||||
let ast = parse(&mut tokens_stream.peekable(), self.optimize)
|
let ast = parse(&mut tokens_stream.peekable(), scope, self.optimize)
|
||||||
.map_err(EvalAltResult::ErrorParsing)?;
|
.map_err(EvalAltResult::ErrorParsing)?;
|
||||||
|
|
||||||
self.consume_ast_with_scope(scope, retain_functions, &ast)
|
self.consume_ast_with_scope(scope, retain_functions, &ast)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::engine::KEYWORD_DUMP_AST;
|
use crate::engine::KEYWORD_DUMP_AST;
|
||||||
use crate::parser::{Expr, Stmt};
|
use crate::parser::{Expr, Stmt};
|
||||||
|
use crate::scope::{Scope, ScopeEntry, VariableType};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
changed: bool,
|
changed: bool,
|
||||||
@ -330,13 +331,27 @@ fn optimize_expr(expr: Expr, state: &mut State) -> Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn optimize(statements: Vec<Stmt>) -> Vec<Stmt> {
|
pub(crate) fn optimize(statements: Vec<Stmt>, scope: &Scope) -> Vec<Stmt> {
|
||||||
let mut result = statements;
|
let mut result = statements;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut state = State::new();
|
let mut state = State::new();
|
||||||
let num_statements = result.len();
|
let num_statements = result.len();
|
||||||
|
|
||||||
|
scope
|
||||||
|
.iter()
|
||||||
|
.filter(|ScopeEntry { var_type, expr, .. }| {
|
||||||
|
// Get all the constants with definite constant expressions
|
||||||
|
*var_type == VariableType::Constant
|
||||||
|
&& expr.as_ref().map(|e| e.is_constant()).unwrap_or(false)
|
||||||
|
})
|
||||||
|
.for_each(|ScopeEntry { name, expr, .. }| {
|
||||||
|
state.push_constant(
|
||||||
|
name.as_ref(),
|
||||||
|
expr.as_ref().expect("should be Some(expr)").clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
result = result
|
result = result
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
use crate::any::Dynamic;
|
use crate::any::Dynamic;
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::{optimize::optimize, scope::VariableType};
|
use crate::optimize::optimize;
|
||||||
|
use crate::scope::{Scope, VariableType};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow, char, cmp::Ordering, fmt, iter::Peekable, str::Chars, str::FromStr, sync::Arc,
|
borrow::Cow, char, cmp::Ordering, fmt, iter::Peekable, str::Chars, str::FromStr, sync::Arc,
|
||||||
@ -148,6 +149,42 @@ pub struct AST(
|
|||||||
#[cfg(not(feature = "no_function"))] pub(crate) Vec<Arc<FnDef>>,
|
#[cfg(not(feature = "no_function"))] pub(crate) Vec<Arc<FnDef>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
impl AST {
|
||||||
|
/// Optimize the AST with constants defined in an external Scope.
|
||||||
|
///
|
||||||
|
/// Although optimization is performed by default during compilation, sometimes it is necessary to
|
||||||
|
/// "re"-optimize an AST. For example, when working with constants that are passed in via an
|
||||||
|
/// external scope, it will be more efficient to optimize the AST once again to take advantage
|
||||||
|
/// of the new constants.
|
||||||
|
///
|
||||||
|
/// With this method, it is no longer necessary to regenerate a large script with hard-coded
|
||||||
|
/// constant values. The script AST can be compiled once and stored. During actually evaluation,
|
||||||
|
/// constants are passed into the Engine via an external scope (i.e. with `scope.push_constant(...)`).
|
||||||
|
/// Then, the AST is cloned and the copy re-optimized before running.
|
||||||
|
pub fn optimize(self, scope: &Scope) -> Self {
|
||||||
|
AST(
|
||||||
|
crate::optimize::optimize(self.0, scope),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
self.1
|
||||||
|
.into_iter()
|
||||||
|
.map(|fn_def| {
|
||||||
|
let pos = fn_def.body.position();
|
||||||
|
let body = optimize(vec![fn_def.body.clone()], scope)
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap_or_else(|| Stmt::Noop(pos));
|
||||||
|
Arc::new(FnDef {
|
||||||
|
name: fn_def.name.clone(),
|
||||||
|
params: fn_def.params.clone(),
|
||||||
|
body,
|
||||||
|
pos: fn_def.pos,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)] // Do not derive Clone because it is expensive
|
#[derive(Debug)] // Do not derive Clone because it is expensive
|
||||||
pub struct FnDef {
|
pub struct FnDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -2043,6 +2080,7 @@ fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef, ParseE
|
|||||||
|
|
||||||
fn parse_top_level<'a>(
|
fn parse_top_level<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
scope: &Scope,
|
||||||
optimize_ast: bool,
|
optimize_ast: bool,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let mut statements = Vec::<Stmt>::new();
|
let mut statements = Vec::<Stmt>::new();
|
||||||
@ -2073,7 +2111,7 @@ fn parse_top_level<'a>(
|
|||||||
|
|
||||||
return Ok(AST(
|
return Ok(AST(
|
||||||
if optimize_ast {
|
if optimize_ast {
|
||||||
optimize(statements)
|
optimize(statements, &scope)
|
||||||
} else {
|
} else {
|
||||||
statements
|
statements
|
||||||
},
|
},
|
||||||
@ -2083,7 +2121,7 @@ fn parse_top_level<'a>(
|
|||||||
.map(|mut fn_def| {
|
.map(|mut fn_def| {
|
||||||
if optimize_ast {
|
if optimize_ast {
|
||||||
let pos = fn_def.body.position();
|
let pos = fn_def.body.position();
|
||||||
let mut body = optimize(vec![fn_def.body]);
|
let mut body = optimize(vec![fn_def.body], &scope);
|
||||||
fn_def.body = body.pop().unwrap_or_else(|| Stmt::Noop(pos));
|
fn_def.body = body.pop().unwrap_or_else(|| Stmt::Noop(pos));
|
||||||
}
|
}
|
||||||
Arc::new(fn_def)
|
Arc::new(fn_def)
|
||||||
@ -2094,7 +2132,8 @@ fn parse_top_level<'a>(
|
|||||||
|
|
||||||
pub fn parse<'a>(
|
pub fn parse<'a>(
|
||||||
input: &mut Peekable<TokenIterator<'a>>,
|
input: &mut Peekable<TokenIterator<'a>>,
|
||||||
|
scope: &Scope,
|
||||||
optimize_ast: bool,
|
optimize_ast: bool,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
parse_top_level(input, optimize_ast)
|
parse_top_level(input, scope, optimize_ast)
|
||||||
}
|
}
|
||||||
|
183
src/scope.rs
183
src/scope.rs
@ -1,6 +1,10 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::any::{Any, Dynamic};
|
use crate::any::{Any, AnyExt, Dynamic};
|
||||||
|
use crate::parser::{Expr, Position, INT};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::parser::FLOAT;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
@ -10,6 +14,13 @@ pub enum VariableType {
|
|||||||
Constant,
|
Constant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ScopeEntry<'a> {
|
||||||
|
pub name: Cow<'a, str>,
|
||||||
|
pub var_type: VariableType,
|
||||||
|
pub value: Dynamic,
|
||||||
|
pub expr: Option<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A type containing information about current scope.
|
/// A type containing information about current scope.
|
||||||
/// Useful for keeping state between `Engine` runs.
|
/// Useful for keeping state between `Engine` runs.
|
||||||
///
|
///
|
||||||
@ -31,7 +42,7 @@ pub enum VariableType {
|
|||||||
///
|
///
|
||||||
/// When searching for variables, newly-added variables are found before similarly-named but older variables,
|
/// When searching for variables, newly-added variables are found before similarly-named but older variables,
|
||||||
/// allowing for automatic _shadowing_ of variables.
|
/// allowing for automatic _shadowing_ of variables.
|
||||||
pub struct Scope<'a>(Vec<(Cow<'a, str>, VariableType, Dynamic)>);
|
pub struct Scope<'a>(Vec<ScopeEntry<'a>>);
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
/// Create a new Scope.
|
/// Create a new Scope.
|
||||||
@ -50,32 +61,62 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new variable to the Scope.
|
/// Add (push) a new variable to the Scope.
|
||||||
pub fn push<K: Into<Cow<'a, str>>, T: Any>(&mut self, key: K, value: T) {
|
pub fn push<K: Into<Cow<'a, str>>, T: Any>(&mut self, name: K, value: T) {
|
||||||
self.0
|
let value = value.into_dynamic();
|
||||||
.push((key.into(), VariableType::Normal, Box::new(value)));
|
|
||||||
|
// Map into constant expressions
|
||||||
|
let (expr, value) = map_dynamic_to_expr(value);
|
||||||
|
|
||||||
|
self.0.push(ScopeEntry {
|
||||||
|
name: name.into(),
|
||||||
|
var_type: VariableType::Normal,
|
||||||
|
value,
|
||||||
|
expr,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new constant to the Scope.
|
/// Add (push) a new constant to the Scope.
|
||||||
pub fn push_constant<K: Into<Cow<'a, str>>, T: Any>(&mut self, key: K, value: T) {
|
pub fn push_constant<K: Into<Cow<'a, str>>, T: Any>(&mut self, name: K, value: T) {
|
||||||
self.0
|
let value = value.into_dynamic();
|
||||||
.push((key.into(), VariableType::Constant, Box::new(value)));
|
|
||||||
|
// Map into constant expressions
|
||||||
|
let (expr, value) = map_dynamic_to_expr(value);
|
||||||
|
|
||||||
|
self.0.push(ScopeEntry {
|
||||||
|
name: name.into(),
|
||||||
|
var_type: VariableType::Constant,
|
||||||
|
value,
|
||||||
|
expr,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new variable with a `Dynamic` value to the Scope.
|
/// Add (push) a new variable with a `Dynamic` value to the Scope.
|
||||||
pub(crate) fn push_dynamic<K: Into<Cow<'a, str>>>(
|
pub(crate) fn push_dynamic<K: Into<Cow<'a, str>>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: K,
|
name: K,
|
||||||
val_type: VariableType,
|
var_type: VariableType,
|
||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
) {
|
) {
|
||||||
self.0.push((key.into(), val_type, value));
|
let (expr, value) = map_dynamic_to_expr(value);
|
||||||
|
|
||||||
|
self.0.push(ScopeEntry {
|
||||||
|
name: name.into(),
|
||||||
|
var_type,
|
||||||
|
value,
|
||||||
|
expr,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove (pop) the last variable from the Scope.
|
/// Remove (pop) the last variable from the Scope.
|
||||||
pub fn pop(&mut self) -> Option<(String, VariableType, Dynamic)> {
|
pub fn pop(&mut self) -> Option<(String, VariableType, Dynamic)> {
|
||||||
self.0
|
self.0.pop().map(
|
||||||
.pop()
|
|ScopeEntry {
|
||||||
.map(|(key, var_type, value)| (key.to_string(), var_type, value))
|
name,
|
||||||
|
var_type,
|
||||||
|
value,
|
||||||
|
..
|
||||||
|
}| (name.to_string(), var_type, value),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Truncate (rewind) the Scope to a previous size.
|
/// Truncate (rewind) the Scope to a previous size.
|
||||||
@ -89,8 +130,18 @@ impl<'a> Scope<'a> {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.find(|(_, (name, _, _))| name == key)
|
.find(|(_, ScopeEntry { name, .. })| name == key)
|
||||||
.map(|(i, (name, var_type, value))| (i, name.as_ref(), *var_type, value.clone()))
|
.map(
|
||||||
|
|(
|
||||||
|
i,
|
||||||
|
ScopeEntry {
|
||||||
|
name,
|
||||||
|
var_type,
|
||||||
|
value,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
)| (i, name.as_ref(), *var_type, value.clone()),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of a variable in the Scope, starting from the last.
|
/// Get the value of a variable in the Scope, starting from the last.
|
||||||
@ -99,50 +150,37 @@ impl<'a> Scope<'a> {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.find(|(_, (name, _, _))| name == key)
|
.find(|(_, ScopeEntry { name, .. })| name == key)
|
||||||
.and_then(|(_, (_, _, value))| value.downcast_ref::<T>())
|
.and_then(|(_, ScopeEntry { value, .. })| value.downcast_ref::<T>())
|
||||||
.map(|value| value.clone())
|
.map(T::clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a variable in the Scope.
|
/// Get a mutable reference to a variable in the Scope.
|
||||||
pub(crate) fn get_mut(&mut self, key: &str, index: usize) -> &mut Dynamic {
|
pub(crate) fn get_mut(&mut self, name: &str, index: usize) -> &mut Dynamic {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
entry.1,
|
entry.var_type,
|
||||||
VariableType::Constant,
|
VariableType::Constant,
|
||||||
"get mut of constant variable"
|
"get mut of constant variable"
|
||||||
);
|
);
|
||||||
assert_eq!(entry.0, key, "incorrect key at Scope entry");
|
assert_eq!(entry.name, name, "incorrect key at Scope entry");
|
||||||
|
|
||||||
&mut entry.2
|
&mut entry.value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type
|
/// Get a mutable reference to a variable in the Scope and downcast it to a specific type
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, key: &str, index: usize) -> &mut T {
|
pub(crate) fn get_mut_by_type<T: Any + Clone>(&mut self, name: &str, index: usize) -> &mut T {
|
||||||
self.get_mut(key, index)
|
self.get_mut(name, index)
|
||||||
.downcast_mut::<T>()
|
.downcast_mut::<T>()
|
||||||
.expect("wrong type cast")
|
.expect("wrong type cast")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to variables in the Scope.
|
/// Get an iterator to variables in the Scope.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, VariableType, &Dynamic)> {
|
pub fn iter(&self) -> impl Iterator<Item = &ScopeEntry> {
|
||||||
self.0
|
self.0.iter().rev() // Always search a Scope in reverse order
|
||||||
.iter()
|
|
||||||
.rev() // Always search a Scope in reverse order
|
|
||||||
.map(|(key, var_type, value)| (key.as_ref(), *var_type, value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// Get a mutable iterator to variables in the Scope.
|
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Dynamic)> {
|
|
||||||
self.0
|
|
||||||
.iter_mut()
|
|
||||||
.rev() // Always search a Scope in reverse order
|
|
||||||
.map(|(key, value)| (key.as_ref(), value))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K> std::iter::Extend<(K, VariableType, Dynamic)> for Scope<'a>
|
impl<'a, K> std::iter::Extend<(K, VariableType, Dynamic)> for Scope<'a>
|
||||||
@ -150,9 +188,66 @@ where
|
|||||||
K: Into<Cow<'a, str>>,
|
K: Into<Cow<'a, str>>,
|
||||||
{
|
{
|
||||||
fn extend<T: IntoIterator<Item = (K, VariableType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, VariableType, Dynamic)>>(&mut self, iter: T) {
|
||||||
self.0.extend(
|
self.0
|
||||||
iter.into_iter()
|
.extend(iter.into_iter().map(|(name, var_type, value)| ScopeEntry {
|
||||||
.map(|(key, var_type, value)| (key.into(), var_type, value)),
|
name: name.into(),
|
||||||
);
|
var_type,
|
||||||
|
value,
|
||||||
|
expr: None,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_dynamic_to_expr(value: Dynamic) -> (Option<Expr>, Dynamic) {
|
||||||
|
if value.is::<INT>() {
|
||||||
|
let value2 = value.clone();
|
||||||
|
(
|
||||||
|
Some(Expr::IntegerConstant(
|
||||||
|
*value.downcast::<INT>().expect("value should be INT"),
|
||||||
|
Position::none(),
|
||||||
|
)),
|
||||||
|
value2,
|
||||||
|
)
|
||||||
|
} else if value.is::<FLOAT>() {
|
||||||
|
let value2 = value.clone();
|
||||||
|
(
|
||||||
|
Some(Expr::FloatConstant(
|
||||||
|
*value.downcast::<FLOAT>().expect("value should be FLOAT"),
|
||||||
|
Position::none(),
|
||||||
|
)),
|
||||||
|
value2,
|
||||||
|
)
|
||||||
|
} else if value.is::<char>() {
|
||||||
|
let value2 = value.clone();
|
||||||
|
(
|
||||||
|
Some(Expr::CharConstant(
|
||||||
|
*value.downcast::<char>().expect("value should be char"),
|
||||||
|
Position::none(),
|
||||||
|
)),
|
||||||
|
value2,
|
||||||
|
)
|
||||||
|
} else if value.is::<String>() {
|
||||||
|
let value2 = value.clone();
|
||||||
|
(
|
||||||
|
Some(Expr::StringConstant(
|
||||||
|
*value.downcast::<String>().expect("value should be String"),
|
||||||
|
Position::none(),
|
||||||
|
)),
|
||||||
|
value2,
|
||||||
|
)
|
||||||
|
} else if value.is::<bool>() {
|
||||||
|
let value2 = value.clone();
|
||||||
|
(
|
||||||
|
Some(
|
||||||
|
if *value.downcast::<bool>().expect("value should be bool") {
|
||||||
|
Expr::True(Position::none())
|
||||||
|
} else {
|
||||||
|
Expr::False(Position::none())
|
||||||
|
},
|
||||||
|
),
|
||||||
|
value2,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user