rename MacroString to TemplateString
This commit is contained in:
parent
3bc3ca180f
commit
08ed56b673
16
grammar.md
16
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
|
||||
|
|
|
@ -8,11 +8,14 @@ use std::{
|
|||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use crate::base::{
|
||||
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<MacroStringLiteral>),
|
||||
TemplateStringLiteral(Box<TemplateStringLiteral>),
|
||||
}
|
||||
|
||||
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<MacroStringLiteralPart>,
|
||||
/// The backtick that ends the macro string literal.
|
||||
/// The parts that make up the template string literal.
|
||||
parts: Vec<TemplateStringLiteralPart>,
|
||||
/// 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<Self, TokenizeError> {
|
||||
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 {
|
||||
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() {
|
||||
|
|
|
@ -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),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,16 +963,19 @@ impl MacroStringLiteral {
|
|||
|
||||
for part in self.parts() {
|
||||
match part {
|
||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||
TemplateStringLiteralPart::Expression { expression, .. } => match expression {
|
||||
Expression::Primary(Primary::Identifier(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())),
|
||||
)));
|
||||
handler.receive(err.clone());
|
||||
errs.push(err);
|
||||
}
|
||||
// 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(),
|
||||
|
@ -981,7 +984,9 @@ impl MacroStringLiteral {
|
|||
errs.push(err);
|
||||
}
|
||||
}
|
||||
MacroStringLiteralPart::Text(_) => {}
|
||||
_ => todo!("handle other expressions in template string literals"),
|
||||
},
|
||||
TemplateStringLiteralPart::Text(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
src/serde.rs
10
src/serde.rs
|
@ -275,14 +275,8 @@ mod tests {
|
|||
serde_json::from_str::<SerdeWrapper<ProgramFile>>(&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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -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<base::Error>,
|
||||
) -> ParseResult<MacroStringLiteral> {
|
||||
) -> ParseResult<TemplateStringLiteral> {
|
||||
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<AnyStringLiteral> {
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<LuaCode>),
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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<T> {
|
|||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<MacroString> for ExtMacroString {
|
||||
|
@ -25,27 +23,3 @@ impl From<MacroStringPart> for ExtMacroStringPart {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&AnyStringLiteral> for ExtMacroString {
|
||||
fn from(value: &AnyStringLiteral) -> Self {
|
||||
Self::from(MacroString::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyStringLiteral> 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<MacroStringLiteral> for ExtMacroString {
|
||||
fn from(value: MacroStringLiteral) -> Self {
|
||||
Self::from(&value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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,21 +343,26 @@ 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, .. } => {
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((cmds, parts))
|
||||
|
|
|
@ -149,6 +149,8 @@ mod enabled {
|
|||
|
||||
table.set("version", crate::VERSION)?;
|
||||
|
||||
// TODO: add functions for requesting data/scoreboard locations
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
|
@ -323,11 +325,14 @@ mod enabled {
|
|||
handler,
|
||||
),
|
||||
Value::Boolean(boolean) => Ok(Some(ComptimeValue::Boolean(boolean))),
|
||||
Value::Table(table) => match table.get::<Value>("value") {
|
||||
Value::Table(table) => {
|
||||
// TODO: allow to return arrays when comptime arrays are implemented
|
||||
match table.get::<Value>("value") {
|
||||
Ok(Value::Nil) => {
|
||||
let err = TranspileError::LuaRuntimeError(LuaRuntimeError {
|
||||
code_block: self.span(),
|
||||
error_message: "return table must contain non-nil 'value'".to_string(),
|
||||
error_message: "return table must contain non-nil 'value'"
|
||||
.to_string(),
|
||||
});
|
||||
handler.receive(Box::new(err.clone()));
|
||||
Err(err)
|
||||
|
@ -335,7 +340,8 @@ mod enabled {
|
|||
Ok(value) => {
|
||||
let value = match self.handle_lua_result(value, handler)? {
|
||||
Some(ComptimeValue::String(s)) => {
|
||||
let contains_macro = match table.get::<Value>("contains_macro") {
|
||||
let contains_macro = match table.get::<Value>("contains_macro")
|
||||
{
|
||||
Ok(Value::Boolean(boolean)) => Ok(boolean),
|
||||
Ok(value) => {
|
||||
if let Some(ComptimeValue::Boolean(boolean)) =
|
||||
|
@ -343,11 +349,12 @@ mod enabled {
|
|||
{
|
||||
Ok(boolean)
|
||||
} else {
|
||||
let err =
|
||||
TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
let err = TranspileError::MismatchedTypes(
|
||||
MismatchedTypes {
|
||||
expression: self.span(),
|
||||
expected_type: ExpectedType::Boolean,
|
||||
});
|
||||
},
|
||||
);
|
||||
handler.receive(Box::new(err.clone()));
|
||||
Err(err)
|
||||
}
|
||||
|
@ -376,14 +383,14 @@ mod enabled {
|
|||
Ok(value)
|
||||
}
|
||||
Err(err) => {
|
||||
let err = TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
|
||||
&err,
|
||||
self.span(),
|
||||
));
|
||||
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(_)
|
||||
|
|
|
@ -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)))
|
||||
})
|
||||
|
|
|
@ -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<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<MacroString> {
|
||||
match self {
|
||||
Self::StringLiteral(literal) => Ok(MacroString::from(literal.str_content().as_ref())),
|
||||
Self::TemplateStringLiteral(literal) => literal.to_macro_string(scope, handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnyStringLiteral> 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<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<MacroString> {
|
||||
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(),
|
||||
)),
|
||||
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()),
|
||||
),
|
||||
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||
MacroStringPart::MacroUsage(
|
||||
crate::util::identifier_to_macro(identifier.span.str()).to_string(),
|
||||
)
|
||||
_ => 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::<TranspileResult<Vec<MacroStringPart>>>()?,
|
||||
);
|
||||
|
||||
impl From<MacroStringLiteral> for MacroString {
|
||||
fn from(value: MacroStringLiteral) -> Self {
|
||||
Self::from(&value)
|
||||
Ok(macro_string)
|
||||
} else {
|
||||
Ok(MacroString::String(self.str_content()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue