Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Extract the parser into a crate #2409

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"boa_cli",
"boa_engine",
"boa_ast",
"boa_parser",
"boa_gc",
"boa_interner",
"boa_profiler",
Expand Down Expand Up @@ -30,6 +31,7 @@ boa_profiler = { version = "0.16.0", path = "boa_profiler" }
boa_unicode = { version = "0.16.0", path = "boa_unicode" }
boa_macros = { version = "0.16.0", path = "boa_macros" }
boa_ast = { version = "0.16.0", path = "boa_ast" }
boa_parser = { version = "0.16.0", path = "boa_parser" }

[workspace.metadata.workspaces]
allow_branch = "main"
Expand Down
1 change: 1 addition & 0 deletions boa_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ rust-version.workspace = true
boa_engine = { workspace = true, features = ["deser", "console"] }
boa_ast = { workspace = true, features = ["serde"]}
boa_interner.workspace = true
boa_parser.workspace = true
rustyline = "10.0.0"
rustyline-derive = "0.7.0"
clap = { version = "4.0.18", features = ["derive"] }
Expand Down
6 changes: 2 additions & 4 deletions boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,9 @@ fn parse_tokens<S>(src: S, context: &mut Context) -> Result<StatementList, Strin
where
S: AsRef<[u8]>,
{
use boa_engine::syntax::parser::Parser;

let src_bytes = src.as_ref();
Parser::new(src_bytes)
.parse_all(context)
boa_parser::Parser::new(src_bytes)
.parse_all(context.interner_mut())
.map_err(|e| format!("ParsingError: {e}"))
}

