feat(es/ast): Add raw to BigInt (#4218)

This commit is contained in:
Alexander Akait 2022-04-02 09:04:14 +03:00 committed by GitHub
parent 257f0dbb7f
commit e91f271873
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 216 additions and 45 deletions

View File

@ -62,11 +62,22 @@ bridge_lit_from!(Number, usize);
bridge_lit_from!(BigInt, BigIntValue);
#[ast_node("BigIntLiteral")]
#[derive(Eq, Hash, EqIgnoreSpan)]
#[derive(Eq, Hash)]
pub struct BigInt {
pub span: Span,
#[cfg_attr(feature = "rkyv", with(EncodeBigInt))]
pub value: BigIntValue,
/// Use `None` value only for transformations to avoid recalculate
/// characters in big integer
#[cfg_attr(feature = "rkyv", with(crate::EncodeJsWord))]
pub raw: Option<JsWord>,
}
impl EqIgnoreSpan for BigInt {
fn eq_ignore_span(&self, other: &Self) -> bool {
self.value == other.value
}
}
#[cfg(feature = "rkyv")]
@ -125,8 +136,9 @@ impl<'a> arbitrary::Arbitrary<'a> for BigInt {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let span = u.arbitrary()?;
let value = u.arbitrary::<usize>()?.into();
let raw = Some(u.arbitrary::<String>()?.into());
Ok(Self { span, value })
Ok(Self { span, value, raw })
}
}
@ -368,6 +380,7 @@ impl From<BigIntValue> for BigInt {
BigInt {
span: DUMMY_SP,
value,
raw: None,
}
}
}

View File

@ -568,8 +568,20 @@ where
fn emit_big_lit(&mut self, v: &BigInt) -> Result {
self.emit_leading_comments_of_span(v.span, false)?;
if self.cfg.minify {
self.wr.write_lit(v.span, &v.value.to_string())?;
self.wr.write_lit(v.span, "n")?;
} else {
match &v.raw {
Some(raw) => {
self.wr.write_lit(v.span, raw)?;
}
_ => {
self.wr.write_lit(v.span, &v.value.to_string())?;
self.wr.write_lit(v.span, "n")?;
}
}
}
}
// fn emit_object_binding_pat(&mut self, node: &ObjectPat) -> Result {

View File

@ -0,0 +1,14 @@
const n1 = 9007199254740991n;
const n2 = -9007199254740991n;
const n3 = +9007199254740991n;
const n4 = 100_000_000n;
const n5 = 0n;
const n6 = +0n;
const n7 = -0n;
const n8 = 0x10n;
const n9 = 0X10n;
const n10 = 0X10n;
const n11 = 0b101n;
const n12 = 0b101n;
const n13 = 0o13n;
const n14 = 0b101_101n;

View File

@ -0,0 +1,14 @@
const n1 = 9007199254740991n;
const n2 = -9007199254740991n;
const n3 = +9007199254740991n;
const n4 = 100_000_000n;
const n5 = 0n;
const n6 = +0n;
const n7 = -0n;
const n8 = 0x10n;
const n9 = 0X10n;
const n10 = 0X10n;
const n11 = 0b101n;
const n12 = 0b101n;
const n13 = 0o13n;
const n14 = 0b101_101n;

View File

@ -0,0 +1 @@
const n1=9007199254740991n;const n2=-9007199254740991n;const n3=+9007199254740991n;const n4=100000000n;const n5=0n;const n6=+0n;const n7=-0n;const n8=16n;const n9=16n;const n10=16n;const n11=5n;const n12=5n;const n13=11n;const n14=45n

View File

@ -61,3 +61,17 @@ const foo43 = -1000000003242;
const foo43 = 0.0;
const foo44 = -0.0;
const foo45 = +0.0;
const foo46 = 1_000_000_000
const foo47 = 1.1_00_01
const foo48 = 1e1_0
const foo49 = 1e+1_0
const foo50 = 1e-1_0
const foo51 = 1.1e10_0
const foo52 = 1.1e+10_0
const foo53 = 1.1e-10_0
const foo54 = 12_34_56
const foo55 = 1_22_333
const foo56 = 1_2.3_4
const foo57 = 1_2.3_4e5_6
const foo58 = 1_2.3_4e+5_6
const foo59 = 1_2.3_4e-5_6

