diff --git a/grammar.md b/grammar.md index 9011717..fd32a50 100644 --- a/grammar.md +++ b/grammar.md @@ -230,13 +230,6 @@ Expression: Primary | Binary ; ``` -## MacroStringLiteral - -```ebnf -MacroStringLiteral: - '`' ( TEXT | '$(' [a-zA-Z0-9_]+ ')' )* '`'; -``` - ## Else ```ebnf @@ -304,7 +297,7 @@ Primary: | StringLiteral | FunctionCall | MemberAccess - | MacroStringLiteral + | TemplateStringLiteral | LuaCode ``` @@ -472,6 +465,13 @@ Prefix: ; ``` +## TemplateStringLiteral + +```ebnf +TemplateStringLiteral: + '`' ( TEXT | '$(' Expression ')' )* '`'; +``` + ## AssignmentDestination ```ebnf diff --git a/src/lexical/token.rs b/src/lexical/token.rs index 8920574..49fbbe2 100644 --- a/src/lexical/token.rs +++ b/src/lexical/token.rs @@ -8,11 +8,14 @@ use std::{ sync::OnceLock, }; -use crate::base::{ - self, - log::SourceCodeDisplay, - source_file::{SourceElement, SourceIterator, Span}, - Handler, +use crate::{ + base::{ + self, + log::SourceCodeDisplay, + source_file::{SourceElement, SourceIterator, Span}, + Handler, + }, + syntax::syntax_tree::expression::{Expression, Primary}, }; use derive_more::From; use enum_as_inner::EnumAsInner; @@ -165,7 +168,7 @@ pub enum Token { DocComment(DocComment), CommandLiteral(CommandLiteral), StringLiteral(StringLiteral), - MacroStringLiteral(Box), + TemplateStringLiteral(Box), } impl SourceElement for Token { @@ -181,7 +184,7 @@ impl SourceElement for Token { Self::DocComment(token) => token.span(), Self::CommandLiteral(token) => token.span(), Self::StringLiteral(token) => token.span(), - Self::MacroStringLiteral(token) => token.span(), + Self::TemplateStringLiteral(token) => token.span(), } } } @@ -352,43 +355,42 @@ impl SourceElement for StringLiteral { } } -/// Represents a hardcoded macro string literal value in the source code. +/// Represents a hardcoded template string literal value in the source code. /// /// ```ebnf -/// MacroStringLiteral: -/// '`' ( TEXT | '$(' [a-zA-Z0-9_]+ ')' )* '`'; +/// TemplateStringLiteral: +/// '`' ( TEXT | '$(' Expression ')' )* '`'; /// ``` #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MacroStringLiteral { - /// The backtick that starts the macro string literal. +pub struct TemplateStringLiteral { + /// The backtick that starts the template string literal. starting_backtick: Punctuation, - /// The parts that make up the macro string literal. - parts: Vec, - /// The backtick that ends the macro string literal. + /// The parts that make up the template string literal. + parts: Vec, + /// The backtick that ends the template string literal. ending_backtick: Punctuation, } -impl MacroStringLiteral { +impl TemplateStringLiteral { /// Returns the string content without escapement characters, leading and trailing double quotes. #[must_use] pub fn str_content(&self) -> String { - use std::fmt::Write; - let mut content = String::new(); for part in &self.parts { match part { - MacroStringLiteralPart::Text(span) => { + TemplateStringLiteralPart::Text(span) => { content += &crate::util::unescape_macro_string(span.str()); } - MacroStringLiteralPart::MacroUsage { identifier, .. } => { - write!( - content, - "$({})", - crate::util::identifier_to_macro(identifier.span.str()) - ) - .expect("can always write to string"); + TemplateStringLiteralPart::Expression { expression, .. } => { + // write!( + // content, + // "$({})", + // crate::util::identifier_to_macro(identifier.span.str()) + // ) + // .expect("can always write to string"); + todo!("handle expression in template string literal") } } } @@ -396,32 +398,32 @@ impl MacroStringLiteral { content } - /// Returns the parts that make up the macro string literal. + /// Returns the parts that make up the template string literal. #[must_use] - pub fn parts(&self) -> &[MacroStringLiteralPart] { + pub fn parts(&self) -> &[TemplateStringLiteralPart] { &self.parts } } -impl SourceElement for MacroStringLiteral { +impl SourceElement for TemplateStringLiteral { fn span(&self) -> Span { self.starting_backtick .span .join(&self.ending_backtick.span) - .expect("Invalid macro string literal span") + .expect("Invalid template string literal span") } } -/// Represents a part of a macro string literal value in the source code. +/// Represents a part of a template string literal value in the source code. #[allow(missing_docs)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum MacroStringLiteralPart { +pub enum TemplateStringLiteralPart { Text(Span), - MacroUsage { + Expression { dollar: Punctuation, open_brace: Punctuation, - identifier: Identifier, + expression: Expression, close_brace: Punctuation, }, } @@ -511,10 +513,10 @@ pub enum TokenizeError { InvalidMacroNameCharacter(#[from] InvalidMacroNameCharacter), #[error(transparent)] - UnclosedMacroUsage(#[from] UnclosedMacroUsage), + UnclosedExpressionInTemplateUsage(#[from] UnclosedExpressionInTemplateUsage), #[error(transparent)] - EmptyMacroUsage(#[from] EmptyMacroUsage), + EmptyExpressionInTemplateUsage(#[from] EmptyExpressionInTemplateUsage), } /// Is an error that can occur when the macro name contains invalid characters. @@ -541,21 +543,21 @@ impl Display for InvalidMacroNameCharacter { impl std::error::Error for InvalidMacroNameCharacter {} -/// Is an error that can occur when the macro usage is not closed. +/// Is an error that can occur when the expression is not closed. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnclosedMacroUsage { - /// The span of the unclosed macro usage. +pub struct UnclosedExpressionInTemplateUsage { + /// The span of the unclosed expression. pub span: Span, } -impl Display for UnclosedMacroUsage { +impl Display for UnclosedExpressionInTemplateUsage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", base::log::Message::new( base::log::Severity::Error, - "A macro usage was opened with `$(` but never closed." + "An expression was opened with `$(` but never closed." ) )?; write!( @@ -566,23 +568,23 @@ impl Display for UnclosedMacroUsage { } } -impl std::error::Error for UnclosedMacroUsage {} +impl std::error::Error for UnclosedExpressionInTemplateUsage {} -/// Is an error that can occur when the macro usage is not closed. +/// Is an error that can occur when the expression is not closed. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct EmptyMacroUsage { - /// The span of the unclosed macro usage. +pub struct EmptyExpressionInTemplateUsage { + /// The span of the unclosed expression. pub span: Span, } -impl Display for EmptyMacroUsage { +impl Display for EmptyExpressionInTemplateUsage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", base::log::Message::new( base::log::Severity::Error, - "A macro usage was opened with `$(` but closed immediately with `)`." + "An expression was opened with `$(` but closed immediately with `)`." ) )?; write!( @@ -593,7 +595,7 @@ impl Display for EmptyMacroUsage { } } -impl std::error::Error for EmptyMacroUsage {} +impl std::error::Error for EmptyExpressionInTemplateUsage {} impl Token { /// Increments the iterator while the predicate returns true. @@ -792,13 +794,13 @@ impl Token { .into() } - /// Handles a sequence of characters that are enclosed in backticks and contain macro usages - fn handle_macro_string_literal( + /// Handles a sequence of characters that are enclosed in backticks and contain expressions + fn handle_template_string_literal( iter: &mut SourceIterator, mut start: usize, ) -> Result { let mut is_escaped = false; - let mut is_inside_macro = false; + let mut is_inside_expression = false; let mut encountered_open_parenthesis = false; let starting_backtick = Punctuation { span: Self::create_span(start, iter), @@ -811,16 +813,16 @@ impl Token { let (index, character) = iter.next().unwrap(); #[expect(clippy::collapsible_else_if)] - if is_inside_macro { + if is_inside_expression { if character == ')' { - // Check if the macro usage is empty + // Check if the template usage is empty if start + 2 == index { - return Err(EmptyMacroUsage { + return Err(UnclosedExpressionInTemplateUsage { span: Span::new(iter.source_file().clone(), start, index + 1).unwrap(), } .into()); } - parts.push(MacroStringLiteralPart::MacroUsage { + parts.push(TemplateStringLiteralPart::Expression { dollar: Punctuation { span: Span::new(iter.source_file().clone(), start, start + 1).unwrap(), punctuation: '$', @@ -830,8 +832,11 @@ impl Token { .unwrap(), punctuation: '(', }, - identifier: Identifier { - span: Self::create_span_with_end_offset(start + 2, iter, -1), + expression: { + // TODO: correctly parse expression + Expression::Primary(Primary::Identifier(Identifier { + span: Self::create_span_with_end_offset(start + 2, iter, -1), + })) }, close_brace: Punctuation { span: Span::new(iter.source_file().clone(), index, index + 1).unwrap(), @@ -839,13 +844,13 @@ impl Token { }, }); start = index + 1; - is_inside_macro = false; + is_inside_expression = false; } else if !encountered_open_parenthesis && character == '(' { encountered_open_parenthesis = true; } else if encountered_open_parenthesis && !Self::is_identifier_character(character) { if character == '`' { - return Err(UnclosedMacroUsage { + return Err(UnclosedExpressionInTemplateUsage { span: Span::new(iter.source_file().clone(), start, start + 2).unwrap(), } .into()); @@ -859,17 +864,17 @@ impl Token { } } else { if character == '$' && iter.peek().is_some_and(|(_, c)| c == '(') { - parts.push(MacroStringLiteralPart::Text( + parts.push(TemplateStringLiteralPart::Text( Self::create_span_with_end_offset(start, iter, -1), )); start = index; - is_inside_macro = true; + is_inside_expression = true; encountered_open_parenthesis = false; } else if character == '\\' { is_escaped = !is_escaped; } else if character == '`' && !is_escaped { if start != index { - parts.push(MacroStringLiteralPart::Text( + parts.push(TemplateStringLiteralPart::Text( Self::create_span_with_end_offset(start, iter, -1), )); } @@ -881,13 +886,13 @@ impl Token { } } - if is_inside_macro { - Err(UnclosedMacroUsage { + if is_inside_expression { + Err(UnclosedExpressionInTemplateUsage { span: Span::new(iter.source_file().clone(), start, start + 2).unwrap(), } .into()) } else { - Ok(Box::new(MacroStringLiteral { + Ok(Box::new(TemplateStringLiteral { starting_backtick, parts, ending_backtick: Punctuation { @@ -947,7 +952,7 @@ impl Token { } // Found macro string literal else if character == '`' { - Self::handle_macro_string_literal(iter, start) + Self::handle_template_string_literal(iter, start) } // Found integer literal else if character.is_ascii_digit() { diff --git a/src/lexical/token_stream.rs b/src/lexical/token_stream.rs index 9502e4a..46c41a2 100644 --- a/src/lexical/token_stream.rs +++ b/src/lexical/token_stream.rs @@ -70,11 +70,15 @@ impl TokenStream { TokenizeError::InvalidMacroNameCharacter(err), )); } - Err(TokenizeError::UnclosedMacroUsage(err)) => { - handler.receive(Error::TokenizeError(TokenizeError::UnclosedMacroUsage(err))); + Err(TokenizeError::UnclosedExpressionInTemplateUsage(err)) => { + handler.receive(Error::TokenizeError( + TokenizeError::UnclosedExpressionInTemplateUsage(err), + )); } - Err(TokenizeError::EmptyMacroUsage(err)) => { - handler.receive(Error::TokenizeError(TokenizeError::EmptyMacroUsage(err))); + Err(TokenizeError::EmptyExpressionInTemplateUsage(err)) => { + handler.receive(Error::TokenizeError( + TokenizeError::EmptyExpressionInTemplateUsage(err), + )); } } } diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index cb99cff..3863f40 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -4,7 +4,7 @@ use crate::{ base::{self, source_file::SourceElement as _, Handler}, - lexical::token::{KeywordKind, MacroStringLiteral, MacroStringLiteralPart}, + lexical::token::{KeywordKind, TemplateStringLiteral, TemplateStringLiteralPart}, syntax::syntax_tree::{ declaration::{Declaration, Function, FunctionVariableType, ImportItems}, expression::{Binary, BinaryOperator, Expression, LuaCode, PrefixOperator, Primary}, @@ -581,7 +581,7 @@ impl Primary { } } Self::Boolean(_) | Self::Integer(_) | Self::StringLiteral(_) => Ok(()), - Self::MacroStringLiteral(lit) => lit.analyze_semantics(scope, handler), + Self::TemplateStringLiteral(lit) => lit.analyze_semantics(scope, handler), Self::FunctionCall(call) => { let var = scope.get_variable(call.identifier().span.str()); var.map_or_else( @@ -719,7 +719,7 @@ impl Primary { match self { Self::Boolean(_) => expected == ValueType::Boolean, Self::Integer(_) => expected == ValueType::Integer, - Self::StringLiteral(_) | Self::MacroStringLiteral(_) => { + Self::StringLiteral(_) | Self::TemplateStringLiteral(_) => { matches!(expected, ValueType::String | ValueType::Boolean) } Self::FunctionCall(_) => matches!(expected, ValueType::Boolean | ValueType::Integer), @@ -948,12 +948,12 @@ impl AnyStringLiteral { ) -> Result<(), error::Error> { match self { Self::StringLiteral(_) => Ok(()), - Self::MacroStringLiteral(lit) => lit.analyze_semantics(scope, handler), + Self::TemplateStringLiteral(lit) => lit.analyze_semantics(scope, handler), } } } -impl MacroStringLiteral { +impl TemplateStringLiteral { fn analyze_semantics( &self, scope: &SemanticScope, @@ -963,25 +963,30 @@ impl MacroStringLiteral { for part in self.parts() { match part { - MacroStringLiteralPart::MacroUsage { identifier, .. } => { - if let Some(variable_type) = scope.get_variable(identifier.span.str()) { - if variable_type != VariableType::MacroParameter { - let err = - error::Error::UnexpectedExpression(UnexpectedExpression(Box::new( - Expression::Primary(Primary::Identifier(identifier.clone())), - ))); + TemplateStringLiteralPart::Expression { expression, .. } => match expression { + Expression::Primary(Primary::Identifier(identifier)) => { + if let Some(variable_type) = scope.get_variable(identifier.span.str()) { + // TODO: correct checks + // if variable_type != VariableType::MacroParameter { + // let err = error::Error::UnexpectedExpression(UnexpectedExpression( + // Box::new(Expression::Primary(Primary::Identifier( + // identifier.clone(), + // ))), + // )); + // handler.receive(err.clone()); + // errs.push(err); + // } + } else { + let err = error::Error::UnknownIdentifier(UnknownIdentifier { + identifier: identifier.span.clone(), + }); handler.receive(err.clone()); errs.push(err); } - } else { - let err = error::Error::UnknownIdentifier(UnknownIdentifier { - identifier: identifier.span.clone(), - }); - handler.receive(err.clone()); - errs.push(err); } - } - MacroStringLiteralPart::Text(_) => {} + _ => todo!("handle other expressions in template string literals"), + }, + TemplateStringLiteralPart::Text(_) => {} } } diff --git a/src/serde.rs b/src/serde.rs index 6eb3a14..31c904b 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -275,14 +275,8 @@ mod tests { serde_json::from_str::>(&serialized).unwrap(); assert_eq!( - Arc::as_ptr( - deserialized - .namespace() - .namespace_keyword() - .span - .source_file() - ), - Arc::as_ptr(deserialized.namespace().namespace_name().span.source_file()) + Arc::as_ptr(deserialized.namespace().keyword().span.source_file()), + Arc::as_ptr(deserialized.namespace().name().span.source_file()) ); } } diff --git a/src/syntax/error.rs b/src/syntax/error.rs index a3cb9e2..b1eadff 100644 --- a/src/syntax/error.rs +++ b/src/syntax/error.rs @@ -37,7 +37,7 @@ pub enum SyntaxKind { Integer, Boolean, StringLiteral, - MacroStringLiteral, + TemplateStringLiteral, AnyStringLiteral, Statement, Expression, @@ -76,8 +76,8 @@ impl SyntaxKind { Self::Integer => "an integer token".to_string(), Self::Boolean => "a boolean token".to_string(), Self::StringLiteral => "a string literal".to_string(), - Self::MacroStringLiteral => "a macro string literal".to_string(), - Self::AnyStringLiteral => "a (macro) string literal".to_string(), + Self::TemplateStringLiteral => "a template string literal".to_string(), + Self::AnyStringLiteral => "a (template) string literal".to_string(), Self::Statement => "a statement syntax".to_string(), Self::Expression => "an expression syntax".to_string(), Self::Operator => "an operator".to_string(), @@ -116,7 +116,7 @@ impl Display for UnexpectedSyntax { Some(Token::Boolean(..)) => "a boolean token".to_string(), Some(Token::CommandLiteral(..)) => "a literal command token".to_string(), Some(Token::StringLiteral(..)) => "a string literal token".to_string(), - Some(Token::MacroStringLiteral(..)) => "a macro string literal token".to_string(), + Some(Token::TemplateStringLiteral(..)) => "a template string literal token".to_string(), None => "EOF".to_string(), }; diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs index 003bc5a..0ded23d 100644 --- a/src/syntax/parser.rs +++ b/src/syntax/parser.rs @@ -7,8 +7,8 @@ use crate::{ base::{self, Handler}, lexical::{ token::{ - Identifier, Integer, Keyword, KeywordKind, MacroStringLiteral, Punctuation, - StringLiteral, Token, + Identifier, Integer, Keyword, KeywordKind, Punctuation, StringLiteral, + TemplateStringLiteral, Token, }, token_stream::{Delimited, Delimiter, TokenStream, TokenTree}, }, @@ -438,19 +438,19 @@ impl Frame<'_> { } } - /// Expects the next [`Token`] to be an [`MacroStringLiteral`], and returns it. + /// Expects the next [`Token`] to be an [`TemplateStringLiteral`], and returns it. /// /// # Errors - /// If the next [`Token`] is not an [`MacroStringLiteral`]. - pub fn parse_macro_string_literal( + /// If the next [`Token`] is not an [`TemplateStringLiteral`]. + pub fn parse_template_string_literal( &mut self, handler: &impl Handler, - ) -> ParseResult { + ) -> ParseResult { match self.next_significant_token() { - Reading::Atomic(Token::MacroStringLiteral(literal)) => Ok(*literal), + Reading::Atomic(Token::TemplateStringLiteral(literal)) => Ok(*literal), found => { let err = Error::UnexpectedSyntax(UnexpectedSyntax { - expected: SyntaxKind::MacroStringLiteral, + expected: SyntaxKind::TemplateStringLiteral, found: found.into_token(), }); handler.receive(Box::new(err.clone())); @@ -469,7 +469,7 @@ impl Frame<'_> { ) -> ParseResult { match self.next_significant_token() { Reading::Atomic(Token::StringLiteral(literal)) => Ok(literal.into()), - Reading::Atomic(Token::MacroStringLiteral(literal)) => Ok((*literal).into()), + Reading::Atomic(Token::TemplateStringLiteral(literal)) => Ok((*literal).into()), found => { let err = Error::UnexpectedSyntax(UnexpectedSyntax { expected: SyntaxKind::AnyStringLiteral, diff --git a/src/syntax/syntax_tree/declaration.rs b/src/syntax/syntax_tree/declaration.rs index dcd814b..88b458b 100644 --- a/src/syntax/syntax_tree/declaration.rs +++ b/src/syntax/syntax_tree/declaration.rs @@ -344,7 +344,6 @@ impl Parser<'_> { /// /// # Errors /// - cannot parse declaration from current position - #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn parse_declaration( &mut self, diff --git a/src/syntax/syntax_tree/expression.rs b/src/syntax/syntax_tree/expression.rs index 971b9b2..d30f044 100644 --- a/src/syntax/syntax_tree/expression.rs +++ b/src/syntax/syntax_tree/expression.rs @@ -13,8 +13,8 @@ use crate::{ }, lexical::{ token::{ - Boolean, Identifier, Integer, Keyword, KeywordKind, MacroStringLiteral, Punctuation, - StringLiteral, Token, + Boolean, Identifier, Integer, Keyword, KeywordKind, Punctuation, StringLiteral, + TemplateStringLiteral, Token, }, token_stream::Delimiter, }, @@ -177,7 +177,7 @@ impl SourceElement for Expression { /// | StringLiteral /// | FunctionCall /// | MemberAccess -/// | MacroStringLiteral +/// | TemplateStringLiteral /// | LuaCode /// ``` #[allow(missing_docs)] @@ -193,7 +193,7 @@ pub enum Primary { StringLiteral(StringLiteral), FunctionCall(FunctionCall), MemberAccess(MemberAccess), - MacroStringLiteral(MacroStringLiteral), + TemplateStringLiteral(TemplateStringLiteral), Lua(Box), } @@ -209,7 +209,7 @@ impl SourceElement for Primary { Self::StringLiteral(string_literal) => string_literal.span(), Self::FunctionCall(function_call) => function_call.span(), Self::MemberAccess(member_access) => member_access.span(), - Self::MacroStringLiteral(macro_string_literal) => macro_string_literal.span(), + Self::TemplateStringLiteral(template_string_literal) => template_string_literal.span(), Self::Lua(lua_code) => lua_code.span(), } } @@ -661,12 +661,12 @@ impl Parser<'_> { Ok(Primary::StringLiteral(literal)) } - // macro string literal expression - Reading::Atomic(Token::MacroStringLiteral(macro_string_literal)) => { - // eat the macro string literal + // template string literal expression + Reading::Atomic(Token::TemplateStringLiteral(template_string_literal)) => { + // eat the template string literal self.forward(); - Ok(Primary::MacroStringLiteral(*macro_string_literal)) + Ok(Primary::TemplateStringLiteral(*template_string_literal)) } // lua code expression diff --git a/src/syntax/syntax_tree/mod.rs b/src/syntax/syntax_tree/mod.rs index bb6e67a..57930e3 100644 --- a/src/syntax/syntax_tree/mod.rs +++ b/src/syntax/syntax_tree/mod.rs @@ -12,7 +12,7 @@ use crate::{ Handler, VoidHandler, }, lexical::{ - token::{Identifier, MacroStringLiteral, Punctuation, StringLiteral, Token}, + token::{Identifier, Punctuation, StringLiteral, TemplateStringLiteral, Token}, token_stream::Delimiter, }, syntax::parser::Reading, @@ -69,25 +69,25 @@ pub struct DelimitedList { pub close: Punctuation, } -/// Represents a syntax tree node that can be either a string literal or a macro string literal. +/// Represents a syntax tree node that can be either a string literal or a template string literal. /// /// Syntax Synopsis: /// ```ebnf -/// AnyStringLiteral: StringLiteral | MacroStringLiteral ; +/// AnyStringLiteral: StringLiteral | TemplateStringLiteral ; /// ``` #[allow(missing_docs)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From)] pub enum AnyStringLiteral { StringLiteral(StringLiteral), - MacroStringLiteral(MacroStringLiteral), + TemplateStringLiteral(TemplateStringLiteral), } impl SourceElement for AnyStringLiteral { fn span(&self) -> Span { match self { Self::StringLiteral(string_literal) => string_literal.span(), - Self::MacroStringLiteral(macro_string_literal) => macro_string_literal.span(), + Self::TemplateStringLiteral(template_string_literal) => template_string_literal.span(), } } } diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index c16ea1e..57a09a1 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -1010,7 +1010,6 @@ impl Parser<'_> { /// /// # Errors /// - cannot parse variable declaration from current position - #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn parse_variable_declaration( &mut self, diff --git a/src/transpile/conversions.rs b/src/transpile/conversions.rs index adcdad0..df90dca 100644 --- a/src/transpile/conversions.rs +++ b/src/transpile/conversions.rs @@ -2,8 +2,6 @@ use shulkerbox::util::{MacroString as ExtMacroString, MacroStringPart as ExtMacroStringPart}; -use crate::{lexical::token::MacroStringLiteral, syntax::syntax_tree::AnyStringLiteral}; - use super::util::{MacroString, MacroStringPart}; impl From for ExtMacroString { @@ -25,27 +23,3 @@ impl From for ExtMacroStringPart { } } } - -impl From<&AnyStringLiteral> for ExtMacroString { - fn from(value: &AnyStringLiteral) -> Self { - Self::from(MacroString::from(value)) - } -} - -impl From for ExtMacroString { - fn from(value: AnyStringLiteral) -> Self { - Self::from(&value) - } -} - -impl From<&MacroStringLiteral> for ExtMacroString { - fn from(value: &MacroStringLiteral) -> Self { - Self::from(MacroString::from(value)) - } -} - -impl From for ExtMacroString { - fn from(value: MacroStringLiteral) -> Self { - Self::from(&value) - } -} diff --git a/src/transpile/expression.rs b/src/transpile/expression.rs index 60d0812..02457d4 100644 --- a/src/transpile/expression.rs +++ b/src/transpile/expression.rs @@ -20,7 +20,7 @@ use super::{ #[cfg(feature = "shulkerbox")] use crate::{ base::{self, source_file::SourceElement, Handler, VoidHandler}, - lexical::token::{Identifier, MacroStringLiteralPart, StringLiteral}, + lexical::token::{Identifier, StringLiteral, TemplateStringLiteralPart}, syntax::syntax_tree::expression::{ Binary, BinaryOperator, Expression, Indexed, MemberAccess, Parenthesized, PrefixOperator, Primary, @@ -337,7 +337,7 @@ impl Primary { } } } - Self::StringLiteral(_) | Self::MacroStringLiteral(_) => { + Self::StringLiteral(_) | Self::TemplateStringLiteral(_) => { matches!(r#type, ValueType::String | ValueType::Boolean) } } @@ -412,17 +412,20 @@ impl Primary { expression: lua.span(), }) .and_then(|val| val), - Self::MacroStringLiteral(macro_string_literal) => { - if macro_string_literal + Self::TemplateStringLiteral(template_string_literal) => { + if template_string_literal .parts() .iter() - .any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. })) + .any(|part| matches!(part, TemplateStringLiteralPart::Expression { .. })) { - Ok(ComptimeValue::MacroString( - macro_string_literal.clone().into(), - )) + template_string_literal + .to_macro_string(scope, handler) + .map(ComptimeValue::MacroString) + .map_err(|_| NotComptime { + expression: template_string_literal.span(), + }) } else { - Ok(ComptimeValue::String(macro_string_literal.str_content())) + Ok(ComptimeValue::String(template_string_literal.str_content())) } } } @@ -446,7 +449,7 @@ impl Primary { | Self::FunctionCall(_) | Self::Integer(_) | Self::Lua(_) - | Self::MacroStringLiteral(_) + | Self::TemplateStringLiteral(_) | Self::MemberAccess(_) | Self::Prefix(_) => Err(NotComptime { expression: self.span(), @@ -476,7 +479,7 @@ impl Primary { | Self::FunctionCall(_) | Self::Integer(_) | Self::Lua(_) - | Self::MacroStringLiteral(_) + | Self::TemplateStringLiteral(_) | Self::MemberAccess(_) | Self::Prefix(_) => false, } @@ -1260,7 +1263,7 @@ impl Transpiler { Err(err) } } - Primary::StringLiteral(_) | Primary::MacroStringLiteral(_) => { + Primary::StringLiteral(_) | Primary::TemplateStringLiteral(_) => { if matches!( target, DataLocation::Storage { @@ -1662,9 +1665,11 @@ impl Transpiler { Vec::new(), ExtendedCondition::Runtime(Condition::Atom(s.str_content().to_string().into())), )), - Primary::MacroStringLiteral(macro_string) => Ok(( + Primary::TemplateStringLiteral(template_string) => Ok(( Vec::new(), - ExtendedCondition::Runtime(Condition::Atom(macro_string.into())), + ExtendedCondition::Runtime(Condition::Atom( + template_string.to_macro_string(scope, handler)?.into(), + )), )), Primary::FunctionCall(func) => { if func diff --git a/src/transpile/function.rs b/src/transpile/function.rs index 72dc07f..5dd5298 100644 --- a/src/transpile/function.rs +++ b/src/transpile/function.rs @@ -448,8 +448,8 @@ impl Transpiler { Expression::Primary(Primary::StringLiteral(string)) => { Ok(Parameter::Static(string.str_content().to_string().into())) } - Expression::Primary(Primary::MacroStringLiteral(literal)) => { - Ok(Parameter::Static(literal.into())) + Expression::Primary(Primary::TemplateStringLiteral(literal)) => { + Ok(Parameter::Static(literal.to_macro_string(scope, handler)?)) } Expression::Primary(primary @ Primary::Identifier(ident)) => { let var = diff --git a/src/transpile/internal_functions.rs b/src/transpile/internal_functions.rs index 72d0db8..fa719f6 100644 --- a/src/transpile/internal_functions.rs +++ b/src/transpile/internal_functions.rs @@ -12,7 +12,7 @@ use serde_json::{json, Value as JsonValue}; use crate::{ base::{source_file::SourceElement as _, VoidHandler}, - lexical::token::{Identifier, MacroStringLiteralPart}, + lexical::token::{Identifier, TemplateStringLiteralPart}, semantic::error::{InvalidFunctionArguments, UnexpectedExpression}, syntax::syntax_tree::expression::{Expression, FunctionCall, Primary}, transpile::{ @@ -343,20 +343,25 @@ fn print_function( reason: IllegalIndexingReason::NotIdentifier, })), }, - Primary::MacroStringLiteral(macro_string) => { + Primary::TemplateStringLiteral(template_string) => { let mut cmds = Vec::new(); let mut parts = Vec::new(); - for part in macro_string.parts() { + for part in template_string.parts() { match part { - MacroStringLiteralPart::Text(text) => { + TemplateStringLiteralPart::Text(text) => { parts.push(JsonValue::String(text.str().to_string())); } - MacroStringLiteralPart::MacroUsage { identifier, .. } => { - let (cur_contains_macro, cur_cmds, part) = - get_identifier_part(identifier, transpiler, scope)?; - contains_macro |= cur_contains_macro; - cmds.extend(cur_cmds); - parts.push(part); + TemplateStringLiteralPart::Expression { expression, .. } => { + match expression { + Expression::Primary(Primary::Identifier(identifier)) => { + let (cur_contains_macro, cur_cmds, part) = + get_identifier_part(identifier, transpiler, scope)?; + contains_macro |= cur_contains_macro; + cmds.extend(cur_cmds); + parts.push(part); + } + _ => todo!("other expression in template string literal"), + } } } } diff --git a/src/transpile/lua.rs b/src/transpile/lua.rs index 6109244..e60ffe0 100644 --- a/src/transpile/lua.rs +++ b/src/transpile/lua.rs @@ -149,6 +149,8 @@ mod enabled { table.set("version", crate::VERSION)?; + // TODO: add functions for requesting data/scoreboard locations + Ok(table) } @@ -323,26 +325,41 @@ mod enabled { handler, ), Value::Boolean(boolean) => Ok(Some(ComptimeValue::Boolean(boolean))), - Value::Table(table) => match table.get::("value") { - Ok(Value::Nil) => { - let err = TranspileError::LuaRuntimeError(LuaRuntimeError { - code_block: self.span(), - error_message: "return table must contain non-nil 'value'".to_string(), - }); - handler.receive(Box::new(err.clone())); - Err(err) - } - Ok(value) => { - let value = match self.handle_lua_result(value, handler)? { - Some(ComptimeValue::String(s)) => { - let contains_macro = match table.get::("contains_macro") { - Ok(Value::Boolean(boolean)) => Ok(boolean), - Ok(value) => { - if let Some(ComptimeValue::Boolean(boolean)) = - self.handle_lua_result(value, handler)? - { - Ok(boolean) - } else { + Value::Table(table) => { + // TODO: allow to return arrays when comptime arrays are implemented + match table.get::("value") { + Ok(Value::Nil) => { + let err = TranspileError::LuaRuntimeError(LuaRuntimeError { + code_block: self.span(), + error_message: "return table must contain non-nil 'value'" + .to_string(), + }); + handler.receive(Box::new(err.clone())); + Err(err) + } + Ok(value) => { + let value = match self.handle_lua_result(value, handler)? { + Some(ComptimeValue::String(s)) => { + let contains_macro = match table.get::("contains_macro") + { + Ok(Value::Boolean(boolean)) => Ok(boolean), + Ok(value) => { + if let Some(ComptimeValue::Boolean(boolean)) = + self.handle_lua_result(value, handler)? + { + Ok(boolean) + } else { + let err = TranspileError::MismatchedTypes( + MismatchedTypes { + expression: self.span(), + expected_type: ExpectedType::Boolean, + }, + ); + handler.receive(Box::new(err.clone())); + Err(err) + } + } + _ => { let err = TranspileError::MismatchedTypes(MismatchedTypes { expression: self.span(), @@ -351,39 +368,29 @@ mod enabled { handler.receive(Box::new(err.clone())); Err(err) } - } - _ => { - let err = - TranspileError::MismatchedTypes(MismatchedTypes { - expression: self.span(), - expected_type: ExpectedType::Boolean, - }); - handler.receive(Box::new(err.clone())); - Err(err) - } - }?; + }?; - if contains_macro { - Some(ComptimeValue::MacroString( - s.parse().expect("parsing cannot fail"), - )) - } else { - Some(ComptimeValue::String(s)) + if contains_macro { + Some(ComptimeValue::MacroString( + s.parse().expect("parsing cannot fail"), + )) + } else { + Some(ComptimeValue::String(s)) + } } - } - value => value, - }; - Ok(value) + value => value, + }; + Ok(value) + } + Err(err) => { + let err = TranspileError::LuaRuntimeError( + LuaRuntimeError::from_lua_err(&err, self.span()), + ); + handler.receive(Box::new(err.clone())); + Err(err) + } } - Err(err) => { - let err = TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err( - &err, - self.span(), - )); - handler.receive(Box::new(err.clone())); - Err(err) - } - }, + } Value::Error(_) | Value::Thread(_) | Value::UserData(_) diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 5dfc9a3..b15fa85 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -69,7 +69,6 @@ impl Transpiler { /// /// # Errors /// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing - #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn transpile( mut self, @@ -645,7 +644,9 @@ impl Transpiler { Primary::StringLiteral(string) => { Ok(vec![Command::Raw(string.str_content().to_string())]) } - Primary::MacroStringLiteral(string) => Ok(vec![Command::UsesMacro(string.into())]), + Primary::TemplateStringLiteral(string) => Ok(vec![Command::UsesMacro( + string.to_macro_string(scope, handler)?.into(), + )]), Primary::Lua(code) => match code.eval_comptime(scope, handler)? { Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]), Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]), @@ -987,37 +988,41 @@ impl Transpiler { } } ExecuteBlockHead::As(r#as) => { - let selector = r#as.as_selector(); + let selector = r#as.as_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::As(selector.into(), Box::new(tail))) }) } ExecuteBlockHead::At(at) => { - let selector = at.at_selector(); + let selector = at.at_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::At(selector.into(), Box::new(tail))) }) } ExecuteBlockHead::Align(align) => { - let align = align.align_selector(); + let align = align.align_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Align(align.into(), Box::new(tail))) }) } ExecuteBlockHead::Anchored(anchored) => { - let anchor = anchored.anchored_selector(); + let anchor = anchored + .anchored_selector() + .to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Anchored(anchor.into(), Box::new(tail))) }) } ExecuteBlockHead::In(r#in) => { - let dimension = r#in.in_selector(); + let dimension = r#in.in_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::In(dimension.into(), Box::new(tail))) }) } ExecuteBlockHead::Positioned(positioned) => { - let position = positioned.positioned_selector(); + let position = positioned + .positioned_selector() + .to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { ( pre_cmds, @@ -1026,37 +1031,37 @@ impl Transpiler { }) } ExecuteBlockHead::Rotated(rotated) => { - let rotation = rotated.rotated_selector(); + let rotation = rotated.rotated_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Rotated(rotation.into(), Box::new(tail))) }) } ExecuteBlockHead::Facing(facing) => { - let facing = facing.facing_selector(); + let facing = facing.facing_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Facing(facing.into(), Box::new(tail))) }) } ExecuteBlockHead::AsAt(as_at) => { - let selector = as_at.asat_selector(); + let selector = as_at.asat_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::AsAt(selector.into(), Box::new(tail))) }) } ExecuteBlockHead::On(on) => { - let dimension = on.on_selector(); + let dimension = on.on_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::On(dimension.into(), Box::new(tail))) }) } ExecuteBlockHead::Store(store) => { - let store = store.store_selector(); + let store = store.store_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Store(store.into(), Box::new(tail))) }) } ExecuteBlockHead::Summon(summon) => { - let entity = summon.summon_selector(); + let entity = summon.summon_selector().to_macro_string(scope, handler)?; tail.map(|(pre_cmds, tail)| { (pre_cmds, Execute::Summon(entity.into(), Box::new(tail))) }) diff --git a/src/transpile/util.rs b/src/transpile/util.rs index 5caf766..a4c1eca 100644 --- a/src/transpile/util.rs +++ b/src/transpile/util.rs @@ -1,10 +1,18 @@ //! Utility methods for transpiling -use std::{fmt::Display, str::FromStr}; +use std::{fmt::Display, str::FromStr, sync::Arc}; use crate::{ - lexical::token::{MacroStringLiteral, MacroStringLiteralPart}, - syntax::syntax_tree::AnyStringLiteral, + base::{self, source_file::SourceElement as _, Handler}, + lexical::token::{TemplateStringLiteral, TemplateStringLiteralPart}, + syntax::syntax_tree::{ + expression::{Expression, Primary}, + AnyStringLiteral, + }, + transpile::{ + error::{TranspileError, UnknownIdentifier}, + Scope, TranspileResult, VariableData, + }, }; /// String that can contain macros @@ -247,53 +255,79 @@ where } } -impl From<&AnyStringLiteral> for MacroString { - fn from(value: &AnyStringLiteral) -> Self { - match value { - AnyStringLiteral::StringLiteral(literal) => Self::from(literal.str_content().as_ref()), - AnyStringLiteral::MacroStringLiteral(literal) => Self::from(literal), +impl AnyStringLiteral { + /// Convert the any string literal to a macro string, using the provided scope to resolve variables + /// + /// # Errors + /// - If an identifier in a template string is not found in the scope + pub fn to_macro_string( + &self, + scope: &Arc, + handler: &impl Handler, + ) -> TranspileResult { + match self { + Self::StringLiteral(literal) => Ok(MacroString::from(literal.str_content().as_ref())), + Self::TemplateStringLiteral(literal) => literal.to_macro_string(scope, handler), } } } -impl From for MacroString { - fn from(value: AnyStringLiteral) -> Self { - Self::from(&value) - } -} - -impl From<&MacroStringLiteral> for MacroString { - fn from(value: &MacroStringLiteral) -> Self { - if value +impl TemplateStringLiteral { + /// Convert the template string literal to a macro string, using the provided scope to resolve variables + /// + /// # Errors + /// - If an identifier in a template string is not found in the scope + pub fn to_macro_string( + &self, + scope: &Arc, + handler: &impl Handler, + ) -> TranspileResult { + if self .parts() .iter() - .any(|p| matches!(p, MacroStringLiteralPart::MacroUsage { .. })) + .any(|p| matches!(p, TemplateStringLiteralPart::Expression { .. })) { - Self::MacroString( - value - .parts() + let macro_string = MacroString::MacroString( + self.parts() .iter() .map(|part| match part { - MacroStringLiteralPart::Text(span) => MacroStringPart::String( + TemplateStringLiteralPart::Text(span) => Ok(MacroStringPart::String( crate::util::unescape_macro_string(span.str()).to_string(), - ), - MacroStringLiteralPart::MacroUsage { identifier, .. } => { - MacroStringPart::MacroUsage( - crate::util::identifier_to_macro(identifier.span.str()).to_string(), - ) + )), + TemplateStringLiteralPart::Expression { expression, .. } => { + match expression { + Expression::Primary(Primary::Identifier(identifier)) => + { + #[expect(clippy::option_if_let_else)] + if let Some(var_data) = + scope.get_variable(identifier.span.str()) + { + match var_data.as_ref() { + VariableData::MacroParameter { macro_name, .. } => Ok( + MacroStringPart::MacroUsage(macro_name.to_owned()), + ), + _ => todo!("other identifiers in template strings"), + } + } else { + let err = + TranspileError::UnknownIdentifier(UnknownIdentifier { + identifier: identifier.span(), + }); + handler.receive(Box::new(err.clone())); + Err(err) + } + } + _ => todo!("other expressions in template strings"), + } } }) - .collect(), - ) - } else { - Self::String(value.str_content()) - } - } -} + .collect::>>()?, + ); -impl From for MacroString { - fn from(value: MacroStringLiteral) -> Self { - Self::from(&value) + Ok(macro_string) + } else { + Ok(MacroString::String(self.str_content())) + } } }