supporting ascii literals

Signed-off-by: Jakub Doka <jakub.doka2@gmail.com>
This commit is contained in:
Jakub Doka 2024-12-14 21:02:29 +01:00
parent dc96c8b10a
commit 00f6729d31
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
5 changed files with 56 additions and 25 deletions

View file

@ -121,10 +121,12 @@ main := fn(): uint {
decimal := 255 decimal := 255
octal := 0o377 octal := 0o377
binary := 0b11111111 binary := 0b11111111
ascii := '\n'
if hex == decimal & octal == decimal & binary == decimal { if hex == decimal & octal == decimal & binary == decimal {
return 0 return 0
} }
return 1 return 1
} }
``` ```

View file

@ -314,6 +314,7 @@ impl<'a> Formatter<'a> {
} }
Expr::Slf { .. } => f.write_str("Self"), Expr::Slf { .. } => f.write_str("Self"),
Expr::String { literal, .. } => f.write_str(literal), Expr::String { literal, .. } => f.write_str(literal),
Expr::Char { literal, .. } => f.write_str(literal),
Expr::Comment { literal, .. } => f.write_str(literal), Expr::Comment { literal, .. } => f.write_str(literal),
Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"), Expr::Mod { path, .. } => write!(f, "@use(\"{path}\")"),
Expr::Embed { path, .. } => write!(f, "@embed(\"{path}\")"), Expr::Embed { path, .. } => write!(f, "@embed(\"{path}\")"),

View file

@ -306,7 +306,7 @@ fn endoce_string(
literal: &str, literal: &str,
str: &mut Vec<u8>, str: &mut Vec<u8>,
report: impl Fn(&core::str::Bytes, &str), report: impl Fn(&core::str::Bytes, &str),
) -> Option<()> { ) -> Option<usize> {
let report = |bytes: &core::str::Bytes, msg: &_| { let report = |bytes: &core::str::Bytes, msg: &_| {
report(bytes, msg); report(bytes, msg);
None::<u8> None::<u8>
@ -332,7 +332,9 @@ fn endoce_string(
}; };
let mut bytes = literal.bytes(); let mut bytes = literal.bytes();
let mut char_len = 0;
while let Some(b) = bytes.next() { while let Some(b) = bytes.next() {
char_len += 1;
if b != b'\\' { if b != b'\\' {
str.push(b); str.push(b);
continue; continue;
@ -354,11 +356,7 @@ fn endoce_string(
str.push(b); str.push(b);
} }
if str.last() != Some(&0) { Some(char_len)
report(&bytes, "string literal must end with null byte (for now)");
}
Some(())
} }
pub fn quad_sort<T>(mut slice: &mut [T], mut cmp: impl FnMut(&T, &T) -> core::cmp::Ordering) { pub fn quad_sort<T>(mut slice: &mut [T], mut cmp: impl FnMut(&T, &T) -> core::cmp::Ordering) {

View file

@ -356,6 +356,7 @@ impl<'a, 'b> Parser<'a, 'b> {
T::Idk => E::Idk { pos }, T::Idk => E::Idk { pos },
T::Die => E::Die { pos }, T::Die => E::Die { pos },
T::DQuote => E::String { pos, literal: self.tok_str(token) }, T::DQuote => E::String { pos, literal: self.tok_str(token) },
T::Quote => E::Char { pos, literal: self.tok_str(token) },
T::Packed => { T::Packed => {
self.packed = true; self.packed = true;
let expr = self.unit_expr()?; let expr = self.unit_expr()?;
@ -896,6 +897,11 @@ generate_expr! {
pos: Pos, pos: Pos,
literal: &'a str, literal: &'a str,
}, },
/// `'\'([^']|\\\')\''`
Char {
pos: Pos,
literal: &'a str,
},
/// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/' /// `'//[^\n]' | '/*' { '([^/*]|*/)*' | Comment } '*/'
Comment { Comment {
pos: Pos, pos: Pos,

View file

@ -2975,7 +2975,7 @@ impl<'a> Codegen<'a> {
} }
Expr::Ident { id, pos, .. } => self.find_type_as_value(pos, self.ci.parent, id, ctx), Expr::Ident { id, pos, .. } => self.find_type_as_value(pos, self.ci.parent, id, ctx),
Expr::Comment { .. } => Some(Value::VOID), Expr::Comment { .. } => Some(Value::VOID),
Expr::String { pos, literal } => { Expr::Char { pos, literal } | Expr::String { pos, literal } => {
let literal = &literal[1..literal.len() - 1]; let literal = &literal[1..literal.len() - 1];
let mut data = core::mem::take(&mut self.pool.lit_buf); let mut data = core::mem::take(&mut self.pool.lit_buf);
debug_assert!(data.is_empty()); debug_assert!(data.is_empty());
@ -2984,27 +2984,51 @@ impl<'a> Codegen<'a> {
self.error(pos + (literal.len() - bytes.len()) as u32 - 1, message); self.error(pos + (literal.len() - bytes.len()) as u32 - 1, message);
}; };
crate::endoce_string(literal, &mut data, report).unwrap(); let char_count = crate::endoce_string(literal, &mut data, report).unwrap();
let ty = self.tys.make_ptr(ty::Id::U8); if matches!(expr, Expr::Char { .. }) {
let global = self if char_count != 1 {
.tys return self.error(
.strings pos,
.get_or_insert(&data, &mut self.tys.ins.globals, |globals| { fa!("character literal can only contain one character, \
StringRef(globals.push(GlobalData { but you supplied {char_count}"),
data: data.clone(), );
ty, }
..Default::default()
}))
})
.0;
let global = self.ci.nodes.new_node_nop(ty, Kind::Global { global }, [VOID]);
self.ci.nodes[global].aclass = GLOBAL_ACLASS as _;
data.clear(); let value = match data.as_slice() {
self.pool.lit_buf = data; &[v] => v as i64,
_ => return self.error(pos, "TODO: support utf-8 characters"),
};
Some(Value::new(global).ty(ty)) data.clear();
self.pool.lit_buf = data;
self.gen_inferred_const(ctx, ty::Id::U8, value, ty::Id::is_integer)
} else {
if data.last() != Some(&0) {
self.error(pos, "string literal must end with null byte (for now)");
}
let ty = self.tys.make_ptr(ty::Id::U8);
let global = self
.tys
.strings
.get_or_insert(&data, &mut self.tys.ins.globals, |globals| {
StringRef(globals.push(GlobalData {
data: data.clone(),
ty,
..Default::default()
}))
})
.0;
let global = self.ci.nodes.new_node_nop(ty, Kind::Global { global }, [VOID]);
self.ci.nodes[global].aclass = GLOBAL_ACLASS as _;
data.clear();
self.pool.lit_buf = data;
Some(Value::new(global).ty(ty))
}
} }
Expr::Defer { pos, value } => { Expr::Defer { pos, value } => {
self.ci.defers.push((pos, ExprRef::new(value))); self.ci.defers.push((pos, ExprRef::new(value)));