View File

@ -63,3 +63,17 @@ const foo43 = -1000000003242;
const foo43 = 0;
const foo44 = -0;
const foo45 = +0;
const foo46 = 1000000000;
const foo47 = 1.10001;
const foo48 = 10000000000;
const foo49 = 10000000000;
const foo50 = 0.0000000001;
const foo51 = 11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
const foo52 = 11000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;
const foo53 = 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011;
const foo54 = 123456;
const foo55 = 122333;
const foo56 = 12.34;
const foo57 = 1234000000000000000000000000000000000000000000000000000000;
const foo58 = 1234000000000000000000000000000000000000000000000000000000;
const foo59 = 0.0000000000000000000000000000000000000000000000000000001234;

View File

@ -1 +1 @@
const exp=1e3;const Exp=1e12;const negativeExp=1e-8;const huge=0xe8d4a51001;const big=100000000001;const fractional=100.23002;const numeric_separators=1e12;const one=1e3;const two=1e6;const three=-1e6;const bin=85;const oct=342391;const hex=3735928559;const fractional2=1000.0001;const identifier=_1000;const negate_identifier=-_1000;const foo=.1;const foo1=+.1;const foo2=-.1;const foo3=1050;const foo4=100500;const foo5=10005e3;const foo6=100005e4;const foo7=.1;const foo8=.01;const foo9=.001;const foo10=1e-4;const foo11=1e-5;const foo12=1e-6;const foo13=24e-6;const foo14=-24e-6;const foo15=10;const foo16=100;const foo17=1e3;const foo18=1e4;const foo19=1e5;const foo20=.1;const foo21=.01;const foo22=.001;const foo23=1e-4;const foo24=1e-5;const foo25=-.1;const foo26=-.01;const foo27=-.001;const foo28=-1e-4;const foo29=-1e-5;const foo30=.001;const foo31=.00321;const foo32=321e-6;const foo33=.1;const foo34=1e-5;const foo35=0;const foo36=-0;const foo37=+0;const foo38=1543e-8;const foo39=1543e-8;const foo40=(1543e-8);const foo41={100005e4:"foo"};const foo42=0xe8d4a51caa;const foo43=-0xe8d4a51caa;const foo43=0;const foo44=-0;const foo45=+0
const exp=1e3;const Exp=1e12;const negativeExp=1e-8;const huge=0xe8d4a51001;const big=100000000001;const fractional=100.23002;const numeric_separators=1e12;const one=1e3;const two=1e6;const three=-1e6;const bin=85;const oct=342391;const hex=3735928559;const fractional2=1000.0001;const identifier=_1000;const negate_identifier=-_1000;const foo=.1;const foo1=+.1;const foo2=-.1;const foo3=1050;const foo4=100500;const foo5=10005e3;const foo6=100005e4;const foo7=.1;const foo8=.01;const foo9=.001;const foo10=1e-4;const foo11=1e-5;const foo12=1e-6;const foo13=24e-6;const foo14=-24e-6;const foo15=10;const foo16=100;const foo17=1e3;const foo18=1e4;const foo19=1e5;const foo20=.1;const foo21=.01;const foo22=.001;const foo23=1e-4;const foo24=1e-5;const foo25=-.1;const foo26=-.01;const foo27=-.001;const foo28=-1e-4;const foo29=-1e-5;const foo30=.001;const foo31=.00321;const foo32=321e-6;const foo33=.1;const foo34=1e-5;const foo35=0;const foo36=-0;const foo37=+0;const foo38=1543e-8;const foo39=1543e-8;const foo40=(1543e-8);const foo41={100005e4:"foo"};const foo42=0xe8d4a51caa;const foo43=-0xe8d4a51caa;const foo43=0;const foo44=-0;const foo45=+0;const foo46=1e9;const foo47=1.10001;const foo48=1e10;const foo49=1e10;const foo50=1e-10;const foo51=11e99;const foo52=11e99;const foo53=11e-101;const foo54=123456;const foo55=122333;const foo56=12.34;const foo57=1234e54;const foo58=1234e54;const foo59=1234e-58

View File

