diff --git a/checker/src/synthesis/mod.rs b/checker/src/synthesis/mod.rs index 1bda34b3..8a07daf0 100644 --- a/checker/src/synthesis/mod.rs +++ b/checker/src/synthesis/mod.rs @@ -57,7 +57,7 @@ impl crate::ASTImplementation for EznoParser { type VariableField<'_a> = parser::VariableField; - type ForStatementInitiliser<'_a> = parser::statements::ForLoopStatementinitialiser; + type ForStatementInitiliser<'_a> = parser::statements::ForLoopStatementInitialiser; fn module_from_string( // TODO remove @@ -163,13 +163,13 @@ impl crate::ASTImplementation for EznoParser { checking_data: &mut crate::CheckingData, ) { match for_loop_initialiser { - parser::statements::ForLoopStatementinitialiser::VariableDeclaration(declaration) => { + parser::statements::ForLoopStatementInitialiser::VariableDeclaration(declaration) => { // TODO is this correct & the best hoist_variable_declaration(declaration, environment, checking_data); synthesise_variable_declaration(declaration, environment, checking_data, false); } - parser::statements::ForLoopStatementinitialiser::VarStatement(_) => todo!(), - parser::statements::ForLoopStatementinitialiser::Expression(_) => todo!(), + parser::statements::ForLoopStatementInitialiser::VarStatement(_) => todo!(), + parser::statements::ForLoopStatementInitialiser::Expression(_) => todo!(), } } diff --git a/parser/src/declarations/variable.rs b/parser/src/declarations/variable.rs index 2b656880..b72ad3c8 100644 --- a/parser/src/declarations/variable.rs +++ b/parser/src/declarations/variable.rs @@ -4,8 +4,9 @@ use iterator_endiate::EndiateIteratorExt; use crate::{ derive_ASTNode, errors::parse_lexing_error, expressions::operators::COMMA_PRECEDENCE, - throw_unexpected_token_with_token, ASTNode, Expression, ParseOptions, ParseResult, Span, - TSXKeyword, TSXToken, Token, TokenReader, TypeAnnotation, VariableField, WithComment, + throw_unexpected_token_with_token, ASTNode, Expression, ParseError, ParseErrors, ParseOptions, + ParseResult, Span, TSXKeyword, TSXToken, Token, TokenReader, TypeAnnotation, VariableField, + WithComment, }; use visitable_derive::Visitable; @@ -270,10 +271,19 @@ impl ASTNode for VariableDeclaration { break; } } - VariableDeclaration::LetDeclaration { - position: start.union(declarations.last().unwrap().get_position()), - declarations, - } + + let position = if let Some(last) = declarations.last() { + start.union(last.get_position()) + } else { + let position = start.with_length(3); + if options.partial_syntax { + position + } else { + return Err(ParseError::new(ParseErrors::ExpectedDeclaration, position)); + } + }; + + VariableDeclaration::LetDeclaration { position, declarations } } VariableDeclarationKeyword::Const => { state.append_keyword_at_pos(start.0, TSXKeyword::Const); @@ -297,10 +307,19 @@ impl ASTNode for VariableDeclaration { break; } } - VariableDeclaration::ConstDeclaration { - position: start.union(declarations.last().unwrap().get_position()), - declarations, - } + + let position = if let Some(last) = declarations.last() { + start.union(last.get_position()) + } else { + let position = start.with_length(3); + if options.partial_syntax { + position + } else { + return Err(ParseError::new(ParseErrors::ExpectedDeclaration, position)); + } + }; + + VariableDeclaration::ConstDeclaration { position, declarations } } }) } diff --git a/parser/src/errors.rs b/parser/src/errors.rs index 25901f6e..a2f800ee 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -32,84 +32,7 @@ pub enum ParseErrors<'a> { NonStandardSyntaxUsedWithoutEnabled, ExpectRule, InvalidRegexFlag, -} - -#[allow(missing_docs)] -#[derive(Debug)] -pub enum LexingErrors { - SecondDecimalPoint, - NumberLiteralCannotHaveDecimalPoint, - NumberLiteralBaseSpecifierMustPrecededWithZero, - InvalidCharacterInJSXTag(char), - UnbalancedJSXClosingTags, - ExpectedClosingAngleAtEndOfSelfClosingTag, - InvalidCharacterInAttributeKey(char), - UnexpectedCharacter(derive_finite_automaton::InvalidCharacter), - EmptyAttributeName, - ExpectedJSXEndTag, - NewLineInStringLiteral, - ExpectedEndToMultilineComment, - ExpectedEndToStringLiteral, - UnexpectedEndToNumberLiteral, - InvalidNumeralItemBecauseOfLiteralKind, - ExpectedEndToRegexLiteral, - ExpectedEndToJSXLiteral, - ExpectedEndToTemplateLiteral, - InvalidExponentUsage, - InvalidUnderscore, - CannotLoadLargeFile(usize), - ExpectedDashInComment, -} - -impl Display for LexingErrors { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - LexingErrors::SecondDecimalPoint => { - f.write_str("Second decimal point found in number literal") - } - LexingErrors::NumberLiteralCannotHaveDecimalPoint => { - f.write_str("Number literal with specified base cannot have decimal point") - } - LexingErrors::NumberLiteralBaseSpecifierMustPrecededWithZero => { - f.write_str("Number literal base character must be proceeded with a zero") - } - LexingErrors::InvalidCharacterInJSXTag(chr) => { - write!(f, "Invalid character {chr:?} in JSX tag") - } - LexingErrors::ExpectedClosingAngleAtEndOfSelfClosingTag => { - f.write_str("Expected closing angle at end of self closing JSX tag") - } - LexingErrors::InvalidCharacterInAttributeKey(chr) => { - write!(f, "Invalid character {chr:?} in JSX attribute name") - } - LexingErrors::EmptyAttributeName => f.write_str("Empty JSX attribute name"), - LexingErrors::ExpectedJSXEndTag => f.write_str("Expected JSX end tag"), - LexingErrors::NewLineInStringLiteral => { - f.write_str("String literals cannot contain new lines") - } - LexingErrors::ExpectedEndToMultilineComment => { - f.write_str("Unclosed multiline comment") - } - LexingErrors::ExpectedEndToStringLiteral => f.write_str("Unclosed string literal"), - LexingErrors::UnexpectedEndToNumberLiteral => f.write_str("Unclosed number literal"), - LexingErrors::ExpectedEndToRegexLiteral => f.write_str("Unclosed regex literal"), - LexingErrors::ExpectedEndToJSXLiteral => f.write_str("Unclosed JSX literal"), - LexingErrors::ExpectedEndToTemplateLiteral => f.write_str("Unclosed template literal"), - LexingErrors::UnexpectedCharacter(err) => Display::fmt(err, f), - LexingErrors::UnbalancedJSXClosingTags => f.write_str("Too many closing JSX tags"), - LexingErrors::InvalidExponentUsage => f.write_str("Two e in number literal"), - LexingErrors::InvalidUnderscore => f.write_str("Numeric separator in invalid place"), - LexingErrors::InvalidNumeralItemBecauseOfLiteralKind => { - f.write_str("Invalid item in binary, hex or octal literal") - } - LexingErrors::CannotLoadLargeFile(size) => { - write!(f, "Cannot parse {size:?} byte file (4GB maximum)") - } - LexingErrors::ExpectedDashInComment => { - f.write_str("JSX comments must have two dashes after ` Display for ParseErrors<'a> { @@ -203,6 +126,87 @@ impl<'a> Display for ParseErrors<'a> { ParseErrors::InvalidRegexFlag => { write!(f, "Regexp flags must be one of 'd', 'g', 'i', 'm', 's', 'u' or 'y'") } + ParseErrors::ExpectedDeclaration => { + write!(f, "Expected identifier after variable declaration keyword") + } + } + } +} + +#[allow(missing_docs)] +#[derive(Debug)] +pub enum LexingErrors { + SecondDecimalPoint, + NumberLiteralCannotHaveDecimalPoint, + NumberLiteralBaseSpecifierMustPrecededWithZero, + InvalidCharacterInJSXTag(char), + UnbalancedJSXClosingTags, + ExpectedClosingAngleAtEndOfSelfClosingTag, + InvalidCharacterInAttributeKey(char), + UnexpectedCharacter(derive_finite_automaton::InvalidCharacter), + EmptyAttributeName, + ExpectedJSXEndTag, + NewLineInStringLiteral, + ExpectedEndToMultilineComment, + ExpectedEndToStringLiteral, + UnexpectedEndToNumberLiteral, + InvalidNumeralItemBecauseOfLiteralKind, + ExpectedEndToRegexLiteral, + ExpectedEndToJSXLiteral, + ExpectedEndToTemplateLiteral, + InvalidExponentUsage, + InvalidUnderscore, + CannotLoadLargeFile(usize), + ExpectedDashInComment, +} + +impl Display for LexingErrors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LexingErrors::SecondDecimalPoint => { + f.write_str("Second decimal point found in number literal") + } + LexingErrors::NumberLiteralCannotHaveDecimalPoint => { + f.write_str("Number literal with specified base cannot have decimal point") + } + LexingErrors::NumberLiteralBaseSpecifierMustPrecededWithZero => { + f.write_str("Number literal base character must be proceeded with a zero") + } + LexingErrors::InvalidCharacterInJSXTag(chr) => { + write!(f, "Invalid character {chr:?} in JSX tag") + } + LexingErrors::ExpectedClosingAngleAtEndOfSelfClosingTag => { + f.write_str("Expected closing angle at end of self closing JSX tag") + } + LexingErrors::InvalidCharacterInAttributeKey(chr) => { + write!(f, "Invalid character {chr:?} in JSX attribute name") + } + LexingErrors::EmptyAttributeName => f.write_str("Empty JSX attribute name"), + LexingErrors::ExpectedJSXEndTag => f.write_str("Expected JSX end tag"), + LexingErrors::NewLineInStringLiteral => { + f.write_str("String literals cannot contain new lines") + } + LexingErrors::ExpectedEndToMultilineComment => { + f.write_str("Unclosed multiline comment") + } + LexingErrors::ExpectedEndToStringLiteral => f.write_str("Unclosed string literal"), + LexingErrors::UnexpectedEndToNumberLiteral => f.write_str("Unclosed number literal"), + LexingErrors::ExpectedEndToRegexLiteral => f.write_str("Unclosed regex literal"), + LexingErrors::ExpectedEndToJSXLiteral => f.write_str("Unclosed JSX literal"), + LexingErrors::ExpectedEndToTemplateLiteral => f.write_str("Unclosed template literal"), + LexingErrors::UnexpectedCharacter(err) => Display::fmt(err, f), + LexingErrors::UnbalancedJSXClosingTags => f.write_str("Too many closing JSX tags"), + LexingErrors::InvalidExponentUsage => f.write_str("Two e in number literal"), + LexingErrors::InvalidUnderscore => f.write_str("Numeric separator in invalid place"), + LexingErrors::InvalidNumeralItemBecauseOfLiteralKind => { + f.write_str("Invalid item in binary, hex or octal literal") + } + LexingErrors::CannotLoadLargeFile(size) => { + write!(f, "Cannot parse {size:?} byte file (4GB maximum)") + } + LexingErrors::ExpectedDashInComment => { + f.write_str("JSX comments must have two dashes after `, + initialiser: Option, condition: Option, afterthought: Option, position: Span, @@ -165,26 +165,26 @@ impl ASTNode for ForLoopCondition { peek { let declaration = VariableDeclaration::from_reader(reader, state, options)?; - Some(ForLoopStatementinitialiser::VariableDeclaration(declaration)) + Some(ForLoopStatementInitialiser::VariableDeclaration(declaration)) } else if let Some(Token(TSXToken::Keyword(TSXKeyword::Var), _)) = peek { let stmt = VarVariableStatement::from_reader(reader, state, options)?; - Some(ForLoopStatementinitialiser::VarStatement(stmt)) + Some(ForLoopStatementInitialiser::VarStatement(stmt)) } else if let Some(Token(TSXToken::SemiColon, _)) = peek { None } else { let expr = MultipleExpression::from_reader(reader, state, options)?; - Some(ForLoopStatementinitialiser::Expression(expr)) + Some(ForLoopStatementInitialiser::Expression(expr)) }; let semi_colon_one = reader.expect_next(TSXToken::SemiColon)?; let start = initialiser.as_ref().map_or(semi_colon_one, |init| match init { - ForLoopStatementinitialiser::VariableDeclaration(item) => { + ForLoopStatementInitialiser::VariableDeclaration(item) => { item.get_position().get_start() } - ForLoopStatementinitialiser::VarStatement(item) => { + ForLoopStatementInitialiser::VarStatement(item) => { item.get_position().get_start() } - ForLoopStatementinitialiser::Expression(item) => { + ForLoopStatementInitialiser::Expression(item) => { item.get_position().get_start() } }); @@ -314,19 +314,19 @@ impl ASTNode for ForLoopCondition { } fn initialiser_to_string( - initialiser: &ForLoopStatementinitialiser, + initialiser: &ForLoopStatementInitialiser, buf: &mut T, options: &crate::ToStringOptions, local: crate::LocalToStringInformation, ) { match initialiser { - ForLoopStatementinitialiser::VariableDeclaration(stmt) => { + ForLoopStatementInitialiser::VariableDeclaration(stmt) => { stmt.to_string_from_buffer(buf, options, local); } - ForLoopStatementinitialiser::Expression(expr) => { + ForLoopStatementInitialiser::Expression(expr) => { expr.to_string_from_buffer(buf, options, local); } - ForLoopStatementinitialiser::VarStatement(stmt) => { + ForLoopStatementInitialiser::VarStatement(stmt) => { stmt.to_string_from_buffer(buf, options, local); } } diff --git a/parser/src/statements/mod.rs b/parser/src/statements/mod.rs index d8ade1d0..6599fc07 100644 --- a/parser/src/statements/mod.rs +++ b/parser/src/statements/mod.rs @@ -8,6 +8,7 @@ use crate::{ declarations::variable::{declarations_to_string, VariableDeclarationItem}, derive_ASTNode, tokens::token_as_identifier, + ParseError, ParseErrors, }; use derive_enum_from_into::{EnumFrom, EnumTryInto}; use derive_partial_eq_extras::PartialEqExtras; @@ -19,7 +20,7 @@ use super::{ TSXKeyword, TSXToken, Token, TokenReader, }; use crate::errors::parse_lexing_error; -pub use for_statement::{ForLoopCondition, ForLoopStatement, ForLoopStatementinitialiser}; +pub use for_statement::{ForLoopCondition, ForLoopStatement, ForLoopStatementInitialiser}; pub use if_statement::*; pub use switch_statement::{SwitchBranch, SwitchStatement}; pub use try_catch_statement::TryCatchStatement; @@ -362,10 +363,19 @@ impl ASTNode for VarVariableStatement { break; } } - Ok(VarVariableStatement { - position: start.union(declarations.last().unwrap().get_position()), - declarations, - }) + + let position = if let Some(last) = declarations.last() { + start.union(last.get_position()) + } else { + let position = start.with_length(3); + if options.partial_syntax { + position + } else { + return Err(ParseError::new(ParseErrors::ExpectedDeclaration, position)); + } + }; + + Ok(VarVariableStatement { position, declarations }) } fn to_string_from_buffer(