Skip to content

Commit

Permalink
js: Parse simple function declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
simonwuelker committed Jan 20, 2024
1 parent 49e3b61 commit c95c806
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 7 deletions.
13 changes: 11 additions & 2 deletions crates/js/src/parser/declaration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use crate::bytecode::{self, CompileToBytecode};

use super::{identifiers, tokenizer::Punctuator, Expression, SyntaxError, Tokenizer};
use super::{
functions_and_classes::FunctionDeclaration, identifiers, tokenizer::Punctuator, Expression,
SyntaxError, Tokenizer,
};

/// <https://262.ecma-international.org/14.0/#prod-Declaration>
#[derive(Clone, Debug)]
pub enum Declaration {
Function(FunctionDeclaration),
Lexical(LexicalDeclaration),
}

Expand All @@ -13,7 +17,11 @@ impl Declaration {
pub fn parse<const YIELD: bool, const AWAIT: bool>(
tokenizer: &mut Tokenizer<'_>,
) -> Result<Self, SyntaxError> {
let declaration = if let Ok(lexical_declaration) =
let declaration = if let Ok(function_declaration) =
tokenizer.attempt(FunctionDeclaration::parse::<YIELD, AWAIT, true>)
{
Self::Function(function_declaration)
} else if let Ok(lexical_declaration) =
tokenizer.attempt(LexicalDeclaration::parse::<true, YIELD, AWAIT>)
{
Self::Lexical(lexical_declaration)
Expand Down Expand Up @@ -121,6 +129,7 @@ impl CompileToBytecode for Declaration {
fn compile(&self, builder: &mut bytecode::Builder) {
match self {
Self::Lexical(lexical_declaration) => lexical_declaration.compile(builder),
Self::Function(function_declaration) => function_declaration.compile(builder),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/js/src/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ fn parse_primary_expression<const YIELD: bool, const AWAIT: bool>(
tokenizer: &mut Tokenizer<'_>,
) -> Result<Expression, SyntaxError> {
if tokenizer
.attempt(|tokenizer| tokenizer.consume_keyword("this"))
.attempt(|tokenizer| tokenizer.expect_keyword("this"))
.is_ok()
{
Ok(Expression::This)
Expand Down
44 changes: 44 additions & 0 deletions crates/js/src/parser/functions_and_classes/function_definitions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! <https://262.ecma-international.org/14.0/#sec-function-definitions>
use crate::{
bytecode::{self, CompileToBytecode},
parser::{
identifiers::parse_binding_identifier, script::Statement, tokenizer::Punctuator,
SyntaxError, Tokenizer,
},
};

/// <https://262.ecma-international.org/14.0/#prod-FunctionDeclaration>
#[derive(Clone, Debug)]
pub struct FunctionDeclaration {
pub identifier: String,
pub body: Vec<Statement>,
}

impl FunctionDeclaration {
/// <https://262.ecma-international.org/14.0/#prod-FunctionDeclaration>
pub fn parse<const YIELD: bool, const AWAIT: bool, const DEFAULT: bool>(
tokenizer: &mut Tokenizer<'_>,
) -> Result<Self, SyntaxError> {
tokenizer.expect_keyword("function")?;

let identifier = parse_binding_identifier::<YIELD, AWAIT>(tokenizer)?;
tokenizer.expect_punctuator(Punctuator::ParenthesisOpen)?;
tokenizer.expect_punctuator(Punctuator::ParenthesisClose)?;
tokenizer.expect_punctuator(Punctuator::CurlyBraceOpen)?;
tokenizer.expect_punctuator(Punctuator::CurlyBraceClose)?;

let body = vec![];

let function_declaration = Self { identifier, body };

Ok(function_declaration)
}
}

impl CompileToBytecode for FunctionDeclaration {
fn compile(&self, builder: &mut bytecode::Builder) {
_ = builder;
todo!()
}
}
6 changes: 6 additions & 0 deletions crates/js/src/parser/functions_and_classes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//! <https://262.ecma-international.org/14.0/#sec-ecmascript-language-functions-and-classes>
mod function_definitions;
mod parameter_lists;

pub(crate) use function_definitions::FunctionDeclaration;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//! <https://262.ecma-international.org/14.0/#prod-FormalParameters>
6 changes: 4 additions & 2 deletions crates/js/src/parser/identifiers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! <https://262.ecma-international.org/14.0/#sec-identifiers>
use super::{SyntaxError, Tokenizer};

const RESERVED_WORDS: [&str; 37] = [
Expand Down Expand Up @@ -83,11 +85,11 @@ pub(crate) fn parse_identifier_reference<const YIELD: bool, const AWAIT: bool>(
return Ok(identifier.0);
}

if YIELD && tokenizer.consume_keyword("yield").is_ok() {
if YIELD && tokenizer.expect_keyword("yield").is_ok() {
return Ok("yield".to_string());
}

if AWAIT && tokenizer.consume_keyword("await").is_ok() {
if AWAIT && tokenizer.expect_keyword("await").is_ok() {
return Ok("await".to_string());
}

Expand Down
1 change: 1 addition & 0 deletions crates/js/src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod declaration;
mod error;
mod expression;
mod functions_and_classes;
pub mod identifiers;
pub mod literals;
pub mod script;
Expand Down
14 changes: 12 additions & 2 deletions crates/js/src/parser/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,17 @@ impl<'a> Tokenizer<'a> {
self.source.go_back()
}

pub fn consume_keyword(&mut self, keyword: &str) -> Result<(), SyntaxError> {
pub fn expect_punctuator(&mut self, expected: Punctuator) -> Result<(), SyntaxError> {
let punctuator = self.consume_punctuator()?;

if punctuator == expected {
Ok(())
} else {
Err(self.syntax_error())
}
}

pub fn expect_keyword(&mut self, keyword: &str) -> Result<(), SyntaxError> {
if self.source.remaining().starts_with(keyword) {
_ = self.source.advance_by(keyword.len());
self.skip_whitespace();
Expand All @@ -186,7 +196,7 @@ impl<'a> Tokenizer<'a> {
}

pub fn consume_null_literal(&mut self) -> Result<(), SyntaxError> {
self.consume_keyword("null")
self.expect_keyword("null")
}

pub fn consume_boolean_literal(&mut self) -> Result<bool, SyntaxError> {
Expand Down

0 comments on commit c95c806

Please sign in to comment.