Expand Down
1 change: 1 addition & 0 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ boa_gc.workspace = true
boa_profiler.workspace = true
boa_macros.workspace = true
boa_ast.workspace = true
boa_parser.workspace = true
gc = "0.4.1"
serde = { version = "1.0.147", features = ["derive", "rc"] }
serde_json = "1.0.87"
Expand Down
103 changes: 95 additions & 8 deletions boa_engine/src/builtins/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ use crate::{
error::JsNativeError,
object::FunctionBuilder,
property::Attribute,
Context, JsResult, JsValue,
Context, JsResult, JsString, JsValue,
};
use boa_ast::operations::{
contains, contains_arguments, top_level_var_declared_names, ContainsSymbol,
};
use boa_ast::operations::top_level_var_declared_names;
use boa_gc::Gc;
use boa_parser::Parser;
use boa_profiler::Profiler;

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -68,6 +71,17 @@ impl Eval {
mut strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
bitflags::bitflags! {
/// Flags used to throw early errors on invalid `eval` calls.
#[derive(Default)]
struct Flags: u8 {
const IN_FUNCTION = 0b0001;
const IN_METHOD = 0b0010;
const IN_DERIVED_CONSTRUCTOR = 0b0100;
const IN_CLASS_FIELD_INITIALIZER = 0b1000;
}
}

/// Possible actions that can be executed after exiting this function to restore the environment to its
/// original state.
#[derive(Debug)]
Expand All @@ -94,19 +108,92 @@ impl Eval {
debug_assert!(direct || !strict);

// 2. If Type(x) is not String, return x.
let Some(x) = x.as_string() else {
// TODO: rework parser to take an iterator of `u32` unicode codepoints
let Some(x) = x.as_string().map(JsString::to_std_string_escaped) else {
return Ok(x.clone());
};

// Because of implementation details the following code differs from the spec.
// TODO: rework parser to take an iterator of `u32` unicode codepoints

// Parse the script body and handle early errors (6 - 11)
let body = match context.parse_eval(x.to_std_string_escaped().as_bytes(), direct, strict) {
Ok(body) => body,
Err(e) => return Err(JsNativeError::syntax().with_message(e.to_string()).into()),
// 5. Perform ? HostEnsureCanCompileStrings(evalRealm).
let mut parser = Parser::new(x.as_bytes());
if strict {
parser.set_strict();
}
// 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:
// a. Let script be ParseText(StringToCodePoints(x), Script).
// b. If script is a List of errors, throw a SyntaxError exception.
// c. If script Contains ScriptBody is false, return undefined.
// d. Let body be the ScriptBody of script.
let body = parser.parse_eval(direct, context.interner_mut())?;

// 6. Let inFunction be false.
// 7. Let inMethod be false.
// 8. Let inDerivedConstructor be false.
// 9. Let inClassFieldInitializer be false.
// a. Let thisEnvRec be GetThisEnvironment().
let flags = match context
.realm
.environments
.get_this_environment()
.as_function_slots()
{
// 10. If direct is true, then
// b. If thisEnvRec is a Function Environment Record, then
Some(function_env) if direct => {
let function_env = function_env.borrow();
// i. Let F be thisEnvRec.[[FunctionObject]].
let function_object = function_env.function_object().borrow();

// ii. Set inFunction to true.
let mut flags = Flags::IN_FUNCTION;

// iii. Set inMethod to thisEnvRec.HasSuperBinding().
if function_env.has_super_binding() {
flags |= Flags::IN_METHOD;
}

// iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.
if function_object
.as_function()
.expect("must be function object")
.is_derived_constructor()
{
flags |= Flags::IN_DERIVED_CONSTRUCTOR;
}

// TODO:
// v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]].
// vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true.

flags
}
_ => Flags::default(),
};

if !flags.contains(Flags::IN_FUNCTION) && contains(&body, ContainsSymbol::NewTarget) {
return Err(JsNativeError::syntax()
.with_message("invalid `new.target` expression inside eval")
.into());
}
if !flags.contains(Flags::IN_METHOD) && contains(&body, ContainsSymbol::SuperProperty) {
return Err(JsNativeError::syntax()
.with_message("invalid `super` reference inside eval")
.into());
}
if !flags.contains(Flags::IN_DERIVED_CONSTRUCTOR)
&& contains(&body, ContainsSymbol::SuperCall)
{
return Err(JsNativeError::syntax()
.with_message("invalid `super` call inside eval")
.into());
}
if flags.contains(Flags::IN_CLASS_FIELD_INITIALIZER) && contains_arguments(&body) {
return Err(JsNativeError::syntax()
.with_message("invalid `arguments` reference inside eval")
.into());
}

strict |= body.strict();

// Because our environment model does not map directly to the spec, this section looks very different.
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use crate::{
property::{Attribute, PropertyDescriptor, PropertyKey},
string::utf16,
symbol::WellKnownSymbols,
syntax::Parser,
value::IntegerOrInfinity,
Context, JsResult, JsString, JsValue,
};
Expand All @@ -37,6 +36,7 @@ use boa_ast::{
};
use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
use boa_interner::Sym;
use boa_parser::Parser;
use boa_profiler::Profiler;
use dyn_clone::DynClone;
use std::{
Expand Down
2 changes: 1 addition & 1 deletion boa_engine/src/builtins/regexp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ use crate::{
property::{Attribute, PropertyDescriptorBuilder},
string::{utf16, CodePoint},
symbol::WellKnownSymbols,
syntax::lexer::regex::RegExpFlags,
value::JsValue,
Context, JsResult, JsString,
};
use boa_parser::lexer::regex::RegExpFlags;
use boa_profiler::Profiler;
use regress::Regex;
use std::str::FromStr;
Expand Down
23 changes: 3 additions & 20 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ use crate::{
object::{FunctionBuilder, GlobalPropertyMap, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
syntax::{parser::ParseError, Parser},
vm::{CallFrame, CodeBlock, FinallyReturn, GeneratorResumeKind, Vm},
JsResult, JsString, JsValue,
};

use boa_ast::StatementList;
use boa_gc::Gc;
use boa_interner::{Interner, Sym};
use boa_parser::{Error as ParseError, Parser};
use boa_profiler::Profiler;

#[cfg(feature = "intl")]
Expand Down Expand Up @@ -162,24 +162,7 @@ impl Context {
S: AsRef<[u8]>,
{
let mut parser = Parser::new(src.as_ref());
parser.parse_all(self)
}

/// Parse the given source text with eval specific handling.
pub(crate) fn parse_eval<S>(
&mut self,
src: S,
direct: bool,
strict: bool,
) -> Result<StatementList, ParseError>
where
S: AsRef<[u8]>,
{
let mut parser = Parser::new(src.as_ref());
if strict {
parser.set_strict();
}
parser.parse_eval(direct, self)
parser.parse_all(&mut self.interner)
}

/// `Call ( F, V [ , argumentsList ] )`
Expand Down Expand Up @@ -471,7 +454,7 @@ impl Context {
{
let main_timer = Profiler::global().start_event("Evaluation", "Main");

let statement_list = Parser::new(src.as_ref()).parse_all(self)?;
let statement_list = Parser::new(src.as_ref()).parse_all(&mut self.interner)?;

let code_block = self.compile(&statement_list)?;
let result = self.execute(code_block);
Expand Down
9 changes: 4 additions & 5 deletions boa_engine/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::{
object::JsObject,
object::ObjectData,
property::PropertyDescriptor,
syntax::parser,
Context, JsString, JsValue,
};
use boa_gc::{Finalize, Trace};
Expand Down Expand Up @@ -320,8 +319,8 @@ impl JsError {
}
}

impl From<parser::ParseError> for JsError {
fn from(err: parser::ParseError) -> Self {
impl From<boa_parser::Error> for JsError {
fn from(err: boa_parser::Error) -> Self {
Self::from(JsNativeError::from(err))
}
}
Expand Down Expand Up @@ -655,8 +654,8 @@ impl JsNativeError {
}
}

impl From<parser::ParseError> for JsNativeError {
fn from(err: parser::ParseError) -> Self {
impl From<boa_parser::Error> for JsNativeError {
fn from(err: boa_parser::Error) -> Self {
Self::syntax().with_message(err.to_string())
}
}
Expand Down
1 change: 0 additions & 1 deletion boa_engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ pub mod property;
pub mod realm;
pub mod string;
pub mod symbol;
pub mod syntax;
pub mod value;
pub mod vm;

Expand Down
8 changes: 0 additions & 8 deletions boa_engine/src/syntax/mod.rs

This file was deleted.

1 change: 1 addition & 0 deletions boa_examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ boa_engine = { workspace = true, features = ["console"] }
boa_ast.workspace = true
boa_interner.workspace = true
boa_gc.workspace = true
boa_parser.workspace = true
gc = "0.4.1"
5 changes: 3 additions & 2 deletions boa_examples/src/bin/commuter_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use boa_ast::{
visitor::{VisitWith, VisitorMut},
Expression,
};
use boa_engine::{syntax::Parser, Context};
use boa_engine::Context;
use boa_interner::ToInternedString;
use boa_parser::Parser;
use core::ops::ControlFlow;
use std::{convert::Infallible, fs::File, io::BufReader};

Expand Down Expand Up @@ -69,7 +70,7 @@ fn main() {
));
let mut ctx = Context::default();

let mut statements = parser.parse_all(&mut ctx).unwrap();
let mut statements = parser.parse_all(ctx.interner_mut()).unwrap();

let mut visitor = CommutorVisitor::default();

Expand Down
Loading