mirror of https://github.com/boa-dev/boa.git
Browse Source
New parser! Plus loads of tidy up in various places. Co-authored-by: Jason Williams <jwilliams720@bloomberg.net> Co-authored-by: HalidOdat <halidodat@gmail.com> Co-authored-by: Iban Eguia <iban.eguia@cern.ch> Co-authored-by: Iban Eguia <razican@protonmail.ch>pull/295/head
Jason Williams
5 years ago
committed by
GitHub
48 changed files with 3242 additions and 2175 deletions
@ -1,24 +1,7 @@
|
||||
FROM rust:latest |
||||
|
||||
WORKDIR /usr/src/myapp |
||||
COPY . . |
||||
|
||||
# LLDB Server |
||||
EXPOSE 9228 |
||||
|
||||
RUN apt-get -y update && \ |
||||
apt-get -y upgrade && \ |
||||
apt-get install -y sudo software-properties-common libpython2.7 |
||||
|
||||
# codelldb depends on libpython2.7 |
||||
# https://stackoverflow.com/questions/20842732/libpython2-7-so-1-0-cannot-open-shared-object-file-no-such-file-or-directory |
||||
|
||||
# https://askubuntu.com/questions/787383/how-to-install-llvm-3-9 |
||||
# http://apt.llvm.org/ |
||||
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - |
||||
RUN apt-add-repository "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-6.0 main" |
||||
RUN apt-get -y update |
||||
RUN sudo apt-get install -y lldb |
||||
|
||||
RUN apt-get -y update && apt-get -y upgrade |
||||
|
||||
CMD ["/bin/bash"] |
@ -1,246 +0,0 @@
|
||||
use crate::syntax::ast::{ |
||||
constant::Const, |
||||
op::{BinOp, Operator, UnaryOp}, |
||||
}; |
||||
use gc_derive::{Finalize, Trace}; |
||||
use std::{ |
||||
collections::btree_map::BTreeMap, |
||||
fmt::{Display, Formatter, Result}, |
||||
}; |
||||
|
||||
#[cfg(feature = "serde-ast")] |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Trace, Finalize, Debug, PartialEq)] |
||||
pub struct Expr { |
||||
/// The expression definition
|
||||
pub def: ExprDef, |
||||
} |
||||
|
||||
impl Expr { |
||||
/// Create a new expression with a starting and ending position
|
||||
pub fn new(def: ExprDef) -> Self { |
||||
Self { def } |
||||
} |
||||
} |
||||
|
||||
impl Display for Expr { |
||||
fn fmt(&self, f: &mut Formatter) -> Result { |
||||
write!(f, "{}", self.def) |
||||
} |
||||
} |
||||
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||
/// A Javascript Expression
|
||||
pub enum ExprDef { |
||||
/// Run a operation between 2 expressions
|
||||
BinOp(BinOp, Box<Expr>, Box<Expr>), |
||||
/// Run an operation on a value
|
||||
UnaryOp(UnaryOp, Box<Expr>), |
||||
/// Make a constant value
|
||||
Const(Const), |
||||
/// Const declaration
|
||||
ConstDecl(Vec<(String, Expr)>), |
||||
/// Construct an object from the function and arg{
|
||||
Construct(Box<Expr>, Vec<Expr>), |
||||
/// Run several expressions from top-to-bottom
|
||||
Block(Vec<Expr>), |
||||
/// Load a reference to a value, or a function argument
|
||||
Local(String), |
||||
/// Gets the constant field of a value
|
||||
GetConstField(Box<Expr>, String), |
||||
/// Gets the field of a value
|
||||
GetField(Box<Expr>, Box<Expr>), |
||||
/// Call a function with some values
|
||||
Call(Box<Expr>, Vec<Expr>), |
||||
/// Repeatedly run an expression while the conditional expression resolves to true
|
||||
WhileLoop(Box<Expr>, Box<Expr>), |
||||
/// Check if a conditional expression is true and run an expression if it is and another expression if it isn't
|
||||
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>), |
||||
/// Run blocks whose cases match the expression
|
||||
Switch(Box<Expr>, Vec<(Expr, Vec<Expr>)>, Option<Box<Expr>>), |
||||
/// Create an object out of the binary tree given
|
||||
ObjectDecl(Box<BTreeMap<String, Expr>>), |
||||
/// Create an array with items inside
|
||||
ArrayDecl(Vec<Expr>), |
||||
/// Create a function with the given name, arguments, and expression
|
||||
FunctionDecl(Option<String>, Vec<Expr>, Box<Expr>), |
||||
/// Create an arrow function with the given arguments and expression
|
||||
ArrowFunctionDecl(Vec<Expr>, Box<Expr>), |
||||
/// Return the expression from a function
|
||||
Return(Option<Box<Expr>>), |
||||
/// Throw a value
|
||||
Throw(Box<Expr>), |
||||
/// Assign an expression to a value
|
||||
Assign(Box<Expr>, Box<Expr>), |
||||
/// {
|
||||
/// A variable declaratio
|
||||
/// }
|
||||
VarDecl(Vec<(String, Option<Expr>)>), |
||||
/// Let declaraton
|
||||
LetDecl(Vec<(String, Option<Expr>)>), |
||||
/// Return a string representing the type of the given expression
|
||||
TypeOf(Box<Expr>), |
||||
} |
||||
|
||||
impl Operator for ExprDef { |
||||
fn get_assoc(&self) -> bool { |
||||
match *self { |
||||
ExprDef::Construct(_, _) |
||||
| ExprDef::UnaryOp(_, _) |
||||
| ExprDef::TypeOf(_) |
||||
| ExprDef::If(_, _, _) |
||||
| ExprDef::Assign(_, _) => false, |
||||
_ => true, |
||||
} |
||||
} |
||||
fn get_precedence(&self) -> u64 { |
||||
match self { |
||||
ExprDef::GetField(_, _) | ExprDef::GetConstField(_, _) => 1, |
||||
ExprDef::Call(_, _) | ExprDef::Construct(_, _) => 2, |
||||
ExprDef::UnaryOp(UnaryOp::IncrementPost, _) |
||||
| ExprDef::UnaryOp(UnaryOp::IncrementPre, _) |
||||
| ExprDef::UnaryOp(UnaryOp::DecrementPost, _) |
||||
| ExprDef::UnaryOp(UnaryOp::DecrementPre, _) => 3, |
||||
ExprDef::UnaryOp(UnaryOp::Not, _) |
||||
| ExprDef::UnaryOp(UnaryOp::Tilde, _) |
||||
| ExprDef::UnaryOp(UnaryOp::Minus, _) |
||||
| ExprDef::TypeOf(_) => 4, |
||||
ExprDef::BinOp(op, _, _) => op.get_precedence(), |
||||
ExprDef::If(_, _, _) => 15, |
||||
// 16 should be yield
|
||||
ExprDef::Assign(_, _) => 17, |
||||
_ => 19, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Display for ExprDef { |
||||
fn fmt(&self, f: &mut Formatter) -> Result { |
||||
match *self { |
||||
ExprDef::Const(ref c) => write!(f, "{}", c), |
||||
ExprDef::Block(ref block) => { |
||||
write!(f, "{{")?; |
||||
for expr in block.iter() { |
||||
write!(f, "{};", expr)?; |
||||
} |
||||
write!(f, "}}") |
||||
} |
||||
ExprDef::Local(ref s) => write!(f, "{}", s), |
||||
ExprDef::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field), |
||||
ExprDef::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field), |
||||
ExprDef::Call(ref ex, ref args) => { |
||||
write!(f, "{}(", ex)?; |
||||
let arg_strs: Vec<String> = args.iter().map(ToString::to_string).collect(); |
||||
write!(f, "{})", arg_strs.join(",")) |
||||
} |
||||
ExprDef::Construct(ref func, ref args) => { |
||||
f.write_fmt(format_args!("new {}", func))?; |
||||
f.write_str("(")?; |
||||
let mut first = true; |
||||
for e in args.iter() { |
||||
if !first { |
||||
f.write_str(", ")?; |
||||
} |
||||
first = false; |
||||
Display::fmt(e, f)?; |
||||
} |
||||
f.write_str(")") |
||||
} |
||||
ExprDef::WhileLoop(ref cond, ref expr) => write!(f, "while({}) {}", cond, expr), |
||||
ExprDef::If(ref cond, ref expr, None) => write!(f, "if({}) {}", cond, expr), |
||||
ExprDef::If(ref cond, ref expr, Some(ref else_e)) => { |
||||
write!(f, "if({}) {} else {}", cond, expr, else_e) |
||||
} |
||||
ExprDef::Switch(ref val, ref vals, None) => { |
||||
f.write_fmt(format_args!("switch({})", val))?; |
||||
f.write_str(" {")?; |
||||
for e in vals.iter() { |
||||
f.write_fmt(format_args!("case {}: \n", e.0))?; |
||||
join_expr(f, &e.1)?; |
||||
} |
||||
f.write_str("}") |
||||
} |
||||
ExprDef::Switch(ref val, ref vals, Some(ref def)) => { |
||||
f.write_fmt(format_args!("switch({})", val))?; |
||||
f.write_str(" {")?; |
||||
for e in vals.iter() { |
||||
f.write_fmt(format_args!("case {}: \n", e.0))?; |
||||
join_expr(f, &e.1)?; |
||||
} |
||||
f.write_str("default: \n")?; |
||||
Display::fmt(def, f)?; |
||||
f.write_str("}") |
||||
} |
||||
ExprDef::ObjectDecl(ref map) => { |
||||
f.write_str("{")?; |
||||
for (key, value) in map.iter() { |
||||
f.write_fmt(format_args!("{}: {},", key, value))?; |
||||
} |
||||
f.write_str("}") |
||||
} |
||||
ExprDef::ArrayDecl(ref arr) => { |
||||
f.write_str("[")?; |
||||
join_expr(f, arr)?; |
||||
f.write_str("]") |
||||
} |
||||
ExprDef::FunctionDecl(ref name, ref args, ref expr) => { |
||||
write!(f, "function ")?; |
||||
if let Some(func_name) = name { |
||||
f.write_fmt(format_args!("{}", func_name))?; |
||||
} |
||||
write!(f, "{{")?; |
||||
join_expr(f, args)?; |
||||
write!(f, "}} {}", expr) |
||||
} |
||||
ExprDef::ArrowFunctionDecl(ref args, ref expr) => { |
||||
write!(f, "(")?; |
||||
join_expr(f, args)?; |
||||
write!(f, ") => {}", expr) |
||||
} |
||||
ExprDef::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), |
||||
ExprDef::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a), |
||||
ExprDef::Return(Some(ref ex)) => write!(f, "return {}", ex), |
||||
ExprDef::Return(None) => write!(f, "return"), |
||||
ExprDef::Throw(ref ex) => write!(f, "throw {}", ex), |
||||
ExprDef::Assign(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), |
||||
ExprDef::VarDecl(ref vars) | ExprDef::LetDecl(ref vars) => { |
||||
if let ExprDef::VarDecl(_) = *self { |
||||
f.write_str("var ")?; |
||||
} else { |
||||
f.write_str("let ")?; |
||||
} |
||||
for (key, val) in vars.iter() { |
||||
match val { |
||||
Some(x) => f.write_fmt(format_args!("{} = {}", key, x))?, |
||||
None => f.write_fmt(format_args!("{}", key))?, |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
ExprDef::ConstDecl(ref vars) => { |
||||
f.write_str("const ")?; |
||||
for (key, val) in vars.iter() { |
||||
f.write_fmt(format_args!("{} = {}", key, val))? |
||||
} |
||||
Ok(()) |
||||
} |
||||
ExprDef::TypeOf(ref e) => write!(f, "typeof {}", e), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// `join_expr` - Utility to join multiple Expressions into a single string
|
||||
fn join_expr(f: &mut Formatter, expr: &[Expr]) -> Result { |
||||
let mut first = true; |
||||
for e in expr.iter() { |
||||
if !first { |
||||
f.write_str(", ")?; |
||||
} |
||||
first = false; |
||||
Display::fmt(e, f)?; |
||||
} |
||||
Ok(()) |
||||
} |
@ -0,0 +1,372 @@
|
||||
use crate::syntax::ast::{ |
||||
constant::Const, |
||||
op::{BinOp, Operator, UnaryOp}, |
||||
}; |
||||
use gc_derive::{Finalize, Trace}; |
||||
use std::fmt; |
||||
|
||||
#[cfg(feature = "serde-ast")] |
||||
use serde::{Deserialize, Serialize}; |
||||
|
||||
/// A Javascript AST Node.
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, Trace, Finalize, PartialEq)] |
||||
pub enum Node { |
||||
/// Create an array with items inside.
|
||||
ArrayDecl(Vec<Node>), |
||||
/// Create an arrow function with the given arguments and internal AST node.
|
||||
ArrowFunctionDecl(Vec<FormalParameter>, Box<Node>), |
||||
/// Assign an AST node result to an AST node.
|
||||
Assign(Box<Node>, Box<Node>), |
||||
/// Run an operation between 2 AST nodes.
|
||||
BinOp(BinOp, Box<Node>, Box<Node>), |
||||
/// Run several AST nodes from top-to-bottom.
|
||||
Block(Vec<Node>), |
||||
/// Break statement with an optional label.
|
||||
Break(Option<String>), |
||||
/// Call a function with some values.
|
||||
Call(Box<Node>, Vec<Node>), |
||||
/// Conditional Operator (`{condition} ? {if true} : {if false}`).
|
||||
ConditionalOp(Box<Node>, Box<Node>, Box<Node>), |
||||
/// Make a constant value.
|
||||
Const(Const), |
||||
/// Const declaration.
|
||||
ConstDecl(Vec<(String, Node)>), |
||||
/// Continue with an optional label.
|
||||
Continue(Option<String>), |
||||
/// Create a function with the given name, arguments, and internal AST node.
|
||||
FunctionDecl(Option<String>, Vec<FormalParameter>, Box<Node>), |
||||
/// Gets the constant field of a value.
|
||||
GetConstField(Box<Node>, String), |
||||
/// Gets the [field] of a value.
|
||||
GetField(Box<Node>, Box<Node>), |
||||
/// [init], [cond], [step], body
|
||||
ForLoop( |
||||
Option<Box<Node>>, |
||||
Option<Box<Node>>, |
||||
Option<Box<Node>>, |
||||
Box<Node>, |
||||
), |
||||
/// Check if a conditional expression is true and run an expression if it is and another expression if it isn't
|
||||
If(Box<Node>, Box<Node>, Option<Box<Node>>), |
||||
/// Let declaraton
|
||||
LetDecl(Vec<(String, Option<Node>)>), |
||||
/// Load a reference to a value, or a function argument
|
||||
Local(String), |
||||
/// New
|
||||
New(Box<Node>), |
||||
/// Object Declaration
|
||||
Object(Vec<PropertyDefinition>), |
||||
/// Return the expression from a function
|
||||
Return(Option<Box<Node>>), |
||||
/// Run blocks whose cases match the expression
|
||||
Switch(Box<Node>, Vec<(Node, Vec<Node>)>, Option<Box<Node>>), |
||||
/// `...a` - spread an iterable value
|
||||
Spread(Box<Node>), |
||||
// Similar to Block but without the braces
|
||||
StatementList(Vec<Node>), |
||||
/// Throw a value
|
||||
Throw(Box<Node>), |
||||
/// Return a string representing the type of the given expression
|
||||
TypeOf(Box<Node>), |
||||
/// Try / Catch
|
||||
Try( |
||||
Box<Node>, |
||||
Option<Box<Node>>, |
||||
Option<Box<Node>>, |
||||
Option<Box<Node>>, |
||||
), |
||||
/// The JavaScript `this` keyword refers to the object it belongs to.
|
||||
///
|
||||
/// A property of an execution context (global, function or eval) that,
|
||||
/// in non–strict mode, is always a reference to an object and in strict
|
||||
/// mode can be any value.
|
||||
///
|
||||
/// For more information, please check: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this>
|
||||
This, |
||||
/// Run an operation on a value
|
||||
UnaryOp(UnaryOp, Box<Node>), |
||||
/// A variable declaration
|
||||
VarDecl(Vec<(String, Option<Node>)>), |
||||
/// Repeatedly run an expression while the conditional expression resolves to true
|
||||
WhileLoop(Box<Node>, Box<Node>), |
||||
} |
||||
|
||||
impl Operator for Node { |
||||
fn get_assoc(&self) -> bool { |
||||
match *self { |
||||
Node::UnaryOp(_, _) | Node::TypeOf(_) | Node::If(_, _, _) | Node::Assign(_, _) => false, |
||||
_ => true, |
||||
} |
||||
} |
||||
fn get_precedence(&self) -> u64 { |
||||
match self { |
||||
Node::GetField(_, _) | Node::GetConstField(_, _) => 1, |
||||
Node::Call(_, _) => 2, |
||||
Node::UnaryOp(UnaryOp::IncrementPost, _) |
||||
| Node::UnaryOp(UnaryOp::IncrementPre, _) |
||||
| Node::UnaryOp(UnaryOp::DecrementPost, _) |
||||
| Node::UnaryOp(UnaryOp::DecrementPre, _) => 3, |
||||
Node::UnaryOp(UnaryOp::Not, _) |
||||
| Node::UnaryOp(UnaryOp::Tilde, _) |
||||
| Node::UnaryOp(UnaryOp::Minus, _) |
||||
| Node::TypeOf(_) => 4, |
||||
Node::BinOp(op, _, _) => op.get_precedence(), |
||||
Node::If(_, _, _) => 15, |
||||
// 16 should be yield
|
||||
Node::Assign(_, _) => 17, |
||||
_ => 19, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for Node { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
self.display(f, 0) |
||||
} |
||||
} |
||||
|
||||
impl Node { |
||||
/// Implements the display formatting with indentation.
|
||||
fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { |
||||
let indent = " ".repeat(indentation); |
||||
match *self { |
||||
Self::Block(_) => {} |
||||
_ => write!(f, "{}", indent)?, |
||||
} |
||||
|
||||
match *self { |
||||
Self::Const(ref c) => write!(f, "{}", c), |
||||
Self::ConditionalOp(_, _, _) => write!(f, "Conditional op"), // TODO
|
||||
Self::ForLoop(_, _, _, _) => write!(f, "for loop"), // TODO
|
||||
Self::This => write!(f, "this"), // TODO
|
||||
Self::Try(_, _, _, _) => write!(f, "try/catch/finally"), // TODO
|
||||
Self::Break(_) => write!(f, "break"), // TODO: add potential value
|
||||
Self::Continue(_) => write!(f, "continue"), // TODO: add potential value
|
||||
Self::Spread(ref node) => write!(f, "...{}", node), |
||||
Self::Block(ref block) => { |
||||
writeln!(f, "{{")?; |
||||
for node in block.iter() { |
||||
node.display(f, indentation + 1)?; |
||||
|
||||
match node { |
||||
Self::Block(_) |
||||
| Self::If(_, _, _) |
||||
| Self::Switch(_, _, _) |
||||
| Self::FunctionDecl(_, _, _) |
||||
| Self::WhileLoop(_, _) |
||||
| Self::StatementList(_) => {} |
||||
_ => write!(f, ";")?, |
||||
} |
||||
writeln!(f)?; |
||||
} |
||||
write!(f, "{}}}", indent) |
||||
} |
||||
Node::StatementList(ref list) => { |
||||
for node in list.iter() { |
||||
node.display(f, indentation + 1)?; |
||||
|
||||
match node { |
||||
Self::Block(_) |
||||
| Self::If(_, _, _) |
||||
| Self::Switch(_, _, _) |
||||
| Self::FunctionDecl(_, _, _) |
||||
| Self::WhileLoop(_, _) |
||||
| Self::StatementList(_) => {} |
||||
_ => write!(f, ";")?, |
||||
} |
||||
writeln!(f)?; |
||||
} |
||||
Ok(()) |
||||
} |
||||
Self::Local(ref s) => write!(f, "{}", s), |
||||
Self::GetConstField(ref ex, ref field) => write!(f, "{}.{}", ex, field), |
||||
Self::GetField(ref ex, ref field) => write!(f, "{}[{}]", ex, field), |
||||
Self::Call(ref ex, ref args) => { |
||||
write!(f, "{}(", ex)?; |
||||
let arg_strs: Vec<String> = args.iter().map(ToString::to_string).collect(); |
||||
write!(f, "{})", arg_strs.join(", ")) |
||||
} |
||||
Self::New(ref call) => { |
||||
let (func, args) = match call.as_ref() { |
||||
Node::Call(func, args) => (func, args), |
||||
_ => unreachable!("Node::New(ref call): 'call' must only be Node::Call type."), |
||||
}; |
||||
|
||||
write!(f, "new {}", func)?; |
||||
f.write_str("(")?; |
||||
let mut first = true; |
||||
for e in args.iter() { |
||||
if !first { |
||||
f.write_str(", ")?; |
||||
} |
||||
first = false; |
||||
write!(f, "{}", e)?; |
||||
} |
||||
f.write_str(")") |
||||
} |
||||
Self::WhileLoop(ref cond, ref node) => { |
||||
write!(f, "while ({}) ", cond)?; |
||||
node.display(f, indentation) |
||||
} |
||||
Self::If(ref cond, ref node, None) => { |
||||
write!(f, "if ({}) ", cond)?; |
||||
node.display(f, indentation) |
||||
} |
||||
Self::If(ref cond, ref node, Some(ref else_e)) => { |
||||
write!(f, "if ({}) ", cond)?; |
||||
node.display(f, indentation)?; |
||||
f.write_str(" else ")?; |
||||
else_e.display(f, indentation) |
||||
} |
||||
Self::Switch(ref val, ref vals, None) => { |
||||
writeln!(f, "switch ({}) {{", val)?; |
||||
for e in vals.iter() { |
||||
writeln!(f, "{}case {}:", indent, e.0)?; |
||||
join_nodes(f, &e.1)?; |
||||
} |
||||
writeln!(f, "{}}}", indent) |
||||
} |
||||
Self::Switch(ref val, ref vals, Some(ref def)) => { |
||||
writeln!(f, "switch ({}) {{", val)?; |
||||
for e in vals.iter() { |
||||
writeln!(f, "{}case {}:", indent, e.0)?; |
||||
join_nodes(f, &e.1)?; |
||||
} |
||||
writeln!(f, "{}default:", indent)?; |
||||
def.display(f, indentation + 1)?; |
||||
write!(f, "{}}}", indent) |
||||
} |
||||
Self::Object(ref properties) => { |
||||
f.write_str("{\n")?; |
||||
for property in properties { |
||||
match property { |
||||
PropertyDefinition::IdentifierReference(key) => { |
||||
write!(f, "{} {},", indent, key)?; |
||||
} |
||||
PropertyDefinition::Property(key, value) => { |
||||
write!(f, "{} {}: {},", indent, key, value)?; |
||||
} |
||||
PropertyDefinition::SpreadObject(key) => { |
||||
write!(f, "{} ...{},", indent, key)?; |
||||
} |
||||
PropertyDefinition::MethodDefinition(_kind, _key, _node) => { |
||||
// TODO: Implement display for PropertyDefinition::MethodDefinition.
|
||||
unimplemented!("Display for PropertyDefinition::MethodDefinition"); |
||||
} |
||||
} |
||||
} |
||||
f.write_str("}") |
||||
} |
||||
Self::ArrayDecl(ref arr) => { |
||||
f.write_str("[")?; |
||||
join_nodes(f, arr)?; |
||||
f.write_str("]") |
||||
} |
||||
Self::FunctionDecl(ref name, ref _args, ref node) => { |
||||
write!(f, "function ")?; |
||||
if let Some(func_name) = name { |
||||
write!(f, "{}", func_name)?; |
||||
} |
||||
write!(f, "{{")?; |
||||
//join_nodes(f, args)?; TODO: port
|
||||
f.write_str("} ")?; |
||||
node.display(f, indentation + 1) |
||||
} |
||||
Self::ArrowFunctionDecl(ref _args, ref node) => { |
||||
write!(f, "(")?; |
||||
//join_nodes(f, args)?; TODO: port
|
||||
f.write_str(") => ")?; |
||||
node.display(f, indentation) |
||||
} |
||||
Self::BinOp(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), |
||||
Self::UnaryOp(ref op, ref a) => write!(f, "{}{}", op, a), |
||||
Self::Return(Some(ref ex)) => write!(f, "return {}", ex), |
||||
Self::Return(None) => write!(f, "return"), |
||||
Self::Throw(ref ex) => write!(f, "throw {}", ex), |
||||
Self::Assign(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), |
||||
Self::VarDecl(ref vars) | Self::LetDecl(ref vars) => { |
||||
if let Self::VarDecl(_) = *self { |
||||
f.write_str("var ")?; |
||||
} else { |
||||
f.write_str("let ")?; |
||||
} |
||||
for (key, val) in vars.iter() { |
||||
match val { |
||||
Some(x) => write!(f, "{} = {}", key, x)?, |
||||
None => write!(f, "{}", key)?, |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
Self::ConstDecl(ref vars) => { |
||||
f.write_str("const ")?; |
||||
for (key, val) in vars.iter() { |
||||
write!(f, "{} = {}", key, val)? |
||||
} |
||||
Ok(()) |
||||
} |
||||
Self::TypeOf(ref e) => write!(f, "typeof {}", e), |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Utility to join multiple Nodes into a single string.
|
||||
fn join_nodes(f: &mut fmt::Formatter<'_>, nodes: &[Node]) -> fmt::Result { |
||||
let mut first = true; |
||||
for e in nodes { |
||||
if !first { |
||||
f.write_str(", ")?; |
||||
} |
||||
first = false; |
||||
write!(f, "{}", e)?; |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
/// "Formal parameter" is a fancy way of saying "function parameter".
|
||||
///
|
||||
/// In the declaration of a function, the parameters must be identifiers,
|
||||
/// not any value like numbers, strings, or objects.
|
||||
///```javascript
|
||||
///function foo(formalParametar1, formalParametar2) {
|
||||
///}
|
||||
///```
|
||||
/// For more information, please check <https://tc39.es/ecma262/#prod-FormalParameter>
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] |
||||
pub struct FormalParameter { |
||||
pub name: String, |
||||
pub init: Option<Box<Node>>, |
||||
pub is_rest_param: bool, |
||||
} |
||||
|
||||
pub type FormalParameters = Vec<FormalParameter>; |
||||
|
||||
impl FormalParameter { |
||||
pub fn new(name: String, init: Option<Box<Node>>, is_rest_param: bool) -> FormalParameter { |
||||
FormalParameter { |
||||
name, |
||||
init, |
||||
is_rest_param, |
||||
} |
||||
} |
||||
} |
||||
|
||||
// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] |
||||
pub enum PropertyDefinition { |
||||
IdentifierReference(String), |
||||
Property(String, Node), |
||||
MethodDefinition(MethodDefinitionKind, String, Node), |
||||
SpreadObject(Node), |
||||
} |
||||
|
||||
#[cfg_attr(feature = "serde-ast", derive(Serialize, Deserialize))] |
||||
#[derive(Clone, Debug, PartialEq, Trace, Finalize)] |
||||
pub enum MethodDefinitionKind { |
||||
Get, |
||||
Set, |
||||
Ordinary, |
||||
} |
@ -0,0 +1,102 @@
|
||||
//! Cursor implementation for the parser.
|
||||
|
||||
use crate::syntax::ast::token::Token; |
||||
|
||||
/// Token cursor.
|
||||
///
|
||||
/// This internal structure gives basic testable operations to the parser.
|
||||
#[derive(Debug, Clone, Default)] |
||||
pub(super) struct Cursor<'a> { |
||||
/// The tokens being input.
|
||||
tokens: &'a [Token], |
||||
/// The current position within the tokens.
|
||||
pos: usize, |
||||
} |
||||
|
||||
impl<'a> Cursor<'a> { |
||||
/// Creates a new cursor.
|
||||
pub(super) fn new(tokens: &'a [Token]) -> Self { |
||||
Self { |
||||
tokens, |
||||
..Self::default() |
||||
} |
||||
} |
||||
|
||||
/// Retrieves the current position of the cursor in the token stream.
|
||||
pub(super) fn pos(&self) -> usize { |
||||
self.pos |
||||
} |
||||
|
||||
/// Moves the cursor to the given position.
|
||||
/// This is intended to be used *always* with `Cursor::pos()`.
|
||||
///
|
||||
pub(super) fn seek(&mut self, pos: usize) { |
||||
self.pos = pos |
||||
} |
||||
|
||||
/// Moves the cursor to the next token and returns the token.
|
||||
pub(super) fn next(&mut self) -> Option<&'a Token> { |
||||
let token = self.tokens.get(self.pos); |
||||
|
||||
if self.pos != self.tokens.len() { |
||||
self.pos += 1; |
||||
} |
||||
|
||||
token |
||||
} |
||||
|
||||
/// Moves the cursor to the next token after skipping tokens based on the predicate.
|
||||
pub(super) fn next_skip<P>(&mut self, mut skip: P) -> Option<&'a Token> |
||||
where |
||||
P: FnMut(&Token) -> bool, |
||||
{ |
||||
while let Some(token) = self.tokens.get(self.pos) { |
||||
self.pos += 1; |
||||
|
||||
if !skip(token) { |
||||
return Some(token); |
||||
} |
||||
} |
||||
None |
||||
} |
||||
|
||||
/// Peeks the next token without moving the cursor.
|
||||
pub(super) fn peek(&self, skip: usize) -> Option<&'a Token> { |
||||
self.tokens.get(self.pos + skip) |
||||
} |
||||
|
||||
/// Peeks the next token after skipping tokens based on the predicate.
|
||||
pub(super) fn peek_skip<P>(&self, mut skip: P) -> Option<&'a Token> |
||||
where |
||||
P: FnMut(&Token) -> bool, |
||||
{ |
||||
let mut current = self.pos; |
||||
while let Some(token) = self.tokens.get(current) { |
||||
if !skip(token) { |
||||
return Some(token); |
||||
} |
||||
current += 1; |
||||
} |
||||
|
||||
None |
||||
} |
||||
|
||||
/// Moves the cursor to the previous token and returns the token.
|
||||
pub(super) fn back(&mut self) { |
||||
assert!( |
||||
self.pos > 0, |
||||
"cannot go back in a cursor that is at the beginning of the list of tokens" |
||||
); |
||||
|
||||
self.pos -= 1; |
||||
} |
||||
|
||||
/// Peeks the previous token without moving the cursor.
|
||||
pub(super) fn peek_prev(&self) -> Option<&'a Token> { |
||||
if self.pos == 0 { |
||||
None |
||||
} else { |
||||
self.tokens.get(self.pos - 1) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue