Skip to content

Commit

Permalink
char literals
Browse files Browse the repository at this point in the history
  • Loading branch information
simvux committed Nov 28, 2024
1 parent 353a96d commit 95c4ee5
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 9 deletions.
2 changes: 2 additions & 0 deletions lumina-compiler/src/hir/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub enum Literal<'s> {
Int(bool, u128, Var),
Float(f64),
String(&'s str),
Char(&'s str),
}

impl<'t, 'a, 's> FuncLower<'t, 'a, 's> {
Expand All @@ -96,6 +97,7 @@ impl<'t, 'a, 's> FuncLower<'t, 'a, 's> {
}
parser::Expr::Lit(parser::Literal::Float(n)) => Expr::Lit(Literal::Float(*n)),
parser::Expr::Lit(parser::Literal::String(str)) => Expr::Lit(Literal::String(*str)),
parser::Expr::Lit(parser::Literal::Char(c)) => Expr::Lit(Literal::Char(*c)),
parser::Expr::Call(apath, params) => self.callable(apath.as_ref(), params, Expr::Call),
parser::Expr::Lambda(patterns, params, body) => {
let lambda = self.lambda(patterns, None, (**body).as_ref());
Expand Down
1 change: 1 addition & 0 deletions lumina-compiler/src/mir/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ impl<'a, 's> Verify<'a, 's> {
let record = self.items.pinfo.string;
IType::string(record, vec![])
}
hir::Literal::Char(_) => IType::int(false, 8),
},
hir::Expr::Poison => IType::poison(),
}
Expand Down
14 changes: 12 additions & 2 deletions lumina-compiler/src/mir/lower/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ impl<'a, 's> Lower<'a, 's> {

Expr::Record(key, params, unordered_fields)
}
hir::Expr::Lit(lit) => self.lower_literal(lit),
hir::Expr::Lit(lit) => self.lower_literal(expr.span, lit),
hir::Expr::Tuple(elems) => Expr::Tuple(self.lower_exprs(elems)),
hir::Expr::List(elems, var) => self.lower_list(elems, *var),
hir::Expr::Array(elems, arr) => self.lower_array(expr.span, elems, *arr),
Expand Down Expand Up @@ -280,7 +280,7 @@ impl<'a, 's> Lower<'a, 's> {
.collect()
}

fn lower_literal(&mut self, lit: &hir::Literal<'s>) -> Expr {
fn lower_literal(&mut self, span: Span, lit: &hir::Literal<'s>) -> Expr {
match lit {
hir::Literal::Bool(b) => Expr::Bool(*b),
hir::Literal::Int(neg, n, nvar) => {
Expand All @@ -292,6 +292,16 @@ impl<'a, 's> Lower<'a, 's> {
Expr::Int(intsize, neg.then(|| -n).unwrap_or(n))
}
hir::Literal::Float(f) => Expr::Float(*f),
hir::Literal::Char(str) => {
let str = self.escape(str);
if str.len() != 1 {
self.errors.push(FinError::LargeCharLiteral(span));
Expr::Poison
} else {
let byte = str[0];
Expr::Int(IntSize::new(false, 8), byte as i128)
}
}
hir::Literal::String(str) => {
let ro_key = self.str_to_ro(*str);

Expand Down
19 changes: 16 additions & 3 deletions lumina-compiler/src/mir/lower/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub enum FinError {
UnreachablePattern(Span),
MissingPatterns(Span, Vec<pat::MissingPattern>),
InvalidCast(Tr<Type>, Type),
LargeCharLiteral(Span),
BadArrayCount { got: Tr<usize>, exp: Tr<u64> },
BadGenericArrayCount { got: Tr<usize> },
DuplicateField(Tr<String>, Span),
Expand Down Expand Up @@ -137,13 +138,13 @@ impl<'a, 's> Lower<'a, 's> {
}
}

fn str_to_ro(&mut self, str: &'s str) -> M<key::ReadOnly> {
fn escape(&self, str: &'s str) -> Vec<u8> {
let mut buffer = Vec::with_capacity(str.len());
let mut bytes = str.bytes();

loop {
let Some(mut b) = bytes.next() else {
break;
break buffer;
};

match b {
Expand All @@ -163,14 +164,18 @@ impl<'a, 's> Lower<'a, 's> {

buffer.push(b);
}
}

fn str_to_ro(&mut self, str: &'s str) -> M<key::ReadOnly> {
let str = self.escape(str);

// We set the type to `u8` because when this data is accessed
// with ReadOnly, it's treated by-reference so it'll become `*u8`
let ty = Type::u8();

self.read_only_table.push(
self.current.fkey.0,
(mir::ReadOnlyBytes(buffer.into_boxed_slice()), ty),
(mir::ReadOnlyBytes(str.into_boxed_slice()), ty),
)
}

Expand Down Expand Up @@ -369,6 +374,14 @@ pub fn emit_fin_error<'s>(
error: FinError,
) {
match error {
FinError::LargeCharLiteral(span) => sources
.error("invalid char literal")
.m(module)
.eline(
span.move_indice(1),
"char literals may only be used to represent a single character",
)
.emit(),
FinError::DuplicateField(field, previous) => sources
.error("field assigned twice")
.m(module)
Expand Down
9 changes: 9 additions & 0 deletions lumina-parser/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub enum Literal<'a> {
Int(bool, u128),
Float(f64),
String(&'a str),
Char(&'a str),
}

pub(super) struct ExprParser<'p, 'a> {
Expand Down Expand Up @@ -235,6 +236,7 @@ impl<'p, 'a> ExprParser<'p, 'a> {
T::Path => self.expr_path(span, true),
T::Default => self.expr_path(span, true),
T::StringLiteral => self.expr_string(span),
T::CharLiteral => self.expr_char(span),
T::OpenParen => self.expr_parenthesis(span),
T::OpenCurly => self.expr_record(span),
T::OpenList => self.expr_list(span),
Expand All @@ -261,6 +263,7 @@ impl<'p, 'a> ExprParser<'p, 'a> {
T::Path => self.expr_path(span, false),
T::Default => self.expr_path(span, false),
T::StringLiteral => self.expr_string(span),
T::CharLiteral => self.expr_char(span),
T::OpenParen => self.expr_parenthesis(span),
T::OpenCurly => self.expr_record(span),
T::OpenList => self.expr_list(span),
Expand Down Expand Up @@ -470,6 +473,11 @@ impl<'p, 'a> ExprParser<'p, 'a> {
Some(Expr::Lit(Literal::String(self.parser.take(inner_span))).tr(span))
}

fn expr_char(&mut self, span: Span) -> Option<Tr<Expr<'a>>> {
let inner_span = span.move_indice(1).extend_length(-1);
Some(Expr::Lit(Literal::Char(self.parser.take(inner_span))).tr(span))
}

fn expr_parenthesis(&mut self, span: Span) -> Option<Tr<Expr<'a>>> {
let (mut elems, end) = self
.parser
Expand Down Expand Up @@ -837,6 +845,7 @@ impl<'a> fmt::Display for Literal<'a> {
Literal::Int(false, n) => n.fmt(f),
Literal::Float(n) => n.fmt(f),
Literal::String(str) => write!(f, "\"{str}\""),
Literal::Char(c) => write!(f, "\'{c}\'"),
}
}
}
11 changes: 7 additions & 4 deletions lumina-parser/src/lexer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use logos::{Lexer as LogosLexer, Logos, SpannedIter};
use lumina_util::Span;

fn find_str_end<'src>(lex: &mut LogosLexer<'src, Token>) {
fn find_str_end<'src>(end: u8, lex: &mut LogosLexer<'src, Token>) {
let mut i = 0;
let bytes = lex.remainder().as_bytes();

while let Some(&c) = bytes.get(i) {
match c {
b'\\' if bytes.get(i + 1) == Some(&b'"') => i += 2,
b'\\' if bytes.get(i + 1) == Some(&end) => i += 2,
b'\\' if bytes.get(i + 1) == Some(&b'\\') => i += 2,
b'"' => break,
c if c == end => break,
_ => i += 1,
}
}
Expand All @@ -24,8 +24,10 @@ pub enum Token {
#[regex("\\n+")]
NewLines,

#[token("\"", find_str_end)]
#[token("\"", |lex| find_str_end(b'"', lex))]
StringLiteral,
#[token("'", |lex| find_str_end(b'\'', lex))]
CharLiteral,

#[regex("\\-?\\d+")]
Int,
Expand Down Expand Up @@ -177,6 +179,7 @@ impl Token {
T::Val => "static",
T::LineDocComment | T::LineComment => "documentation comment",
T::StringLiteral => "string literal",
T::CharLiteral => "char literal",
T::If => "start of if expression",
T::Bar => "vertical bar",
T::Can => "`can` keyword",
Expand Down
1 change: 1 addition & 0 deletions luminapath/std/char/lib.lm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Placeholder module until proper utf-8 support is implemented

0 comments on commit 95c4ee5

Please sign in to comment.