From 3acd1afb99adde522d8471ccba90932eaa198a95 Mon Sep 17 00:00:00 2001 From: IsaacShelton Date: Thu, 26 Sep 2024 22:10:54 -0500 Subject: [PATCH] Added support for generic character literals --- src/ast/expr/kind.rs | 1 + src/lexer/mod.rs | 14 +++++++++++++- src/parser/parse_expr/primary/mod.rs | 7 +++++++ src/resolve/error.rs | 7 +++++++ src/resolve/expr/mod.rs | 24 +++++++++++++++++++++++- src/token.rs | 1 + tests/character_literals/main.adept | 12 ++++++++++++ tests/run.sh | 1 + 8 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/character_literals/main.adept diff --git a/src/ast/expr/kind.rs b/src/ast/expr/kind.rs index b9b34392..8d39393a 100644 --- a/src/ast/expr/kind.rs +++ b/src/ast/expr/kind.rs @@ -12,6 +12,7 @@ pub enum ExprKind { Boolean(bool), Integer(Integer), Float(f64), + Char(String), String(String), NullTerminatedString(CString), CharLiteral(u8), diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index 7f763aa5..a7f7cedb 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -301,6 +301,16 @@ impl Lexer { }); Waiting } + '\'' => { + // Rune Literal + self.state = State::String(StringState { + value: String::new(), + closing_char: '\'', + modifier: StringModifier::RuneLiteral, + start_source: source, + }); + Waiting + } _ if c.is_alphabetic() || c == '_' => { self.state = State::Identifier(IdentifierState { identifier: String::from(c), @@ -370,7 +380,9 @@ impl Lexer { StringModifier::Normal | StringModifier::NullTerminated => { "Unclosed string literal" } - StringModifier::CharLiteral => "Unclosed character literal", + StringModifier::RuneLiteral | StringModifier::CharLiteral => { + "Unclosed character literal" + } }; return Has(TokenKind::Error(message.into()).at(state.start_source)); diff --git a/src/parser/parse_expr/primary/mod.rs b/src/parser/parse_expr/primary/mod.rs index b405b2e0..c45df786 100644 --- a/src/parser/parse_expr/primary/mod.rs +++ b/src/parser/parse_expr/primary/mod.rs @@ -82,6 +82,13 @@ impl<'a, I: Inflow> Parser<'a, I> { source, )) } + TokenKind::String(StringLiteral { + modifier: StringModifier::RuneLiteral, + .. + }) => { + let content = self.input.advance().kind.unwrap_string().value; + Ok(Expr::new(ExprKind::Char(content), source)) + } TokenKind::OpenParen => { self.input.advance().kind.unwrap_open_paren(); let inner = self.parse_expr()?; diff --git a/src/resolve/error.rs b/src/resolve/error.rs index 9139ad5c..ef9a659c 100644 --- a/src/resolve/error.rs +++ b/src/resolve/error.rs @@ -163,6 +163,7 @@ pub enum ResolveErrorKind { AmbiguousSymbol { name: String, }, + UndeterminedCharacterLiteral, Other { message: String, }, @@ -418,6 +419,12 @@ impl Display for ResolveErrorKind { ResolveErrorKind::AmbiguousSymbol { name } => { write!(f, "Ambiguous symbol '{name}'")?; } + ResolveErrorKind::UndeterminedCharacterLiteral => { + write!( + f, + "Undetermined character literal, consider using c'A' if you want a 'char'" + )?; + } ResolveErrorKind::Other { message } => { write!(f, "{}", message)?; } diff --git a/src/resolve/expr/mod.rs b/src/resolve/expr/mod.rs index b3fd5786..4fd40ff7 100644 --- a/src/resolve/expr/mod.rs +++ b/src/resolve/expr/mod.rs @@ -129,7 +129,7 @@ impl<'a> PreferredType<'a> { } pub fn resolve_expr( - ctx: &mut ResolveExprCtx<'_, '_>, + ctx: &mut ResolveExprCtx, ast_expr: &ast::Expr, preferred_type: Option, initialized: Initialized, @@ -140,6 +140,28 @@ pub fn resolve_expr( ast::ExprKind::Variable(name) => { resolve_variable_expr(ctx, name, preferred_type, initialized, source) } + ast::ExprKind::Char(content) => { + if content.len() == 1 { + if let Some(preferred_type) = preferred_type { + if let TypeKind::CInteger(CInteger::Char, _) = + preferred_type.view(ctx.resolved_ast).kind + { + let expr = resolved::ExprKind::IntegerKnown(Box::new(IntegerKnown { + rigidity: ast::IntegerRigidity::Loose(CInteger::Char, None), + value: content.as_bytes()[0].into(), + })) + .at(source); + + return Ok(TypedExpr::new( + resolved::TypeKind::CInteger(CInteger::Char, None).at(source), + expr, + )); + } + } + } + + Err(ResolveErrorKind::UndeterminedCharacterLiteral.at(source)) + } ast::ExprKind::Integer(value) => { let (resolved_type, expr) = match value { ast::Integer::Known(known) => ( diff --git a/src/token.rs b/src/token.rs index b0093eeb..c26e96f0 100644 --- a/src/token.rs +++ b/src/token.rs @@ -39,6 +39,7 @@ pub enum StringModifier { Normal, NullTerminated, CharLiteral, + RuneLiteral, } #[derive(Clone, Debug, PartialEq)] diff --git a/tests/character_literals/main.adept b/tests/character_literals/main.adept new file mode 100644 index 00000000..ea336854 --- /dev/null +++ b/tests/character_literals/main.adept @@ -0,0 +1,12 @@ + +#[foreign] +func printf(format ptr, ...) int + +func main { + letter char = 'A' // Generic character literal + another_letter := c'B' // C `char` literal + + printf(c"letter = %c\n", letter) + printf(c"another_letter = %c\n", another_letter) +} + diff --git a/tests/run.sh b/tests/run.sh index 9affe6c4..3f7fdd19 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -24,6 +24,7 @@ compile annotation_groups compile array_access compile bitwise_operators compile c_printf +compile character_literals compile comparison_operators compile defines compile enums