feat: add bin/oct/hex literal

This commit is contained in:
Shunsuke Shibayama 2023-07-12 16:31:39 +09:00
parent e73e404083
commit 9025fe7e99
6 changed files with 91 additions and 10 deletions

View file

@ -48,7 +48,7 @@ pub fn type_from_token_kind(kind: TokenKind) -> Type {
use TokenKind::*; use TokenKind::*;
match kind { match kind {
NatLit => Type::Nat, NatLit | BinLit | OctLit | HexLit => Type::Nat,
IntLit => Type::Int, IntLit => Type::Int,
RatioLit => Type::Ratio, RatioLit => Type::Ratio,
StrLit | DocComment => Type::Str, StrLit | DocComment => Type::Str,

View file

@ -920,12 +920,29 @@ impl ValueObj {
pub fn from_str(t: Type, mut content: Str) -> Option<Self> { pub fn from_str(t: Type, mut content: Str) -> Option<Self> {
match t { match t {
Type::Int => content.replace('_', "").parse::<i32>().ok().map(Self::Int), Type::Int => content.replace('_', "").parse::<i32>().ok().map(Self::Int),
Type::Nat => content Type::Nat => {
let content = content
.trim_start_matches('-') // -0 -> 0 .trim_start_matches('-') // -0 -> 0
.replace('_', "") .replace('_', "");
.parse::<u64>() if content.len() <= 1 {
.ok() return content.parse::<u64>().ok().map(Self::Nat);
.map(Self::Nat), }
match &content[0..=1] {
pre @ ("0b" | "0B") => {
let content = content.trim_start_matches(pre);
u64::from_str_radix(content, 2).ok().map(Self::Nat)
}
pre @ ("0o" | "0O") => {
let content = content.trim_start_matches(pre);
u64::from_str_radix(content, 8).ok().map(Self::Nat)
}
pre @ ("0x" | "0X") => {
let content = content.trim_start_matches(pre);
u64::from_str_radix(content, 16).ok().map(Self::Nat)
}
_ => content.parse::<u64>().ok().map(Self::Nat),
}
}
Type::Float => content Type::Float => content
.replace('_', "") .replace('_', "")
.parse::<f64>() .parse::<f64>()

View file

@ -621,6 +621,18 @@ impl Lexer /*<'a>*/ {
n if n.is_ascii_digit() || n == '_' => { n if n.is_ascii_digit() || n == '_' => {
num.push(self.consume().unwrap()); num.push(self.consume().unwrap());
} }
'b' | 'B' => {
num.push(self.consume().unwrap());
return self.lex_bin(num);
}
'o' | 'O' => {
num.push(self.consume().unwrap());
return self.lex_oct(num);
}
'x' | 'X' => {
num.push(self.consume().unwrap());
return self.lex_hex(num);
}
c if Self::is_valid_continue_symbol_ch(c) => { c if Self::is_valid_continue_symbol_ch(c) => {
// exponent (e.g. 10e+3) // exponent (e.g. 10e+3)
if c == 'e' if c == 'e'
@ -682,6 +694,39 @@ impl Lexer /*<'a>*/ {
} }
} }
fn lex_bin(&mut self, mut num: String) -> LexResult<Token> {
while let Some(cur) = self.peek_cur_ch() {
if cur == '0' || cur == '1' || cur == '_' {
num.push(self.consume().unwrap());
} else {
break;
}
}
Ok(self.emit_token(BinLit, &num))
}
fn lex_oct(&mut self, mut num: String) -> LexResult<Token> {
while let Some(cur) = self.peek_cur_ch() {
if matches!(cur, '0'..='7') || cur == '_' {
num.push(self.consume().unwrap());
} else {
break;
}
}
Ok(self.emit_token(OctLit, &num))
}
fn lex_hex(&mut self, mut num: String) -> LexResult<Token> {
while let Some(cur) = self.peek_cur_ch() {
if cur.is_ascii_hexdigit() || cur == '_' {
num.push(self.consume().unwrap());
} else {
break;
}
}
Ok(self.emit_token(HexLit, &num))
}
/// int_part_and_point must be like `12.` /// int_part_and_point must be like `12.`
fn lex_ratio(&mut self, intpart_and_point: String) -> LexResult<Token> { fn lex_ratio(&mut self, intpart_and_point: String) -> LexResult<Token> {
let mut num = intpart_and_point; let mut num = intpart_and_point;
@ -1547,7 +1592,7 @@ impl Iterator for Lexer /*<'a>*/ {
None, None,
))) )))
} }
// IntLit or RatioLit // IntLit (or Bin/Oct/Hex) or RatioLit
Some(n) if n.is_ascii_digit() => Some(self.lex_num(n)), Some(n) if n.is_ascii_digit() => Some(self.lex_num(n)),
// Symbol (includes '_') // Symbol (includes '_')
Some(c) if Self::is_valid_start_symbol_ch(c) => Some(self.lex_symbol(c)), Some(c) if Self::is_valid_start_symbol_ch(c) => Some(self.lex_symbol(c)),

View file

@ -24,6 +24,12 @@ pub enum TokenKind {
NatLit, NatLit,
/// e.g. -1, -2 /// e.g. -1, -2
IntLit, IntLit,
/// e.g. 0b101
BinLit,
/// e.g. 0o777
OctLit,
/// e.g. 0xdeadbeef
HexLit,
RatioLit, RatioLit,
BoolLit, BoolLit,
StrLit, StrLit,
@ -232,8 +238,8 @@ impl TokenKind {
pub const fn category(&self) -> TokenCategory { pub const fn category(&self) -> TokenCategory {
match self { match self {
Symbol => TokenCategory::Symbol, Symbol => TokenCategory::Symbol,
NatLit | IntLit | RatioLit | StrLit | BoolLit | NoneLit | EllipsisLit | InfLit NatLit | BinLit | OctLit | HexLit | IntLit | RatioLit | StrLit | BoolLit | NoneLit
| DocComment => TokenCategory::Literal, | EllipsisLit | InfLit | DocComment => TokenCategory::Literal,
StrInterpLeft => TokenCategory::StrInterpLeft, StrInterpLeft => TokenCategory::StrInterpLeft,
StrInterpMid => TokenCategory::StrInterpMid, StrInterpMid => TokenCategory::StrInterpMid,
StrInterpRight => TokenCategory::StrInterpRight, StrInterpRight => TokenCategory::StrInterpRight,

View file

@ -0,0 +1,8 @@
b = 0b010010
assert b == 18
o = 0o22
assert o == 18
h = 0x12
assert h == 18

View file

@ -52,6 +52,11 @@ fn exec_control_expr() -> Result<(), ()> {
expect_success("tests/should_ok/control_expr.er", 3) expect_success("tests/should_ok/control_expr.er", 3)
} }
#[test]
fn exec_decimal() -> Result<(), ()> {
expect_success("tests/should_ok/decimal.er", 0)
}
#[test] #[test]
fn exec_default_param() -> Result<(), ()> { fn exec_default_param() -> Result<(), ()> {
expect_success("tests/should_ok/default_param.er", 0) expect_success("tests/should_ok/default_param.er", 0)