@ -186,7 +186,10 @@ impl<'a, I: Input> Lexer<'a, I> {
.read_number(true)
.map(|v| match v {
Left(v) => Num(v),
Right(v) => BigInt(v),
Right((value, raw)) => BigInt {
value,
raw: raw.into(),
},
})
.map(Some);
}
@ -269,7 +272,10 @@ impl<'a, I: Input> Lexer<'a, I> {
.read_number(false)
.map(|v| match v {
Left(v) => Num(v),
Right(v) => BigInt(v),
Right((value, raw)) => BigInt {
value,
raw: raw.into(),
},
})
.map(Some)
}
@ -278,16 +284,23 @@ impl<'a, I: Input> Lexer<'a, I> {
return bigint
.map(|v| match v {
Left(v) => Num(v),
Right(v) => BigInt(v),
Right((value, raw)) => BigInt {
value,
raw: raw.into(),
},
})
.map(Some);
}
'1'..='9' => {
return self
.read_number(false)
.map(|v| match v {
Left(v) => Num(v),
Right(v) => BigInt(v),
Right((value, raw)) => BigInt {
value,
raw: raw.into(),
},
})
.map(Some)
}

View File

@ -33,8 +33,9 @@ impl<'a, I: Input> Lexer<'a, I> {
pub(super) fn read_number(
&mut self,
starts_with_dot: bool,
) -> LexResult<Either<f64, BigIntValue>> {
) -> LexResult<Either<f64, (BigIntValue, String)>> {
debug_assert!(self.cur().is_some());
if starts_with_dot {
debug_assert_eq!(
self.cur(),
@ -42,6 +43,7 @@ impl<'a, I: Input> Lexer<'a, I> {
"read_number(starts_with_dot = true) expects current char to be '.'"
);
}
let start = self.cur_pos();
let starts_with_zero = self.cur().unwrap() == '0';
@ -52,14 +54,25 @@ impl<'a, I: Input> Lexer<'a, I> {
0f64
} else {
// Use read_number_no_dot to support long numbers.
let (val, s, not_octal) = self
let (val, s, mut raw, not_octal) = self
.read_number_no_dot_as_str::<10, { lexical::NumberFormatBuilder::from_radix(10) }>(
)?;
if self.input.cur() == Some('n') {
self.input.bump();
return Ok(Either::Right(s.into_value()));
if self.eat(b'n') {
raw.push('n');
return Ok(Either::Right((s.into_value(), raw)));
}
if self.input.cur() == Some('n') {
raw.push('n');
self.input.bump();
return Ok(Either::Right((s.into_value(), raw)));
}
write!(raw_val, "{}", &s.value).unwrap();
if starts_with_zero {
// TODO: I guess it would be okay if I don't use -ffast-math
// (or something like that), but needs review.
@ -117,7 +130,9 @@ impl<'a, I: Input> Lexer<'a, I> {
// `.1.a`, `.1e-4.a` are valid,
if self.cur() == Some('.') {
raw_val.push('.');
self.bump();
if starts_with_dot {
debug_assert!(self.cur().is_some());
debug_assert!(self.cur().unwrap().is_digit(10));
@ -126,12 +141,15 @@ impl<'a, I: Input> Lexer<'a, I> {
let mut raw = Raw(Some(String::new()));
// Read numbers after dot
let dec_val = self.read_int::<10>(0, &mut raw)?;
val = {
if let Some(..) = dec_val {
raw_val.push_str(raw.0.as_ref().unwrap());
}
raw_val
// Remove number separator from number
.replace('_', "")
.parse()
.expect("failed to parse float using rust's impl")
};
@ -176,7 +194,10 @@ impl<'a, I: Input> Lexer<'a, I> {
write!(raw_val, "{}", exp).unwrap();
// TODO:
raw_val.parse().expect("failed to parse float literal")
raw_val
.replace('_', "")
.parse()
.expect("failed to parse float literal")
}
}
@ -188,7 +209,7 @@ impl<'a, I: Input> Lexer<'a, I> {
/// Returns `Left(value)` or `Right(BigInt)`
pub(super) fn read_radix_number<const RADIX: u8, const FORMAT: u128>(
&mut self,
) -> LexResult<Either<f64, BigIntValue>> {
) -> LexResult<Either<f64, (BigIntValue, String)>> {
debug_assert!(
RADIX == 2 || RADIX == 8 || RADIX == 16,
"radix should be one of 2, 8, 16, but got {}",
@ -196,17 +217,36 @@ impl<'a, I: Input> Lexer<'a, I> {
);
debug_assert_eq!(self.cur(), Some('0'));
self.bump(); // 0
self.bump(); // x
self.with_buf(|l, buf| {
l.bump();
buf.push('0');
let (val, s, _) = self.read_number_no_dot_as_str::<RADIX, FORMAT>()?;
if self.eat(b'n') {
return Ok(Either::Right(s.into_value()));
let c = match l.input.cur() {
Some(c) => {
l.bump();
c
}
_ => {
unreachable!();
}
};
buf.push(c);
let (val, s, raw, _) = l.read_number_no_dot_as_str::<RADIX, FORMAT>()?;
if l.eat(b'n') {
buf.push_str(&raw);
buf.push('n');
return Ok(Either::Right((s.into_value(), (&**buf).into())));
}
self.ensure_not_ident()?;
l.ensure_not_ident()?;
Ok(Either::Left(val))
})
}
/// This can read long integers like
@ -224,6 +264,7 @@ impl<'a, I: Input> Lexer<'a, I> {
let res = self.read_digits::<_, f64, RADIX>(
|total, radix, v| {
read_any = true;
Ok((f64::mul_add(total, radix as f64, v as f64), true))
},
&mut Raw(None),
@ -243,18 +284,19 @@ impl<'a, I: Input> Lexer<'a, I> {
/// Returned bool is `true` is there was `8` or `9`.
fn read_number_no_dot_as_str<const RADIX: u8, const FORMAT: u128>(
&mut self,
) -> LexResult<(f64, LazyBigInt<RADIX>, bool)> {
) -> LexResult<(f64, LazyBigInt<RADIX>, String, bool)> {
debug_assert!(
RADIX == 2 || RADIX == 8 || RADIX == 10 || RADIX == 16,
"radix for read_number_no_dot should be one of 2, 8, 10, 16, but got {}",
RADIX
);
let start = self.cur_pos();
let mut non_octal = false;
let mut non_octal = false;
let mut read_any = false;
let mut raw = Raw(Some(String::new()));
self.read_digits::<_, f64, RADIX>(
|total, radix, v| {
read_any = true;
@ -274,13 +316,17 @@ impl<'a, I: Input> Lexer<'a, I> {
}
let raw_str = raw.0.take().unwrap();
// Remove number separator from number
let raw_number_str = raw_str.replace('_', "");
Ok((
lexical::parse_with_options::<f64, _, FORMAT>(
raw_str.as_bytes(),
raw_number_str.as_bytes(),
&lexical::parse_float_options::Options::from_radix(RADIX),
)
.expect("failed to parse float using lexical"),
LazyBigInt::new(raw_str),
LazyBigInt::new(raw_number_str),
raw_str,
non_octal,
))
}
@ -372,22 +418,24 @@ impl<'a, I: Input> Lexer<'a, I> {
"radix for read_int should be one of 2, 8, 10, 16, but got {}",
RADIX
);
if cfg!(feature = "debug") {
trace!("read_digits(radix = {}), cur = {:?}", RADIX, self.cur());
}
let start = self.cur_pos();
let mut total: Ret = Default::default();
let mut prev = None;
while let Some(c) = self.cur() {
if allow_num_separator && c == '_' {
let is_allowed = |c: Option<char>| {
if c.is_none() {
return false;
}
let c = c.unwrap();
c.is_digit(RADIX as _)
};
let is_forbidden = |c: Option<char>| {
@ -413,6 +461,8 @@ impl<'a, I: Input> Lexer<'a, I> {
// Ignore this _ character
self.input.bump();
raw.push(c);
continue;
}
@ -426,11 +476,15 @@ impl<'a, I: Input> Lexer<'a, I> {
raw.push(c);
self.bump();
let (t, cont) = op(total, RADIX, val)?;
total = t;
if !cont {
return Ok(total);
}
prev = Some(c);
}
@ -612,9 +666,12 @@ mod tests {
"10000000000000000000000000000000000000000000000000000n",
|l| l.read_number(false).unwrap().right().unwrap()
),
(
"10000000000000000000000000000000000000000000000000000"
.parse::<BigIntValue>()
.unwrap(),
String::from("10000000000000000000000000000000000000000000000000000n")
),
);
}

View File

@ -104,7 +104,7 @@ impl<'a> From<&'a Token> for TokenType {
| Token::Word(Word::Ident(..))
| Token::DollarLBrace
| Token::Regex(..)
| Token::BigInt(..)
| Token::BigInt { .. }
| Token::JSXText { .. }
| Token::RBrace
),

View File

@ -317,7 +317,7 @@ impl<'a, I: Tokens> Parser<I> {
| tok!("true")
| tok!("false")
| Token::Num(..)
| Token::BigInt(..)
| Token::BigInt { .. }
| Token::Str { .. } => {
return Ok(Box::new(Expr::Lit(self.parse_lit()?)));
}
@ -1834,10 +1834,11 @@ impl<'a, I: Tokens> Parser<I> {
}),
_ => unreachable!(),
},
Token::BigInt(..) => match bump!(self) {
Token::BigInt(value) => Lit::BigInt(BigInt {
Token::BigInt { .. } => match bump!(self) {
Token::BigInt { value, raw } => Lit::BigInt(BigInt {
span: span!(self, start),
value,
raw: Some(raw),
}),
_ => unreachable!(),
},

View File

@ -65,10 +65,11 @@ impl<'a, I: Tokens> Parser<I> {
}),
_ => unreachable!(),
},
Token::BigInt(_) => match bump!(p) {
Token::BigInt(value) => PropName::BigInt(BigInt {
Token::BigInt { .. } => match bump!(p) {
Token::BigInt { value, raw } => PropName::BigInt(BigInt {
span: span!(p, start),
value,
raw: Some(raw),
}),
_ => unreachable!(),
},

View File

@ -122,7 +122,10 @@ pub enum Token {
Num(f64),
#[kind(starts_expr)]
BigInt(BigIntValue),
BigInt {
value: BigIntValue,
raw: JsWord,
},
JSXName {
name: JsWord,
@ -611,7 +614,7 @@ impl Debug for Token {
Str { value, .. } => write!(f, "string literal ({})", value)?,
Regex(exp, flags) => write!(f, "regexp literal ({}, {})", exp, flags)?,
Num(..) => write!(f, "numeric literal")?,
BigInt(..) => write!(f, "bigint literal")?,
BigInt { .. } => write!(f, "bigint literal")?,
JSXName { name } => write!(f, "jsx name ({})", name)?,
JSXText { raw } => write!(f, "jsx text ({})", raw)?,
JSXTagStart => write!(f, "< (jsx tag start)")?,

View File

@ -86,7 +86,8 @@
[
1
]
]
],
"raw": "1n"
}
}
],

View File

@ -32,7 +32,8 @@
395401161,
37
]
]
],
"raw": "1000000000000000000000000000000000000000000000000000000000000000000000n"
}
}
],

View File

@ -790,7 +790,9 @@ impl ObjectRest {
}))),
}),
),
PropName::BigInt(BigInt { span, ref value }) => {
PropName::BigInt(BigInt {
span, ref value, ..
}) => {
let value = value.clone();
(
key,
@ -1003,7 +1005,7 @@ fn excluded_props(props: &[ObjectPatProp]) -> Vec<Option<ExprOrSpread>> {
value: format!("{}", value).into(),
})
.as_arg(),
PropName::BigInt(BigInt { span, value }) => Lit::Str(Str {
PropName::BigInt(BigInt { span, value, .. }) => Lit::Str(Str {
span: *span,
raw: None,
value: format!("{}", value).into(),

View File

@ -986,6 +986,7 @@ define!({
pub struct BigInt {
pub span: Span,
pub value: BigIntValue,
pub raw: Option<JsWord>,
}
pub struct Str {
pub span: Span,

View File

@ -317,6 +317,8 @@ pub struct BigIntLiteral {
pub base: BaseNode,
#[serde(default)]
pub value: String,
#[serde(default)]
pub raw: JsWord,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]

View File

@ -84,6 +84,11 @@ impl Babelify for BigInt {
BigIntLiteral {
base: ctx.base(self.span),
value: self.value.to_string(),
// TODO improve me
raw: match self.raw {
Some(value) => value,
_ => "".into(),
},
}
}
}

View File

@ -138,6 +138,8 @@ impl Swcify for BigIntLiteral {
.value
.parse()
.expect("failed to parse the value of BigIntLiteral"),
// TODO improve me
raw: None,
}
}
}