mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 20:34:44 +00:00
feat: add bin/oct/hex literal
This commit is contained in:
parent
e73e404083
commit
9025fe7e99
6 changed files with 91 additions and 10 deletions
|
@ -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,
|
||||||
|
|
|
@ -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 => {
|
||||||
.trim_start_matches('-') // -0 -> 0
|
let content = content
|
||||||
.replace('_', "")
|
.trim_start_matches('-') // -0 -> 0
|
||||||
.parse::<u64>()
|
.replace('_', "");
|
||||||
.ok()
|
if content.len() <= 1 {
|
||||||
.map(Self::Nat),
|
return content.parse::<u64>().ok().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>()
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
8
tests/should_ok/decimal.er
Normal file
8
tests/should_ok/decimal.er
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
b = 0b010010
|
||||||
|
assert b == 18
|
||||||
|
|
||||||
|
o = 0o22
|
||||||
|
assert o == 18
|
||||||
|
|
||||||
|
h = 0x12
|
||||||
|
assert h == 18
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue