Skip to content

Commit a2745f6

Browse files
authored
Merge pull request #133 from cobalt-language/num-lits-base
Add alternate bases for integer literals
2 parents 3475a64 + 6669c2e commit a2745f6

File tree

3 files changed

+93
-18
lines changed

3 files changed

+93
-18
lines changed

cobalt-errors/src/error.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,14 @@ pub enum CobaltError<'src> {
4949
#[label("previously defined here")]
5050
prev: SourceSpan,
5151
},
52-
53-
#[error("unexpected character {ch}")]
52+
#[error("invalid character {ch:?} in base-{base} literal")]
53+
InvalidCharInLiteral {
54+
ch: char,
55+
base: u8,
56+
#[label]
57+
loc: SourceSpan,
58+
},
59+
#[error("unexpected character {ch:?}")]
5460
UnexpectedChar {
5561
#[label]
5662
loc: SourceSpan,

cobalt-parser/src/lexer/tokenizer.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,74 @@ impl<'src> SourceReader<'src> {
485485
span: self.source_span_backward(1),
486486
});
487487
}
488+
// alt. base int. literals
489+
'0' => {
490+
let start = self.index;
491+
492+
self.next_char();
493+
let (base, alt) = match self.peek() {
494+
Some(&'x') => (16, true),
495+
Some(&'o') => (8, true),
496+
Some(&'b') => (2, true),
497+
_ => (10, false),
498+
};
499+
if alt {
500+
self.next_char();
501+
}
502+
503+
// Now pointing to the first digit.
504+
let mut end = self.index;
505+
while let Some(&ch) = self.peek() {
506+
if ch.is_digit(base as _) {
507+
self.next_char();
508+
end += 1;
509+
} else {
510+
if ch.is_ascii_digit() {
511+
errors.push(CobaltError::InvalidCharInLiteral {
512+
ch,
513+
base,
514+
loc: (self.index, 1).into(),
515+
});
516+
while self.peek().map_or(false, char::is_ascii_digit) {
517+
self.next_char();
518+
}
519+
}
520+
break;
521+
}
522+
}
523+
524+
// Now pointing to the last digit before the non-digit.
525+
526+
if !alt && self.peek() == Some(&'.') {
527+
self.next_char();
528+
end += 1;
529+
} else {
530+
// Not a float.
531+
tokens.push(Token {
532+
kind: TokenKind::Literal(LiteralToken::Int(&self.source[start..end])),
533+
span: (start..end).into(),
534+
});
535+
continue;
536+
}
537+
538+
// Now pointing to the decimal point.
539+
540+
while let Some(c) = self.peek() {
541+
if c.is_ascii_digit() {
542+
self.next_char();
543+
end += 1;
544+
} else {
545+
break;
546+
}
547+
}
548+
549+
// Now pointing to the last digit of the float.
550+
551+
tokens.push(Token {
552+
kind: TokenKind::Literal(LiteralToken::Float(&self.source[start..end])),
553+
span: (start..end).into(),
554+
});
555+
}
488556

489557
// LITERALS.
490558
n if n.is_ascii_digit() => {

cobalt-parser/src/parser/expr/literal.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use std::borrow::Cow;
2+
use std::collections::HashMap;
13
use std::iter::Peekable;
2-
use std::{borrow::Cow, collections::HashMap, str::CharIndices};
4+
use std::str::CharIndices;
35

4-
use cobalt_ast::ast::CharLiteralAST;
5-
use cobalt_ast::{ast::*, BoxedAST};
6+
use cobalt_ast::ast::*;
67
use cobalt_errors::{CobaltError, SourceSpan};
78

89
use crate::{
@@ -41,16 +42,21 @@ impl<'src> Parser<'src> {
4142

4243
// Now we want to parse the string `s` to an `i128`. If there is a suffix,
4344
// we don't want to include this in the string we parse.
45+
let parsed_int = match s.as_bytes().get(1) {
46+
Some(&b'x') => i128::from_str_radix(&s[2..], 16),
47+
Some(&b'o') => i128::from_str_radix(&s[2..], 8),
48+
Some(&b'b') => i128::from_str_radix(&s[2..], 2),
49+
_ => s.parse::<i128>(),
50+
};
4451

45-
let parsed_int = s.parse::<i128>();
46-
if parsed_int.is_err() {
52+
let parsed_int = parsed_int.unwrap_or_else(|_| {
4753
errors.push(CobaltError::ExpectedFound {
4854
ex: "integer literal",
4955
found: Some(s.into()),
5056
loc: span,
5157
});
52-
}
53-
let parsed_int = parsed_int.unwrap();
58+
0
59+
});
5460

5561
return Box::new(IntLiteralAST::new(span, parsed_int, suffix));
5662
}
@@ -69,21 +75,16 @@ impl<'src> Parser<'src> {
6975

7076
// Now we want to parse the string `s` to an `f64`.
7177

72-
let parsed_float = s.parse::<f64>();
73-
if parsed_float.is_err() {
78+
let parsed_float = s.parse::<f64>().unwrap_or_else(|_| {
7479
errors.push(CobaltError::ExpectedFound {
7580
ex: "float literal",
7681
found: Some(s.into()),
7782
loc: span,
7883
});
79-
}
80-
let parsed_float = parsed_float.unwrap();
84+
0.0
85+
});
8186

82-
return Box::new(cobalt_ast::ast::FloatLiteralAST::new(
83-
span,
84-
parsed_float,
85-
suffix,
86-
));
87+
return Box::new(FloatLiteralAST::new(span, parsed_float, suffix));
8788
}
8889

8990
TokenKind::Literal(LiteralToken::Char(_)) => {

0 commit comments

Comments
 (0)