Skip to content

Commit

Permalink
js: Parse CallExpressions
Browse files Browse the repository at this point in the history
  • Loading branch information
simonwuelker committed Jul 10, 2024
1 parent a48d5ae commit ba480e0
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 3 deletions.
6 changes: 6 additions & 0 deletions crates/js/src/bytecode/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,4 +351,10 @@ impl<'a> BasicBlockBuilder<'a> {

dst
}

#[must_use]
/// <https://262.ecma-international.org/14.0/#sec-evaluatecall>
pub fn call(&mut self, callable: Register, arguments: Vec<Register>) -> Register {
todo!("function call evaluation")
}
}
66 changes: 66 additions & 0 deletions crates/js/src/parser/expressions/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! <https://262.ecma-international.org/14.0/#prod-CallExpression>
use crate::{
bytecode::{self, CompileToBytecode},
parser::{
tokenization::{Punctuator, SkipLineTerminators, Token, Tokenizer},
SyntaxError,
},
};

use super::{AssignmentExpression, Expression};

/// <https://262.ecma-international.org/14.0/#prod-CallExpression>
#[derive(Clone, Debug)]
pub struct CallExpression {
pub callable: Box<Expression>,
pub arguments: Vec<Expression>,
}

/// <https://262.ecma-international.org/14.0/#prod-Arguments>
pub fn parse_arguments<const YIELD: bool, const AWAIT: bool>(
tokenizer: &mut Tokenizer<'_>,
) -> Result<Vec<Expression>, SyntaxError> {
tokenizer.expect_punctuator(Punctuator::ParenthesisOpen)?;

let mut arguments = vec![];
let mut next_token = tokenizer.peek(0, SkipLineTerminators::Yes)?;

while !matches!(
next_token,
Some(Token::Punctuator(Punctuator::ParenthesisClose))
) {
let argument = AssignmentExpression::parse::<true, YIELD, AWAIT>(tokenizer)?;
arguments.push(argument);

next_token = tokenizer.peek(0, SkipLineTerminators::Yes)?;

// There may or may not be a comma - if there's not, then this is the last element
if matches!(next_token, Some(Token::Punctuator(Punctuator::Comma))) {
tokenizer.next(SkipLineTerminators::Yes)?;
next_token = tokenizer.peek(0, SkipLineTerminators::Yes)?;
} else {
break;
}
}

// Consume the final semicolon
tokenizer.next(SkipLineTerminators::Yes)?;

Ok(arguments)
}

impl CompileToBytecode for CallExpression {
type Result = bytecode::Register;

fn compile(&self, builder: &mut bytecode::ProgramBuilder) -> Self::Result {
// https://262.ecma-international.org/14.0/#sec-function-calls-runtime-semantics-evaluation
let callable = self.callable.compile(builder);
let arguments = self
.arguments
.iter()
.map(|arg| arg.compile(builder))
.collect();

builder.get_current_block().call(callable, arguments)
}
}
25 changes: 22 additions & 3 deletions crates/js/src/parser/expressions/left_hand_side_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
use crate::{
bytecode::{self, CompileToBytecode},
parser::{
tokenization::{SkipLineTerminators, Token, Tokenizer},
tokenization::{Punctuator, SkipLineTerminators, Token, Tokenizer},
SyntaxError,
},
};

use super::{parse_primary_expression, Expression, MemberExpression};
use super::{
call::parse_arguments, parse_primary_expression, CallExpression, Expression, MemberExpression,
};

/// <https://262.ecma-international.org/14.0/#prod-NewExpression>
#[derive(Clone, Debug)]
Expand All @@ -30,7 +32,24 @@ pub fn parse_lefthandside_expression<const YIELD: bool, const AWAIT: bool>(
Token::Identifier(ident) if ident == "new" => {
NewExpression::parse::<YIELD, AWAIT>(tokenizer)?
},
_ => MemberExpression::parse::<YIELD, AWAIT>(tokenizer)?,
_ => {
let member_expression = MemberExpression::parse::<YIELD, AWAIT>(tokenizer)?;

let expression = match tokenizer.peek(0, SkipLineTerminators::Yes)? {
Some(Token::Punctuator(Punctuator::ParenthesisOpen)) => {
// Parse call expression
let arguments = parse_arguments::<YIELD, AWAIT>(tokenizer)?;

CallExpression {
callable: Box::new(member_expression),
arguments,
}
.into()
},
_ => member_expression,
};
expression
},
};

Ok(lhs_expression)
Expand Down
10 changes: 10 additions & 0 deletions crates/js/src/parser/expressions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod assignment_expression;
mod binary_expression;
mod call;
mod conditional;
mod left_hand_side_expression;
mod member;
Expand All @@ -12,6 +13,7 @@ mod update_expression;

pub use assignment_expression::AssignmentExpression;
pub use binary_expression::BinaryExpression;
pub use call::CallExpression;
pub use conditional::ConditionalExpression;
pub use member::MemberExpression;
pub use unary_expression::UnaryExpression;
Expand Down Expand Up @@ -44,6 +46,7 @@ pub enum Expression {
Assignment(AssignmentExpression),
ConditionalExpression(ConditionalExpression),
Member(MemberExpression),
Call(CallExpression),
}

/// <https://262.ecma-international.org/14.0/#prod-PrimaryExpression>
Expand Down Expand Up @@ -111,6 +114,7 @@ impl CompileToBytecode for Expression {
Self::This => todo!(),
Self::Assignment(assignment_expression) => assignment_expression.compile(builder),
Self::Binary(binary_expression) => binary_expression.compile(builder),
Self::Call(call_expression) => call_expression.compile(builder),
Self::ConditionalExpression(conditional_expression) => {
conditional_expression.compile(builder)
},
Expand Down Expand Up @@ -185,3 +189,9 @@ impl From<MemberExpression> for Expression {
Self::Member(value)
}
}

impl From<CallExpression> for Expression {
fn from(value: CallExpression) -> Self {
Self::Call(value)
}
}

0 comments on commit ba480e0

Please sign in to comment.