Remove hashing of the entire script by making Expr and Stmt Hash.

This commit is contained in:
Stephen Chung 2021-01-04 11:58:24 +08:00
parent e2a47b2a65
commit cf9d35166d
6 changed files with 191 additions and 112 deletions

View File

@ -8,14 +8,13 @@ use crate::stdlib::{
boxed::Box,
collections::HashMap,
fmt,
hash::Hash,
hash::{Hash, Hasher},
num::{NonZeroU64, NonZeroUsize},
ops::{Add, AddAssign},
ops::{Add, AddAssign, Deref, DerefMut},
string::String,
vec,
vec::Vec,
};
use crate::syntax::FnCustomSyntaxEval;
use crate::token::Token;
use crate::utils::StraightHasherBuilder;
use crate::{
@ -492,11 +491,7 @@ impl AST {
(true, true) => vec![],
};
let source = if other.source.is_some() {
other.source.clone()
} else {
self.source.clone()
};
let source = other.source.clone().or_else(|| self.source.clone());
let mut functions = functions.as_ref().clone();
functions.merge_filtered(&other.functions, &mut filter);
@ -696,24 +691,68 @@ pub enum ReturnType {
Exception,
}
/// A type that wraps a [`HashMap`]`<u64, Stmt>` for the `switch` statement and implements [`Hash`].
#[derive(Clone)]
pub struct SwitchHashWrapper(HashMap<u64, Stmt, StraightHasherBuilder>);
impl From<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
fn from(value: HashMap<u64, Stmt, StraightHasherBuilder>) -> Self {
Self(value)
}
}
impl AsRef<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
fn as_ref(&self) -> &HashMap<u64, Stmt, StraightHasherBuilder> {
&self.0
}
}
impl AsMut<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
fn as_mut(&mut self) -> &mut HashMap<u64, Stmt, StraightHasherBuilder> {
&mut self.0
}
}
impl Deref for SwitchHashWrapper {
type Target = HashMap<u64, Stmt, StraightHasherBuilder>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SwitchHashWrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl fmt::Debug for SwitchHashWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl Hash for SwitchHashWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
let mut keys: Vec<_> = self.0.keys().collect();
keys.sort();
keys.into_iter().for_each(|key| {
key.hash(state);
self.0.get(&key).unwrap().hash(state);
});
}
}
/// _(INTERNALS)_ A statement.
/// Exported under the `internals` feature only.
///
/// # WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Hash)]
pub enum Stmt {
/// No-op.
Noop(Position),
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
Switch(
Expr,
Box<(HashMap<u64, Stmt, StraightHasherBuilder>, Option<Stmt>)>,
Position,
),
Switch(Expr, Box<(SwitchHashWrapper, Option<Stmt>)>, Position),
/// `while` expr `{` stmt `}`
While(Expr, Box<Stmt>, Position),
/// `do` `{` stmt `}` `while`|`until` expr
@ -899,10 +938,8 @@ impl Stmt {
/// # WARNING
///
/// This type is volatile and may change.
#[derive(Clone)]
#[derive(Clone, Hash)]
pub struct CustomExpr {
/// Implementation function.
pub func: Shared<FnCustomSyntaxEval>,
/// List of keywords.
pub keywords: StaticVec<Expr>,
/// List of tokens actually parsed.
@ -926,7 +963,7 @@ impl fmt::Debug for CustomExpr {
/// # WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Hash)]
pub struct BinaryExpr {
/// LHS expression.
pub lhs: Expr,
@ -940,7 +977,7 @@ pub struct BinaryExpr {
/// # WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, Hash)]
pub struct FnCallExpr {
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
/// None if native Rust only.
@ -959,13 +996,83 @@ pub struct FnCallExpr {
pub args: StaticVec<Expr>,
}
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
#[cfg(not(feature = "no_float"))]
#[derive(Clone, Copy)]
pub struct FloatWrapper(FLOAT);
#[cfg(not(feature = "no_float"))]
impl Hash for FloatWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.to_le_bytes().hash(state);
}
}
#[cfg(not(feature = "no_float"))]
impl AsRef<FLOAT> for FloatWrapper {
fn as_ref(&self) -> &FLOAT {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl AsMut<FLOAT> for FloatWrapper {
fn as_mut(&mut self) -> &mut FLOAT {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl Deref for FloatWrapper {
type Target = FLOAT;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(not(feature = "no_float"))]
impl DerefMut for FloatWrapper {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[cfg(not(feature = "no_float"))]
impl fmt::Debug for FloatWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(not(feature = "no_float"))]
impl fmt::Display for FloatWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(not(feature = "no_float"))]
impl From<FLOAT> for FloatWrapper {
fn from(value: FLOAT) -> Self {
Self::new(value)
}
}
#[cfg(not(feature = "no_float"))]
impl FloatWrapper {
pub const fn new(value: FLOAT) -> Self {
Self(value)
}
}
/// _(INTERNALS)_ An expression sub-tree.
/// Exported under the `internals` feature only.
///
/// # WARNING
///
/// This type is volatile and may change.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Hash)]
pub enum Expr {
/// Dynamic constant.
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning.
@ -977,7 +1084,7 @@ pub enum Expr {
IntegerConstant(INT, Position),
/// Floating-point constant.
#[cfg(not(feature = "no_float"))]
FloatConstant(FLOAT, Position),
FloatConstant(FloatWrapper, Position),
/// Character constant.
CharConstant(char, Position),
/// [String][ImmutableString] constant.
@ -1250,19 +1357,20 @@ mod tests {
/// This test is to make sure no code changes increase the sizes of critical data structures.
#[test]
fn check_struct_sizes() {
use std::mem::size_of;
use crate::stdlib::mem::size_of;
use crate::*;
assert_eq!(size_of::<crate::Dynamic>(), 16);
assert_eq!(size_of::<Option<crate::Dynamic>>(), 16);
assert_eq!(size_of::<crate::Position>(), 4);
assert_eq!(size_of::<crate::ast::Expr>(), 16);
assert_eq!(size_of::<Option<crate::ast::Expr>>(), 16);
assert_eq!(size_of::<crate::ast::Stmt>(), 32);
assert_eq!(size_of::<Option<crate::ast::Stmt>>(), 32);
assert_eq!(size_of::<crate::FnPtr>(), 32);
assert_eq!(size_of::<crate::Scope>(), 48);
assert_eq!(size_of::<crate::LexError>(), 56);
assert_eq!(size_of::<crate::ParseError>(), 16);
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
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>(), 32);
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
assert_eq!(size_of::<FnPtr>(), 32);
assert_eq!(size_of::<Scope>(), 48);
assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16);
assert_eq!(size_of::<EvalAltResult>(), 72);
}
}

View File

@ -7,14 +7,13 @@ use crate::stdlib::{
boxed::Box,
fmt,
hash::{Hash, Hasher},
mem,
ops::{Deref, DerefMut},
string::String,
};
use crate::{FnPtr, ImmutableString, INT};
#[cfg(not(feature = "no_float"))]
use crate::FLOAT;
use crate::{ast::FloatWrapper, FLOAT};
#[cfg(not(feature = "no_index"))]
use crate::Array;
@ -155,7 +154,7 @@ pub enum Union {
Char(char, AccessMode),
Int(INT, AccessMode),
#[cfg(not(feature = "no_float"))]
Float(FLOAT, AccessMode),
Float(FloatWrapper, AccessMode),
#[cfg(not(feature = "no_index"))]
Array(Box<Array>, AccessMode),
#[cfg(not(feature = "no_object"))]
@ -362,8 +361,6 @@ impl Dynamic {
impl Hash for Dynamic {
fn hash<H: Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
match &self.0 {
Union::Unit(_, _) => ().hash(state),
Union::Bool(value, _) => value.hash(state),
@ -371,7 +368,7 @@ impl Hash for Dynamic {
Union::Char(ch, _) => ch.hash(state),
Union::Int(i, _) => i.hash(state),
#[cfg(not(feature = "no_float"))]
Union::Float(f, _) => f.to_le_bytes().hash(state),
Union::Float(f, _) => f.hash(state),
#[cfg(not(feature = "no_index"))]
Union::Array(a, _) => (**a).hash(state),
#[cfg(not(feature = "no_object"))]
@ -559,13 +556,16 @@ impl Dynamic {
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, AccessMode::ReadWrite));
/// A [`Dynamic`] containing the floating-point zero.
#[cfg(not(feature = "no_float"))]
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0, AccessMode::ReadWrite));
pub const FLOAT_ZERO: Dynamic =
Self(Union::Float(FloatWrapper::new(0.0), AccessMode::ReadWrite));
/// A [`Dynamic`] containing the floating-point one.
#[cfg(not(feature = "no_float"))]
pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0, AccessMode::ReadWrite));
pub const FLOAT_ONE: Dynamic =
Self(Union::Float(FloatWrapper::new(1.0), AccessMode::ReadWrite));
/// A [`Dynamic`] containing the floating-point negative one.
#[cfg(not(feature = "no_float"))]
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0, AccessMode::ReadWrite));
pub const FLOAT_NEGATIVE_ONE: Dynamic =
Self(Union::Float(FloatWrapper::new(-1.0), AccessMode::ReadWrite));
/// Get the [`AccessMode`] for this [`Dynamic`].
pub(crate) fn access_mode(&self) -> AccessMode {
@ -836,7 +836,7 @@ impl Dynamic {
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return match self.0 {
Union::Float(value, _) => unsafe_try_cast(value),
Union::Float(value, _) => unsafe_try_cast(*value),
_ => None,
};
}
@ -1105,7 +1105,7 @@ impl Dynamic {
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return match &self.0 {
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value),
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
_ => None,
};
}
@ -1194,7 +1194,7 @@ impl Dynamic {
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return match &mut self.0 {
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value),
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
_ => None,
};
}
@ -1277,7 +1277,7 @@ impl Dynamic {
#[inline(always)]
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
match self.0 {
Union::Float(n, _) => Ok(n),
Union::Float(n, _) => Ok(*n),
#[cfg(not(feature = "no_closure"))]
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
_ => Err(self.type_name()),
@ -1380,6 +1380,13 @@ impl From<INT> for Dynamic {
impl From<FLOAT> for Dynamic {
#[inline(always)]
fn from(value: FLOAT) -> Self {
Self(Union::Float(value.into(), AccessMode::ReadWrite))
}
}
#[cfg(not(feature = "no_float"))]
impl From<FloatWrapper> for Dynamic {
#[inline(always)]
fn from(value: FloatWrapper) -> Self {
Self(Union::Float(value, AccessMode::ReadWrite))
}
}

View File

@ -1923,6 +1923,10 @@ impl Engine {
.iter()
.map(Into::into)
.collect::<StaticVec<_>>();
let custom_def = self
.custom_syntax
.get(custom.tokens.first().unwrap())
.unwrap();
let mut context = EvalContext {
engine: self,
scope,
@ -1932,7 +1936,7 @@ impl Engine {
this_ptr,
level,
};
(custom.func)(&mut context, &expressions)
(custom_def.func)(&mut context, &expressions)
}
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
@ -2072,11 +2076,7 @@ impl Engine {
let args = &mut [lhs_ptr_inner, &mut rhs_val];
// Overriding exact implementation
let source = if source.is_none() {
state.source.as_ref()
} else {
source
};
let source = source.or_else(|| state.source.as_ref());
if func.is_plugin_fn() {
func.get_plugin_fn()
.call((self, source, &*mods, lib).into(), args)?;
@ -2130,14 +2130,13 @@ impl Engine {
&mut rhs_val,
];
let result = self
.exec_fn_call(
Some(
self.exec_fn_call(
mods, state, lib, op, None, args, false, false, false, *op_pos, None,
None, level,
)
.map(|(v, _)| v)?;
Some((result, rhs_expr.position()))
.map(|(v, _)| (v, rhs_expr.position()))?,
)
};
// Must be either `var[index] op= val` or `var.prop op= val`

View File

@ -8,11 +8,9 @@ use crate::stdlib::{
any::{type_name, TypeId},
boxed::Box,
format,
hash::{Hash, Hasher},
string::String,
vec::Vec,
};
use crate::utils::get_hasher;
use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
ParseError, Position, Shared, AST,
@ -24,13 +22,6 @@ use crate::Array;
#[cfg(not(feature = "no_object"))]
use crate::Map;
/// Calculate a unique hash for a script.
fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator<Item = &'a &'a str>) -> u64 {
let s = &mut get_hasher();
scripts.into_iter().for_each(|&script| script.hash(s));
s.finish()
}
/// Engine public API
impl Engine {
/// Register a function of the [`Engine`].
@ -960,9 +951,8 @@ impl Engine {
scripts: &[&str],
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let hash = calc_hash_for_scripts(scripts);
let stream = self.lex(scripts);
self.parse(hash, &mut stream.peekable(), scope, optimization_level)
self.parse(&mut stream.peekable(), scope, optimization_level)
}
/// Read the contents of a file into a string.
#[cfg(not(feature = "no_std"))]
@ -1123,8 +1113,6 @@ impl Engine {
.into());
};
let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex_with_map(
&scripts,
if has_null {
@ -1138,12 +1126,8 @@ impl Engine {
},
);
let ast = self.parse_global_expr(
hash,
&mut stream.peekable(),
&scope,
OptimizationLevel::None,
)?;
let ast =
self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
// Handle null - map to ()
if has_null {
@ -1222,11 +1206,10 @@ impl Engine {
script: &str,
) -> Result<AST, ParseError> {
let scripts = [script];
let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts);
let mut peekable = stream.peekable();
self.parse_global_expr(hash, &mut peekable, scope, self.optimization_level)
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
}
/// Evaluate a script file.
///
@ -1384,12 +1367,10 @@ impl Engine {
script: &str,
) -> Result<T, Box<EvalAltResult>> {
let scripts = [script];
let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts);
// No need to optimize a lone expression
let ast =
self.parse_global_expr(hash, &mut stream.peekable(), scope, OptimizationLevel::None)?;
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
self.eval_ast_with_scope(scope, &ast)
}
@ -1522,9 +1503,8 @@ impl Engine {
script: &str,
) -> Result<(), Box<EvalAltResult>> {
let scripts = [script];
let hash = calc_hash_for_scripts(&scripts);
let stream = self.lex(&scripts);
let ast = self.parse(hash, &mut stream.peekable(), scope, self.optimization_level)?;
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast)
}
/// Evaluate an AST, but throw away the result and only return error (if any).

View File

@ -183,7 +183,10 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
#[cfg(feature = "internals")]
#[deprecated = "this type is volatile and may change"]
pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
pub use ast::{
BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt,
SwitchHashWrapper,
};
#[cfg(feature = "internals")]
#[deprecated = "this type is volatile and may change"]

View File

@ -41,8 +41,6 @@ type FunctionsLib = HashMap<NonZeroU64, ScriptFnDef, StraightHasherBuilder>;
struct ParseState<'e> {
/// Reference to the scripting [`Engine`].
engine: &'e Engine,
/// Hash that uniquely identifies a script.
script_hash: u64,
/// Interned strings.
strings: HashMap<String, ImmutableString>,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
@ -75,7 +73,6 @@ impl<'e> ParseState<'e> {
#[inline(always)]
pub fn new(
engine: &'e Engine,
script_hash: u64,
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
@ -83,7 +80,6 @@ impl<'e> ParseState<'e> {
) -> Self {
Self {
engine,
script_hash,
#[cfg(not(feature = "unchecked"))]
max_expr_depth,
#[cfg(not(feature = "unchecked"))]
@ -919,7 +915,7 @@ fn parse_switch(
Ok(Stmt::Switch(
item,
Box::new((final_table, def_stmt)),
Box::new((final_table.into(), def_stmt)),
settings.pos,
))
}
@ -956,7 +952,7 @@ fn parse_primary(
},
#[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => {
let x = *x;
let x = (*x).into();
input.next().unwrap();
Expr::FloatConstant(x, settings.pos)
}
@ -986,7 +982,6 @@ fn parse_primary(
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
let mut new_state = ParseState::new(
state.engine,
state.script_hash,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth,
#[cfg(not(feature = "unchecked"))]
@ -1284,7 +1279,7 @@ fn parse_unary(
.map(|i| Expr::IntegerConstant(i, pos))
.or_else(|| {
#[cfg(not(feature = "no_float"))]
return Some(Expr::FloatConstant(-(num as FLOAT), pos));
return Some(Expr::FloatConstant((-(num as FLOAT)).into(), pos));
#[cfg(feature = "no_float")]
return None;
})
@ -1292,7 +1287,7 @@ fn parse_unary(
// Negative float
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x, pos) => Ok(Expr::FloatConstant(-x, pos)),
Expr::FloatConstant(x, pos) => Ok(Expr::FloatConstant((-(*x)).into(), pos)),
// Call negative function
expr => {
@ -1998,14 +1993,7 @@ fn parse_custom_syntax(
}
}
Ok(Expr::Custom(
Box::new(CustomExpr {
keywords,
func: syntax.func.clone(),
tokens,
}),
pos,
))
Ok(Expr::Custom(Box::new(CustomExpr { keywords, tokens }), pos))
}
/// Parse an expression.
@ -2600,7 +2588,6 @@ fn parse_stmt(
(Token::Fn, pos) => {
let mut new_state = ParseState::new(
state.engine,
state.script_hash,
#[cfg(not(feature = "unchecked"))]
state.max_function_expr_depth,
#[cfg(not(feature = "unchecked"))]
@ -3009,10 +2996,10 @@ fn parse_anon_fn(
params.into_iter().map(|(v, _)| v).collect()
};
// Create unique function name by hashing the script hash plus the position
// Create unique function name by hashing the script body plus the parameters.
let hasher = &mut get_hasher();
state.script_hash.hash(hasher);
settings.pos.hash(hasher);
params.iter().for_each(|p| p.as_str().hash(hasher));
body.hash(hasher);
let hash = hasher.finish();
let fn_name: ImmutableString = format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash).into();
@ -3045,7 +3032,6 @@ fn parse_anon_fn(
impl Engine {
pub(crate) fn parse_global_expr(
&self,
script_hash: u64,
input: &mut TokenStream,
scope: &Scope,
optimization_level: OptimizationLevel,
@ -3053,7 +3039,6 @@ impl Engine {
let mut functions = Default::default();
let mut state = ParseState::new(
self,
script_hash,
#[cfg(not(feature = "unchecked"))]
self.max_expr_depth(),
#[cfg(not(feature = "unchecked"))]
@ -3095,14 +3080,12 @@ impl Engine {
/// Parse the global level statements.
fn parse_global_level(
&self,
script_hash: u64,
input: &mut TokenStream,
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
let mut statements = Vec::with_capacity(16);
let mut functions = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
let mut state = ParseState::new(
self,
script_hash,
#[cfg(not(feature = "unchecked"))]
self.max_expr_depth(),
#[cfg(not(feature = "unchecked"))]
@ -3165,12 +3148,11 @@ impl Engine {
#[inline(always)]
pub(crate) fn parse(
&self,
script_hash: u64,
input: &mut TokenStream,
scope: &Scope,
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let (statements, lib) = self.parse_global_level(script_hash, input)?;
let (statements, lib) = self.parse_global_level(input)?;
Ok(
// Optimize AST