use crate::dynamic::{AccessMode, Union};
use crate::fn_native::shared_make_mut;
use crate::module::NamespaceRef;
use crate::stdlib::{
borrow::Cow,
boxed::Box,
fmt,
hash::Hash,
num::NonZeroUsize,
ops::{Add, AddAssign},
string::String,
vec,
vec::Vec,
};
use crate::token::Token;
use crate::utils::{HashableHashMap, StraightHasherBuilder};
use crate::{
Dynamic, FnNamespace, FnPtr, ImmutableString, Module, Position, Shared, StaticVec, INT,
};
#[cfg(not(feature = "no_float"))]
use crate::{stdlib::str::FromStr, FLOAT};
#[cfg(not(feature = "no_index"))]
use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FnAccess {
Public,
Private,
}
#[derive(Debug, Clone)]
pub struct ScriptFnDef {
pub body: StmtBlock,
pub lib: Option<Shared<Module>>,
#[cfg(not(feature = "no_module"))]
pub mods: crate::engine::Imports,
pub name: ImmutableString,
pub access: FnAccess,
pub params: StaticVec<ImmutableString>,
#[cfg(not(feature = "no_closure"))]
pub externals: StaticVec<ImmutableString>,
pub comments: StaticVec<String>,
}
impl fmt::Display for ScriptFnDef {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private",
},
self.name,
self.params
.iter()
.map(|s| s.as_str())
.collect::<Vec<_>>()
.join(", ")
)
}
}
#[cfg(not(feature = "no_function"))]
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct ScriptFnMetadata<'a> {
pub comments: Vec<&'a str>,
pub access: FnAccess,
pub name: &'a str,
pub params: Vec<&'a str>,
}
#[cfg(not(feature = "no_function"))]
impl fmt::Display for ScriptFnMetadata<'_> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{}({})",
match self.access {
FnAccess::Public => "",
FnAccess::Private => "private",
},
self.name,
self.params.iter().cloned().collect::<Vec<_>>().join(", ")
)
}
}
#[cfg(not(feature = "no_function"))]
impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef {
#[inline(always)]
fn into(self) -> ScriptFnMetadata<'a> {
ScriptFnMetadata {
comments: self.comments.iter().map(|s| s.as_str()).collect(),
access: self.access,
name: &self.name,
params: self.params.iter().map(|s| s.as_str()).collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct AST {
source: Option<ImmutableString>,
body: StmtBlock,
functions: Shared<Module>,
#[cfg(not(feature = "no_module"))]
resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
}
impl Default for AST {
#[inline(always)]
fn default() -> Self {
Self {
source: None,
body: Default::default(),
functions: Default::default(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
}
impl AST {
#[inline(always)]
pub fn new(
statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>,
) -> Self {
Self {
source: None,
body: StmtBlock {
statements: statements.into_iter().collect(),
pos: Position::NONE,
},
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
#[inline(always)]
pub fn new_with_source(
statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>,
source: impl Into<ImmutableString>,
) -> Self {
Self {
source: Some(source.into()),
body: StmtBlock {
statements: statements.into_iter().collect(),
pos: Position::NONE,
},
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: None,
}
}
#[inline(always)]
pub fn source(&self) -> Option<&str> {
self.source.as_ref().map(|s| s.as_str())
}
#[inline(always)]
pub(crate) fn clone_source(&self) -> Option<ImmutableString> {
self.source.clone()
}
#[inline(always)]
pub fn set_source(&mut self, source: impl Into<ImmutableString>) -> &mut Self {
self.source = Some(source.into());
if let Some(module) = Shared::get_mut(&mut self.functions) {
module.set_id(self.source.clone());
}
self
}
#[inline(always)]
pub fn clear_source(&mut self) -> &mut Self {
self.source = None;
self
}
#[cfg(not(feature = "internals"))]
#[inline(always)]
pub(crate) fn statements(&self) -> &[Stmt] {
&self.body.statements
}
#[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"]
#[inline(always)]
pub fn statements(&self) -> &[Stmt] {
&self.body.statements
}
#[cfg(not(feature = "no_optimize"))]
#[inline(always)]
pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
&mut self.body.statements
}
#[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub(crate) fn shared_lib(&self) -> Shared<Module> {
self.functions.clone()
}
#[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"]
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn shared_lib(&self) -> Shared<Module> {
self.functions.clone()
}
#[cfg(not(feature = "internals"))]
#[inline(always)]
pub(crate) fn lib(&self) -> &Module {
&self.functions
}
#[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"]
#[inline(always)]
pub fn lib(&self) -> &Module {
&self.functions
}
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "internals"))]
#[inline(always)]
pub(crate) fn resolver(
&self,
) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone()
}
#[cfg(not(feature = "no_module"))]
#[cfg(feature = "internals")]
#[inline(always)]
pub fn resolver(&self) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone()
}
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn set_resolver(
&mut self,
resolver: impl Into<Shared<crate::module::resolvers::StaticModuleResolver>>,
) -> &mut Self {
self.resolver = Some(resolver.into());
self
}
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn clone_functions_only(&self) -> Self {
self.clone_functions_only_filtered(|_, _, _, _, _| true)
}
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn clone_functions_only_filtered(
&self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
let mut functions: Module = Default::default();
functions.merge_filtered(&self.functions, &filter);
Self {
source: self.source.clone(),
body: Default::default(),
functions: functions.into(),
#[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(),
}
}
#[inline(always)]
pub fn clone_statements_only(&self) -> Self {
Self {
source: self.source.clone(),
body: self.body.clone(),
functions: Default::default(),
#[cfg(not(feature = "no_module"))]
resolver: self.resolver.clone(),
}
}
#[inline(always)]
pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _, _, _| true)
}
#[inline(always)]
pub fn combine(&mut self, other: Self) -> &mut Self {
self.combine_filtered(other, |_, _, _, _, _| true)
}
#[inline]
pub fn merge_filtered(
&self,
other: &Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> Self {
let Self {
body, functions, ..
} = self;
let merged = match (body.is_empty(), other.body.is_empty()) {
(false, false) => {
let mut body = body.clone();
body.statements
.extend(other.body.statements.iter().cloned());
body
}
(false, true) => body.clone(),
(true, false) => other.body.clone(),
(true, true) => Default::default(),
};
let source = other.source.clone().or_else(|| self.source.clone());
let mut functions = functions.as_ref().clone();
functions.merge_filtered(&other.functions, &filter);
if let Some(source) = source {
Self::new_with_source(merged.statements, functions, source)
} else {
Self::new(merged.statements, functions)
}
}
#[inline(always)]
pub fn combine_filtered(
&mut self,
other: Self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
) -> &mut Self {
self.body
.statements
.extend(other.body.statements.into_iter());
if !other.functions.is_empty() {
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &filter);
}
self
}
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn retain_functions(
&mut self,
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
) -> &mut Self {
if !self.functions.is_empty() {
shared_make_mut(&mut self.functions).retain_script_functions(filter);
}
self
}
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
}
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into())
}
#[cfg(not(feature = "no_function"))]
#[inline(always)]
pub fn clear_functions(&mut self) {
self.functions = Default::default();
}
#[inline(always)]
pub fn clear_statements(&mut self) {
self.body = Default::default();
}
#[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Default::default();
for stmt in self.statements() {
if !stmt.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_function"))]
for stmt in self.iter_fn_def().flat_map(|f| f.body.statements.iter()) {
if !stmt.walk(path, on_node) {
return false;
}
}
true
}
#[cfg(feature = "internals")]
#[inline(always)]
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Default::default();
for stmt in self.statements() {
if !stmt.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_function"))]
for stmt in self.iter_fn_def().flat_map(|f| f.body.statements.iter()) {
if !stmt.walk(path, on_node) {
return false;
}
}
true
}
}
impl<A: AsRef<AST>> Add<A> for &AST {
type Output = AST;
#[inline(always)]
fn add(self, rhs: A) -> Self::Output {
self.merge(rhs.as_ref())
}
}
impl<A: Into<AST>> AddAssign<A> for AST {
#[inline(always)]
fn add_assign(&mut self, rhs: A) {
self.combine(rhs.into());
}
}
impl AsRef<[Stmt]> for AST {
#[inline(always)]
fn as_ref(&self) -> &[Stmt] {
self.statements()
}
}
impl AsRef<Module> for AST {
#[inline(always)]
fn as_ref(&self) -> &Module {
self.lib()
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct Ident {
pub name: ImmutableString,
pub pos: Position,
}
impl fmt::Debug for Ident {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ident({:?} @ {:?})", self.name, self.pos)
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
pub enum ReturnType {
Return,
Exception,
}
#[derive(Debug, Clone, Hash)]
pub enum ASTNode<'a> {
Stmt(&'a Stmt),
Expr(&'a Expr),
}
impl<'a> From<&'a Stmt> for ASTNode<'a> {
fn from(stmt: &'a Stmt) -> Self {
Self::Stmt(stmt)
}
}
impl<'a> From<&'a Expr> for ASTNode<'a> {
fn from(expr: &'a Expr) -> Self {
Self::Expr(expr)
}
}
#[derive(Clone, Hash, Default)]
pub struct StmtBlock {
pub statements: StaticVec<Stmt>,
pub pos: Position,
}
impl StmtBlock {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.statements.is_empty()
}
#[inline(always)]
pub fn len(&self) -> usize {
self.statements.len()
}
}
impl fmt::Debug for StmtBlock {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.pos.is_none() {
write!(f, "{} @ ", self.pos)?;
}
fmt::Debug::fmt(&self.statements, f)
}
}
#[derive(Debug, Clone, Hash)]
pub enum Stmt {
Noop(Position),
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
Switch(
Expr,
Box<(
HashableHashMap<u64, StmtBlock, StraightHasherBuilder>,
StmtBlock,
)>,
Position,
),
While(Expr, Box<StmtBlock>, Position),
Do(Box<StmtBlock>, Expr, bool, Position),
For(Expr, Box<(String, StmtBlock)>, Position),
Let(Expr, Ident, bool, Position),
Const(Expr, Ident, bool, Position),
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
Block(Vec<Stmt>, Position),
TryCatch(
Box<(StmtBlock, Option<Ident>, StmtBlock)>,
Position,
Position,
),
Expr(Expr),
Continue(Position),
Break(Position),
Return(ReturnType, Option<Expr>, Position),
#[cfg(not(feature = "no_module"))]
Import(Expr, Option<Ident>, Position),
#[cfg(not(feature = "no_module"))]
Export(Vec<(Ident, Option<Ident>)>, Position),
#[cfg(not(feature = "no_closure"))]
Share(Ident),
}
impl Default for Stmt {
#[inline(always)]
fn default() -> Self {
Self::Noop(Position::NONE)
}
}
impl From<Stmt> for StmtBlock {
#[inline(always)]
fn from(stmt: Stmt) -> Self {
match stmt {
Stmt::Block(block, pos) => Self {
statements: block.into(),
pos,
},
Stmt::Noop(pos) => Self {
statements: Default::default(),
pos,
},
_ => {
let pos = stmt.position();
let statements = vec![stmt].into();
Self { statements, pos }
}
}
}
}
impl Stmt {
#[inline(always)]
pub fn is_noop(&self) -> bool {
match self {
Self::Noop(_) => true,
_ => false,
}
}
pub fn position(&self) -> Position {
match self {
Self::Noop(pos)
| Self::Continue(pos)
| Self::Break(pos)
| Self::Block(_, pos)
| Self::Assignment(_, pos)
| Self::If(_, _, pos)
| Self::Switch(_, _, pos)
| Self::While(_, _, pos)
| Self::Do(_, _, _, pos)
| Self::For(_, _, pos)
| Self::Return(_, _, pos)
| Self::Let(_, _, _, pos)
| Self::Const(_, _, _, pos)
| Self::TryCatch(_, pos, _) => *pos,
Self::Expr(x) => x.position(),
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos,
#[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(x) => x.pos,
}
}
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
Self::Noop(pos)
| Self::Continue(pos)
| Self::Break(pos)
| Self::Block(_, pos)
| Self::Assignment(_, pos)
| Self::If(_, _, pos)
| Self::Switch(_, _, pos)
| Self::While(_, _, pos)
| Self::Do(_, _, _, pos)
| Self::For(_, _, pos)
| Self::Return(_, _, pos)
| Self::Let(_, _, _, pos)
| Self::Const(_, _, _, pos)
| Self::TryCatch(_, pos, _) => *pos = new_pos,
Self::Expr(x) => {
x.set_position(new_pos);
}
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos = new_pos,
#[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))]
Self::Share(x) => x.pos = new_pos,
}
self
}
pub fn returns_value(&self) -> bool {
match self {
Self::If(_, _, _) | Self::Switch(_, _, _) | Self::Block(_, _) | Self::Expr(_) => true,
Self::Noop(_)
| Self::While(_, _, _)
| Self::Do(_, _, _, _)
| Self::For(_, _, _)
| Self::TryCatch(_, _, _) => false,
Self::Let(_, _, _, _)
| Self::Const(_, _, _, _)
| Self::Assignment(_, _)
| Self::Continue(_)
| Self::Break(_)
| Self::Return(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => unreachable!("Stmt::Share should not be parsed"),
}
}
pub fn is_self_terminated(&self) -> bool {
match self {
Self::If(_, _, _)
| Self::Switch(_, _, _)
| Self::While(_, _, _)
| Self::For(_, _, _)
| Self::Block(_, _)
| Self::TryCatch(_, _, _) => true,
Self::Noop(_) => false,
Self::Let(_, _, _, _)
| Self::Const(_, _, _, _)
| Self::Assignment(_, _)
| Self::Expr(_)
| Self::Do(_, _, _, _)
| Self::Continue(_)
| Self::Break(_)
| Self::Return(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => unreachable!("Stmt::Share should not be parsed"),
}
}
pub fn is_pure(&self) -> bool {
match self {
Self::Noop(_) => true,
Self::Expr(expr) => expr.is_pure(),
Self::If(condition, x, _) => {
condition.is_pure()
&& x.0.statements.iter().all(Stmt::is_pure)
&& x.1.statements.iter().all(Stmt::is_pure)
}
Self::Switch(expr, x, _) => {
expr.is_pure()
&& x.0
.values()
.flat_map(|block| block.statements.iter())
.all(Stmt::is_pure)
&& x.1.statements.iter().all(Stmt::is_pure)
}
Self::While(condition, block, _) | Self::Do(block, condition, _, _) => {
condition.is_pure() && block.statements.iter().all(Stmt::is_pure)
}
Self::For(iterable, x, _) => {
iterable.is_pure() && x.1.statements.iter().all(Stmt::is_pure)
}
Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
Self::TryCatch(x, _, _) => {
x.0.statements.iter().all(Stmt::is_pure) && x.2.statements.iter().all(Stmt::is_pure)
}
#[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) => false,
#[cfg(not(feature = "no_module"))]
Self::Export(_, _) => false,
#[cfg(not(feature = "no_closure"))]
Self::Share(_) => false,
}
}
#[inline(always)]
pub fn is_internally_pure(&self) -> bool {
match self {
Self::Let(expr, _, _, _) | Self::Const(expr, _, _, _) => expr.is_pure(),
#[cfg(not(feature = "no_module"))]
Self::Import(expr, _, _) => expr.is_pure(),
#[cfg(not(feature = "no_module"))]
Self::Export(_, _) => true,
_ => self.is_pure(),
}
}
#[inline(always)]
pub fn is_control_flow_break(&self) -> bool {
match self {
Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true,
_ => false,
}
}
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Let(e, _, _, _) | Self::Const(e, _, _, _) => {
if !e.walk(path, on_node) {
return false;
}
}
Self::If(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &x.0.statements {
if !s.walk(path, on_node) {
return false;
}
}
for s in &x.1.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Switch(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in x.0.values().flat_map(|block| block.statements.iter()) {
if !s.walk(path, on_node) {
return false;
}
}
for s in &x.1.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::While(e, s, _) | Self::Do(s, e, _, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &s.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::For(e, x, _) => {
if !e.walk(path, on_node) {
return false;
}
for s in &x.1.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Assignment(x, _) => {
if !x.0.walk(path, on_node) {
return false;
}
if !x.1.walk(path, on_node) {
return false;
}
}
Self::Block(x, _) => {
for s in x {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::TryCatch(x, _, _) => {
for s in &x.0.statements {
if !s.walk(path, on_node) {
return false;
}
}
for s in &x.2.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Expr(e) | Self::Return(_, Some(e), _) => {
if !e.walk(path, on_node) {
return false;
}
}
#[cfg(not(feature = "no_module"))]
Self::Import(e, _, _) => {
if !e.walk(path, on_node) {
return false;
}
}
_ => (),
}
path.pop().unwrap();
true
}
}
#[derive(Debug, Clone, Hash)]
pub struct CustomExpr {
pub keywords: StaticVec<Expr>,
pub tokens: Vec<ImmutableString>,
pub scope_delta: isize,
}
#[derive(Debug, Clone, Hash)]
pub struct BinaryExpr {
pub lhs: Expr,
pub rhs: Expr,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct OpAssignment {
pub hash_op_assign: u64,
pub hash_op: u64,
pub op: Cow<'static, str>,
}
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)]
pub struct FnHash {
script: Option<u64>,
native: u64,
}
impl fmt::Debug for FnHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(script) = self.script {
if script == self.native {
write!(f, "({}=={})", script, self.native)
} else {
write!(f, "({}, {})", script, self.native)
}
} else {
write!(f, "{}", self.native)
}
}
}
impl FnHash {
#[inline(always)]
pub fn from_native(hash: u64) -> Self {
Self {
script: None,
native: hash,
}
}
#[inline(always)]
pub fn from_script(hash: u64) -> Self {
Self {
script: Some(hash),
native: hash,
}
}
#[inline(always)]
pub fn from_script_and_native(script: u64, native: u64) -> Self {
Self {
script: Some(script),
native,
}
}
#[inline(always)]
pub fn is_native_only(&self) -> bool {
self.script.is_none()
}
#[inline(always)]
pub fn script_hash(&self) -> u64 {
self.script.unwrap()
}
#[inline(always)]
pub fn native_hash(&self) -> u64 {
self.native
}
}
#[derive(Debug, Clone, Default, Hash)]
pub struct FnCallExpr {
pub hash: FnHash,
pub capture: bool,
pub args: StaticVec<Expr>,
pub namespace: Option<NamespaceRef>,
pub name: Cow<'static, str>,
}
#[cfg(not(feature = "no_float"))]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
pub struct FloatWrapper(FLOAT);
#[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper {
#[inline(always)]
fn hash<H: crate::stdlib::hash::Hasher>(&self, state: &mut H) {
self.0.to_ne_bytes().hash(state);
}
}
#[cfg(not(feature = "no_float"))]
impl AsRef<FLOAT> for FloatWrapper {
#[inline(always)]
fn as_ref(&self) -> &FLOAT {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl AsMut<FLOAT> for FloatWrapper {
#[inline(always)]
fn as_mut(&mut self) -> &mut FLOAT {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl crate::stdlib::ops::Deref for FloatWrapper {
type Target = FLOAT;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl crate::stdlib::ops::DerefMut for FloatWrapper {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl fmt::Debug for FloatWrapper {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
#[cfg(not(feature = "no_float"))]
impl fmt::Display for FloatWrapper {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(feature = "no_std")]
use num_traits::Float;
let abs = self.0.abs();
if abs > 10000000000000.0 || abs < 0.0000000000001 {
write!(f, "{:e}", self.0)
} else {
self.0.fmt(f)
}
}
}
#[cfg(not(feature = "no_float"))]
impl From<FLOAT> for FloatWrapper {
#[inline(always)]
fn from(value: FLOAT) -> Self {
Self::new(value)
}
}
#[cfg(not(feature = "no_float"))]
impl FromStr for FloatWrapper {
type Err = <FLOAT as FromStr>::Err;
#[inline(always)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
FLOAT::from_str(s).map(Into::<Self>::into)
}
}
#[cfg(not(feature = "no_float"))]
impl FloatWrapper {
#[inline(always)]
pub const fn new(value: FLOAT) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Hash)]
pub enum Expr {
DynamicConstant(Box<Dynamic>, Position),
BoolConstant(bool, Position),
IntegerConstant(INT, Position),
#[cfg(not(feature = "no_float"))]
FloatConstant(FloatWrapper, Position),
CharConstant(char, Position),
StringConstant(ImmutableString, Position),
FnPointer(ImmutableString, Position),
Array(Box<StaticVec<Expr>>, Position),
Map(Box<StaticVec<(Ident, Expr)>>, Position),
Unit(Position),
Variable(Box<(Option<NonZeroUsize>, Option<(u64, NamespaceRef)>, Ident)>),
Property(Box<((ImmutableString, u64), (ImmutableString, u64), Ident)>),
Stmt(Box<StmtBlock>),
FnCall(Box<FnCallExpr>, Position),
Dot(Box<BinaryExpr>, Position),
Index(Box<BinaryExpr>, Position),
And(Box<BinaryExpr>, Position),
Or(Box<BinaryExpr>, Position),
Custom(Box<CustomExpr>, Position),
}
impl Default for Expr {
#[inline(always)]
fn default() -> Self {
Self::Unit(Position::NONE)
}
}
impl Expr {
#[inline]
pub fn get_constant_value(&self) -> Option<Dynamic> {
Some(match self {
Self::DynamicConstant(x, _) => x.as_ref().clone(),
Self::IntegerConstant(x, _) => (*x).into(),
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(x, _) => (*x).into(),
Self::CharConstant(x, _) => (*x).into(),
Self::StringConstant(x, _) => x.clone().into(),
Self::FnPointer(x, _) => Dynamic(Union::FnPtr(
Box::new(FnPtr::new_unchecked(x.clone(), Default::default())),
AccessMode::ReadOnly,
)),
Self::BoolConstant(x, _) => (*x).into(),
Self::Unit(_) => Dynamic::UNIT,
#[cfg(not(feature = "no_index"))]
Self::Array(x, _) if self.is_constant() => {
let mut arr = Array::with_capacity(crate::stdlib::cmp::max(
crate::engine::TYPICAL_ARRAY_SIZE,
x.len(),
));
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
Dynamic(Union::Array(Box::new(arr), AccessMode::ReadOnly))
}
#[cfg(not(feature = "no_object"))]
Self::Map(x, _) if self.is_constant() => {
let mut map = Map::with_capacity(crate::stdlib::cmp::max(
crate::engine::TYPICAL_MAP_SIZE,
x.len(),
));
map.extend(
x.iter()
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
);
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
}
_ => return None,
})
}
#[inline(always)]
pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> {
match self {
Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.2).name.as_str()),
_ => None,
}
}
#[inline]
pub fn position(&self) -> Position {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos,
Self::DynamicConstant(_, pos) => *pos,
Self::BoolConstant(_, pos) => *pos,
Self::IntegerConstant(_, pos) => *pos,
Self::CharConstant(_, pos) => *pos,
Self::StringConstant(_, pos) => *pos,
Self::FnPointer(_, pos) => *pos,
Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos,
Self::Property(x) => (x.2).pos,
Self::Stmt(x) => x.pos,
Self::Variable(x) => (x.2).pos,
Self::FnCall(_, pos) => *pos,
Self::And(x, _) | Self::Or(x, _) => x.lhs.position(),
Self::Unit(pos) => *pos,
Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(),
Self::Custom(_, pos) => *pos,
}
}
#[inline]
pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos = new_pos,
Self::DynamicConstant(_, pos) => *pos = new_pos,
Self::BoolConstant(_, pos) => *pos = new_pos,
Self::IntegerConstant(_, pos) => *pos = new_pos,
Self::CharConstant(_, pos) => *pos = new_pos,
Self::StringConstant(_, pos) => *pos = new_pos,
Self::FnPointer(_, pos) => *pos = new_pos,
Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos,
Self::Variable(x) => (x.2).pos = new_pos,
Self::Property(x) => (x.2).pos = new_pos,
Self::Stmt(x) => x.pos = new_pos,
Self::FnCall(_, pos) => *pos = new_pos,
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
Self::Unit(pos) => *pos = new_pos,
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
Self::Custom(_, pos) => *pos = new_pos,
}
self
}
#[inline]
pub fn is_pure(&self) -> bool {
match self {
Self::Array(x, _) => x.iter().all(Self::is_pure),
Self::Map(x, _) => x.iter().map(|(_, v)| v).all(Self::is_pure),
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
x.lhs.is_pure() && x.rhs.is_pure()
}
Self::Stmt(x) => x.statements.iter().all(Stmt::is_pure),
Self::Variable(_) => true,
_ => self.is_constant(),
}
}
#[inline(always)]
pub fn is_unit(&self) -> bool {
match self {
Self::Unit(_) => true,
_ => false,
}
}
#[inline]
pub fn is_constant(&self) -> bool {
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => true,
Self::DynamicConstant(_, _)
| Self::BoolConstant(_, _)
| Self::IntegerConstant(_, _)
| Self::CharConstant(_, _)
| Self::StringConstant(_, _)
| Self::FnPointer(_, _)
| Self::Unit(_) => true,
Self::Array(x, _) => x.iter().all(Self::is_constant),
Self::Map(x, _) => x.iter().map(|(_, expr)| expr).all(Self::is_constant),
_ => false,
}
}
#[inline]
pub fn is_valid_postfix(&self, token: &Token) -> bool {
match token {
#[cfg(not(feature = "no_object"))]
Token::Period => return true,
_ => (),
}
match self {
#[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => false,
Self::DynamicConstant(_, _)
| Self::BoolConstant(_, _)
| Self::IntegerConstant(_, _)
| Self::CharConstant(_, _)
| Self::FnPointer(_, _)
| Self::And(_, _)
| Self::Or(_, _)
| Self::Unit(_) => false,
Self::StringConstant(_, _)
| Self::FnCall(_, _)
| Self::Stmt(_)
| Self::Dot(_, _)
| Self::Index(_, _)
| Self::Array(_, _)
| Self::Map(_, _) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
_ => false,
},
Self::Variable(_) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
Token::Bang => true,
Token::DoubleColon => true,
_ => false,
},
Self::Property(_) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true,
_ => false,
},
Self::Custom(_, _) => false,
}
}
pub fn walk<'a>(
&'a self,
path: &mut Vec<ASTNode<'a>>,
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
) -> bool {
path.push(self.into());
if !on_node(path) {
return false;
}
match self {
Self::Stmt(x) => {
for s in &x.statements {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Array(x, _) => {
for e in x.as_ref() {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Map(x, _) => {
for (_, e) in x.as_ref() {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {
if !x.lhs.walk(path, on_node) {
return false;
}
if !x.rhs.walk(path, on_node) {
return false;
}
}
Self::FnCall(x, _) => {
for e in &x.args {
if !e.walk(path, on_node) {
return false;
}
}
}
Self::Custom(x, _) => {
for e in &x.keywords {
if !e.walk(path, on_node) {
return false;
}
}
}
_ => (),
}
path.pop().unwrap();
true
}
}
#[cfg(test)]
mod tests {
#[test]
fn check_struct_sizes() {
use crate::stdlib::mem::size_of;
use crate::*;
assert_eq!(size_of::<Dynamic>(), 16);
assert_eq!(size_of::<Option<Dynamic>>(), 16);
assert_eq!(size_of::<Position>(), 4);
assert_eq!(size_of::<ast::Expr>(), 16);
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
assert_eq!(size_of::<ast::Stmt>(), 40);
assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
assert_eq!(size_of::<FnPtr>(), 32);
assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16);
assert_eq!(size_of::<EvalAltResult>(), 72);
}
}