implement variable declaration parsing

This commit is contained in:
Moritz Hölting 2025-02-24 23:02:07 +01:00
parent b8303689db
commit 0d0df920ee
8 changed files with 363 additions and 68 deletions

View File

@ -16,4 +16,4 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: cargo test --verbose - run: cargo test --verbose --all-features

View File

@ -1,6 +1,12 @@
//! Contains the [`Token`] struct and its related types. //! Contains the [`Token`] struct and its related types.
use std::{borrow::Cow, collections::HashMap, fmt::Display, str::FromStr, sync::OnceLock}; use std::{
borrow::Cow,
collections::HashMap,
fmt::{Debug, Display},
str::FromStr,
sync::OnceLock,
};
use crate::base::{ use crate::base::{
self, self,
@ -44,6 +50,8 @@ pub enum KeywordKind {
Tag, Tag,
Of, Of,
Replace, Replace,
Int,
Bool,
} }
impl Display for KeywordKind { impl Display for KeywordKind {
@ -107,6 +115,8 @@ impl KeywordKind {
Self::Tag => "tag", Self::Tag => "tag",
Self::Of => "of", Self::Of => "of",
Self::Replace => "replace", Self::Replace => "replace",
Self::Int => "int",
Self::Bool => "bool",
} }
} }
@ -141,7 +151,8 @@ pub enum Token {
Identifier(Identifier), Identifier(Identifier),
Keyword(Keyword), Keyword(Keyword),
Punctuation(Punctuation), Punctuation(Punctuation),
Numeric(Numeric), Integer(Integer),
Boolean(Boolean),
Comment(Comment), Comment(Comment),
DocComment(DocComment), DocComment(DocComment),
CommandLiteral(CommandLiteral), CommandLiteral(CommandLiteral),
@ -156,7 +167,8 @@ impl SourceElement for Token {
Self::Identifier(token) => token.span(), Self::Identifier(token) => token.span(),
Self::Keyword(token) => token.span(), Self::Keyword(token) => token.span(),
Self::Punctuation(token) => token.span(), Self::Punctuation(token) => token.span(),
Self::Numeric(token) => token.span(), Self::Integer(token) => token.span(),
Self::Boolean(token) => token.span(),
Self::Comment(token) => token.span(), Self::Comment(token) => token.span(),
Self::DocComment(token) => token.span(), Self::DocComment(token) => token.span(),
Self::CommandLiteral(token) => token.span(), Self::CommandLiteral(token) => token.span(),
@ -227,20 +239,72 @@ impl SourceElement for Punctuation {
} }
} }
/// Represents a hardcoded numeric literal value in the source code. /// Represents a hardcoded numeric integer literal value in the source code.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Numeric { pub struct Integer {
/// Is the span that makes up the token. /// Is the span that makes up the token.
pub span: Span, pub span: Span,
} }
impl SourceElement for Numeric { impl SourceElement for Integer {
fn span(&self) -> Span { fn span(&self) -> Span {
self.span.clone() self.span.clone()
} }
} }
impl Integer {
/// Returns the integer value of the token.
#[must_use]
pub fn as_i64(&self) -> i64 {
self.span.str().parse().unwrap()
}
}
impl Debug for Integer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("Integer");
s.field("value", &self.as_i64());
s.field("span", &self.span);
s.finish()
}
}
/// Represents a hardcoded boolean literal value in the source code.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Boolean {
/// Is the span that makes up the token.
pub span: Span,
}
impl SourceElement for Boolean {
fn span(&self) -> Span {
self.span.clone()
}
}
impl Boolean {
/// Returns the boolean value of the token.
#[must_use]
pub fn value(&self) -> bool {
match self.span.str() {
"true" => true,
"false" => false,
_ => unreachable!("Invalid boolean literal"),
}
}
}
impl Debug for Boolean {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = f.debug_struct("Boolean");
s.field("value", &self.value());
s.field("span", &self.span);
s.finish()
}
}
/// Represents a hardcoded string literal value in the source code. /// Represents a hardcoded string literal value in the source code.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -590,16 +654,13 @@ impl Token {
let word = span.str(); let word = span.str();
// Checks if the word is a keyword // Checks if the word is a keyword
KeywordKind::from_str(word).ok().map_or_else( if let Ok(kw) = KeywordKind::from_str(word) {
|| Identifier { span: span.clone() }.into(), Keyword { span, keyword: kw }.into()
|kw| { } else if bool::from_str(word).is_ok() {
Keyword { Boolean { span }.into()
span: span.clone(), } else {
keyword: kw, Identifier { span }.into()
} }
.into()
},
)
} }
/// Handles a sequence starting with a slash /// Handles a sequence starting with a slash
@ -684,11 +745,11 @@ impl Token {
} }
/// Handles a sequence of digits /// Handles a sequence of digits
fn handle_numeric_literal(iter: &mut SourceIterator, start: usize) -> Self { fn handle_integer_literal(iter: &mut SourceIterator, start: usize) -> Self {
// Tokenizes the whole number part // Tokenizes the whole number part
Self::walk_iter(iter, |character| character.is_ascii_digit()); Self::walk_iter(iter, |character| character.is_ascii_digit());
Numeric { Integer {
span: Self::create_span(start, iter), span: Self::create_span(start, iter),
} }
.into() .into()
@ -871,9 +932,9 @@ impl Token {
else if character == '`' { else if character == '`' {
Self::handle_macro_string_literal(iter, start) Self::handle_macro_string_literal(iter, start)
} }
// Found numeric literal // Found integer literal
else if character.is_ascii_digit() { else if character.is_ascii_digit() {
Ok(Self::handle_numeric_literal(iter, start)) Ok(Self::handle_integer_literal(iter, start))
} }
// Found a punctuation // Found a punctuation
else if character.is_ascii_punctuation() { else if character.is_ascii_punctuation() {

View File

@ -11,7 +11,7 @@ use error::{
use crate::{ use crate::{
base::{self, source_file::SourceElement as _, Handler}, base::{self, source_file::SourceElement as _, Handler},
lexical::token::{MacroStringLiteral, MacroStringLiteralPart}, lexical::token::{KeywordKind, MacroStringLiteral, MacroStringLiteralPart},
syntax::syntax_tree::{ syntax::syntax_tree::{
condition::{ condition::{
BinaryCondition, Condition, ParenthesizedCondition, PrimaryCondition, UnaryCondition, BinaryCondition, Condition, ParenthesizedCondition, PrimaryCondition, UnaryCondition,
@ -24,7 +24,7 @@ use crate::{
Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _, Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _,
ExecuteBlockTail, ExecuteBlockTail,
}, },
Block, Grouping, Run, Semicolon, Statement, Block, Grouping, Run, Semicolon, SemicolonStatement, Statement, VariableDeclaration,
}, },
AnyStringLiteral, AnyStringLiteral,
}, },
@ -291,17 +291,20 @@ impl Semicolon {
macro_names: &HashSet<String>, macro_names: &HashSet<String>,
handler: &impl Handler<base::Error>, handler: &impl Handler<base::Error>,
) -> Result<(), error::Error> { ) -> Result<(), error::Error> {
match self.expression() { match self.statement() {
Expression::Primary(Primary::FunctionCall(func)) => { SemicolonStatement::Expression(expr) => match expr {
func.analyze_semantics(function_names, macro_names, handler) Expression::Primary(Primary::FunctionCall(func)) => {
} func.analyze_semantics(function_names, macro_names, handler)
Expression::Primary(unexpected) => { }
let error = error::Error::UnexpectedExpression(UnexpectedExpression( Expression::Primary(unexpected) => {
Expression::Primary(unexpected.clone()), let error = error::Error::UnexpectedExpression(UnexpectedExpression(
)); Expression::Primary(unexpected.clone()),
handler.receive(error.clone()); ));
Err(error) handler.receive(error.clone());
} Err(error)
}
},
SemicolonStatement::VariableDeclaration(decl) => decl.analyze_semantics(handler),
} }
} }
} }
@ -456,7 +459,7 @@ impl Primary {
Self::FunctionCall(func) => { Self::FunctionCall(func) => {
func.analyze_semantics(function_names, macro_names, handler) func.analyze_semantics(function_names, macro_names, handler)
} }
Self::Lua(_) | Self::StringLiteral(_) => Ok(()), Self::Lua(_) | Self::StringLiteral(_) | Self::Integer(_) | Self::Boolean(_) => Ok(()),
Self::MacroStringLiteral(literal) => literal.analyze_semantics(macro_names, handler), Self::MacroStringLiteral(literal) => literal.analyze_semantics(macro_names, handler),
} }
} }
@ -514,6 +517,69 @@ impl AnyStringLiteral {
} }
} }
impl VariableDeclaration {
/// Analyzes the semantics of a variable declaration.
pub fn analyze_semantics(
&self,
handler: &impl Handler<base::Error>,
) -> Result<(), error::Error> {
match self.expression() {
Expression::Primary(Primary::Integer(num)) => {
if self.variable_type().keyword == KeywordKind::Bool {
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::Integer(num.clone())),
));
handler.receive(err.clone());
Err(err)
} else {
Ok(())
}
}
Expression::Primary(Primary::Boolean(bool)) => {
if self.variable_type().keyword == KeywordKind::Int {
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::Boolean(bool.clone())),
));
handler.receive(err.clone());
Err(err)
} else {
Ok(())
}
}
Expression::Primary(Primary::StringLiteral(str)) => {
if matches!(
self.variable_type().keyword,
KeywordKind::Int | KeywordKind::Bool
) {
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::StringLiteral(str.clone())),
));
handler.receive(err.clone());
Err(err)
} else {
Ok(())
}
}
Expression::Primary(Primary::MacroStringLiteral(str)) => {
if matches!(
self.variable_type().keyword,
KeywordKind::Int | KeywordKind::Bool
) {
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::MacroStringLiteral(str.clone())),
));
handler.receive(err.clone());
Err(err)
} else {
Ok(())
}
}
Expression::Primary(_) => Ok(()),
}
}
}
impl PrimaryCondition { impl PrimaryCondition {
/// Analyzes the semantics of a primary condition. /// Analyzes the semantics of a primary condition.
pub fn analyze_semantics( pub fn analyze_semantics(

View File

@ -32,7 +32,8 @@ pub enum SyntaxKind {
Keyword(KeywordKind), Keyword(KeywordKind),
Identifier, Identifier,
Declaration, Declaration,
Numeric, Integer,
Boolean,
StringLiteral, StringLiteral,
MacroStringLiteral, MacroStringLiteral,
AnyStringLiteral, AnyStringLiteral,
@ -69,7 +70,8 @@ impl SyntaxKind {
Self::Punctuation(char) => format!("a punctuation token `{char}`"), Self::Punctuation(char) => format!("a punctuation token `{char}`"),
Self::Keyword(keyword) => format!("a keyword token `{}`", keyword.as_str()), Self::Keyword(keyword) => format!("a keyword token `{}`", keyword.as_str()),
Self::Declaration => "a declaration token".to_string(), Self::Declaration => "a declaration token".to_string(),
Self::Numeric => "a numeric token".to_string(), Self::Integer => "an integer token".to_string(),
Self::Boolean => "a boolean token".to_string(),
Self::StringLiteral => "a string literal".to_string(), Self::StringLiteral => "a string literal".to_string(),
Self::MacroStringLiteral => "a macro string literal".to_string(), Self::MacroStringLiteral => "a macro string literal".to_string(),
Self::AnyStringLiteral => "a (macro) string literal".to_string(), Self::AnyStringLiteral => "a (macro) string literal".to_string(),
@ -106,7 +108,8 @@ impl Display for UnexpectedSyntax {
Some(Token::Punctuation(punctuation)) => { Some(Token::Punctuation(punctuation)) => {
format!("a punctuation token `{}`", punctuation.punctuation) format!("a punctuation token `{}`", punctuation.punctuation)
} }
Some(Token::Numeric(..)) => "a numeric token".to_string(), Some(Token::Integer(..)) => "an integer token".to_string(),
Some(Token::Boolean(..)) => "a boolean token".to_string(),
Some(Token::CommandLiteral(..)) => "a literal command token".to_string(), Some(Token::CommandLiteral(..)) => "a literal command token".to_string(),
Some(Token::StringLiteral(..)) => "a string literal token".to_string(), Some(Token::StringLiteral(..)) => "a string literal token".to_string(),
Some(Token::MacroStringLiteral(..)) => "a macro string literal token".to_string(), Some(Token::MacroStringLiteral(..)) => "a macro string literal token".to_string(),

View File

@ -7,7 +7,7 @@ use crate::{
base::{self, Handler}, base::{self, Handler},
lexical::{ lexical::{
token::{ token::{
Identifier, Keyword, KeywordKind, MacroStringLiteral, Numeric, Punctuation, Identifier, Integer, Keyword, KeywordKind, MacroStringLiteral, Punctuation,
StringLiteral, Token, StringLiteral, Token,
}, },
token_stream::{Delimited, Delimiter, TokenStream, TokenTree}, token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
@ -399,16 +399,16 @@ impl<'a> Frame<'a> {
} }
} }
/// Expects the next [`Token`] to be an [`Numeric`], and returns it. /// Expects the next [`Token`] to be an [`Integer`], and returns it.
/// ///
/// # Errors /// # Errors
/// If the next [`Token`] is not an [`Identifier`]. /// If the next [`Token`] is not an [`Identifier`].
pub fn parse_numeric(&mut self, handler: &impl Handler<Error>) -> ParseResult<Numeric> { pub fn parse_integer(&mut self, handler: &impl Handler<Error>) -> ParseResult<Integer> {
match self.next_significant_token() { match self.next_significant_token() {
Reading::Atomic(Token::Numeric(ident)) => Ok(ident), Reading::Atomic(Token::Integer(ident)) => Ok(ident),
found => { found => {
let err = Error::UnexpectedSyntax(UnexpectedSyntax { let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Numeric, expected: SyntaxKind::Integer,
found: found.into_token(), found: found.into_token(),
}); });
handler.receive(err.clone()); handler.receive(err.clone());

View File

@ -11,7 +11,8 @@ use crate::{
}, },
lexical::{ lexical::{
token::{ token::{
Identifier, Keyword, KeywordKind, MacroStringLiteral, Punctuation, StringLiteral, Token, Boolean, Identifier, Integer, Keyword, KeywordKind, MacroStringLiteral, Punctuation,
StringLiteral, Token,
}, },
token_stream::Delimiter, token_stream::Delimiter,
}, },
@ -53,8 +54,10 @@ impl SourceElement for Expression {
/// ///
/// ``` ebnf /// ``` ebnf
/// Primary: /// Primary:
/// FunctionCall /// Integer
/// | Boolean
/// | StringLiteral /// | StringLiteral
/// | FunctionCall
/// | MacroStringLiteral /// | MacroStringLiteral
/// | LuaCode /// | LuaCode
/// ``` /// ```
@ -62,8 +65,10 @@ impl SourceElement for Expression {
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
pub enum Primary { pub enum Primary {
FunctionCall(FunctionCall), Integer(Integer),
Boolean(Boolean),
StringLiteral(StringLiteral), StringLiteral(StringLiteral),
FunctionCall(FunctionCall),
MacroStringLiteral(MacroStringLiteral), MacroStringLiteral(MacroStringLiteral),
Lua(Box<LuaCode>), Lua(Box<LuaCode>),
} }
@ -71,8 +76,10 @@ pub enum Primary {
impl SourceElement for Primary { impl SourceElement for Primary {
fn span(&self) -> Span { fn span(&self) -> Span {
match self { match self {
Self::FunctionCall(function_call) => function_call.span(), Self::Integer(int) => int.span(),
Self::Boolean(bool) => bool.span(),
Self::StringLiteral(string_literal) => string_literal.span(), Self::StringLiteral(string_literal) => string_literal.span(),
Self::FunctionCall(function_call) => function_call.span(),
Self::MacroStringLiteral(macro_string_literal) => macro_string_literal.span(), Self::MacroStringLiteral(macro_string_literal) => macro_string_literal.span(),
Self::Lua(lua_code) => lua_code.span(), Self::Lua(lua_code) => lua_code.span(),
} }
@ -224,6 +231,22 @@ impl<'a> Parser<'a> {
} }
} }
// integer expression
Reading::Atomic(Token::Integer(int)) => {
// eat the int
self.forward();
Ok(Primary::Integer(int))
}
// boolean expression
Reading::Atomic(Token::Boolean(bool)) => {
// eat the bool
self.forward();
Ok(Primary::Boolean(bool))
}
// string literal expression // string literal expression
Reading::Atomic(Token::StringLiteral(literal)) => { Reading::Atomic(Token::StringLiteral(literal)) => {
// eat the string literal // eat the string literal

View File

@ -12,11 +12,11 @@ use crate::{
Handler, Handler,
}, },
lexical::{ lexical::{
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token}, token::{CommandLiteral, DocComment, Identifier, Keyword, KeywordKind, Punctuation, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::ParseResult, error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
@ -189,7 +189,7 @@ impl SourceElement for Grouping {
/// Syntax Synopsis: /// Syntax Synopsis:
/// ``` ebnf /// ``` ebnf
/// Semicolon: /// Semicolon:
/// Expression ';' /// SemicolonStatement ';'
/// ; /// ;
/// ``` /// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -197,7 +197,7 @@ impl SourceElement for Grouping {
pub struct Semicolon { pub struct Semicolon {
/// The expression of the semicolon statement. /// The expression of the semicolon statement.
#[get = "pub"] #[get = "pub"]
expression: Expression, statement: SemicolonStatement,
/// The semicolon of the semicolon statement. /// The semicolon of the semicolon statement.
#[get = "pub"] #[get = "pub"]
semicolon: Punctuation, semicolon: Punctuation,
@ -205,7 +205,7 @@ pub struct Semicolon {
impl SourceElement for Semicolon { impl SourceElement for Semicolon {
fn span(&self) -> Span { fn span(&self) -> Span {
self.expression self.statement
.span() .span()
.join(&self.semicolon.span()) .join(&self.semicolon.span())
.expect("The span of the semicolon statement is invalid.") .expect("The span of the semicolon statement is invalid.")
@ -215,8 +215,69 @@ impl SourceElement for Semicolon {
impl Semicolon { impl Semicolon {
/// Dissolves the [`Semicolon`] into its components. /// Dissolves the [`Semicolon`] into its components.
#[must_use] #[must_use]
pub fn dissolve(self) -> (Expression, Punctuation) { pub fn dissolve(self) -> (SemicolonStatement, Punctuation) {
(self.expression, self.semicolon) (self.statement, self.semicolon)
}
}
/// Represents a statement that ends with a semicolon in the syntax tree.
///
/// Syntax Synopsis:
/// ``` ebnf
/// SemicolonStatement:
/// (Expression | VariableDeclaration)
/// ';'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SemicolonStatement {
/// An expression that ends with a semicolon.
Expression(Expression),
/// A variable declaration.
VariableDeclaration(VariableDeclaration),
}
impl SourceElement for SemicolonStatement {
fn span(&self) -> Span {
match self {
Self::Expression(expression) => expression.span(),
Self::VariableDeclaration(declaration) => declaration.span(),
}
}
}
/// Represents a variable declaration in the syntax tree.
///
/// Syntax Synopsis:
///
/// ```ebnf
/// LuaCode:
/// ('int' | 'bool') identifier '=' Expression ';'
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct VariableDeclaration {
/// The type of the variable.
#[get = "pub"]
variable_type: Keyword,
/// The identifier of the variable.
#[get = "pub"]
identifier: Identifier,
/// The equals sign of the variable declaration.
#[get = "pub"]
equals: Punctuation,
/// The expression of the variable declaration.
#[get = "pub"]
expression: Expression,
}
impl SourceElement for VariableDeclaration {
fn span(&self) -> Span {
self.variable_type
.span()
.join(&self.expression.span())
.expect("The span of the variable declaration is invalid.")
} }
} }
@ -333,17 +394,77 @@ impl<'a> Parser<'a> {
} }
// semicolon statement // semicolon statement
_ => { _ => self.parse_semicolon(handler).map(Statement::Semicolon),
let expression = self.parse_expression(handler)?;
let semicolon = self.parse_punctuation(';', true, handler)?;
tracing::trace!("Parsed semicolon statement: {:?}", expression);
Ok(Statement::Semicolon(Semicolon {
expression,
semicolon,
}))
}
} }
} }
/// Parses a [`Semicolon`].
#[tracing::instrument(level = "trace", skip_all)]
pub fn parse_semicolon(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<Semicolon> {
let statement = match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(keyword))
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
{
self.parse_variable_declaration(handler)
.map(SemicolonStatement::VariableDeclaration)
}
_ => self
.parse_expression(handler)
.map(SemicolonStatement::Expression),
}?;
let semicolon = self.parse_punctuation(';', true, handler)?;
Ok(Semicolon {
statement,
semicolon,
})
}
/// Parses a [`VariableDeclaration`].
#[tracing::instrument(level = "trace", skip_all)]
pub fn parse_variable_declaration(
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<VariableDeclaration> {
let variable_type = match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(keyword))
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
{
self.forward();
keyword
}
unexpected => {
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Either(&[
SyntaxKind::Keyword(KeywordKind::Int),
SyntaxKind::Keyword(KeywordKind::Bool),
]),
found: unexpected.into_token(),
});
handler.receive(err.clone());
return Err(err);
}
};
// read identifier
self.stop_at_significant();
let identifier = self.parse_identifier(handler)?;
// read equals sign
let equals = self.parse_punctuation('=', true, handler)?;
// read expression
let expression = self.parse_expression(handler)?;
Ok(VariableDeclaration {
variable_type,
identifier,
equals,
expression,
})
}
} }

View File

@ -392,6 +392,8 @@ impl Transpiler {
Expression::Primary(Primary::Lua(lua)) => { Expression::Primary(Primary::Lua(lua)) => {
lua.eval_string(handler).map(Option::unwrap_or_default) lua.eval_string(handler).map(Option::unwrap_or_default)
} }
Expression::Primary(Primary::Integer(num)) => Ok(num.span.str().to_string()),
Expression::Primary(Primary::Boolean(bool)) => Ok(bool.span.str().to_string()),
Expression::Primary(Primary::StringLiteral(string)) => { Expression::Primary(Primary::StringLiteral(string)) => {
Ok(string.str_content().to_string()) Ok(string.str_content().to_string())
} }
@ -459,6 +461,20 @@ impl Transpiler {
Expression::Primary(Primary::FunctionCall(func)) => { Expression::Primary(Primary::FunctionCall(func)) => {
self.transpile_function_call(func, handler).map(Some) self.transpile_function_call(func, handler).map(Some)
} }
Expression::Primary(Primary::Integer(num)) => {
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::Integer(num.clone())),
));
handler.receive(error.clone());
Err(error)
}
Expression::Primary(Primary::Boolean(bool)) => {
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::Boolean(bool.clone())),
));
handler.receive(error.clone());
Err(error)
}
Expression::Primary(Primary::StringLiteral(string)) => { Expression::Primary(Primary::StringLiteral(string)) => {
Ok(Some(Command::Raw(string.str_content().to_string()))) Ok(Some(Command::Raw(string.str_content().to_string())))
} }
@ -501,8 +517,9 @@ impl Transpiler {
Ok(Some(Command::Group(commands))) Ok(Some(Command::Group(commands)))
} }
} }
#[allow(clippy::match_wildcard_for_single_variants)] Statement::Semicolon(semi) => match semi.statement() {
Statement::Semicolon(semi) => match semi.expression() { #[expect(clippy::match_wildcard_for_single_variants)]
SemicolonStatement::Expression(expr) => match expr {
Expression::Primary(Primary::FunctionCall(func)) => { Expression::Primary(Primary::FunctionCall(func)) => {
self.transpile_function_call(func, handler).map(Some) self.transpile_function_call(func, handler).map(Some)
} }
@ -512,6 +529,10 @@ impl Transpiler {
)); ));
handler.receive(error.clone()); handler.receive(error.clone());
Err(error) Err(error)
}
},
SemicolonStatement::VariableDeclaration(_) => {
todo!("Variable declarations are not yet supported.")
} }
}, },
} }