Skip to content

Commit 1535b3b

Browse files
authored
Fix: Enable based exponents (#456)
1 parent 531751f commit 1535b3b

1 file changed

Lines changed: 94 additions & 8 deletions

File tree

vhdl_lang/src/syntax/tokens/tokenizer.rs

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,18 +1409,46 @@ fn parse_abstract_literal(
14091409
let (integer, mut int_text) = base_result?;
14101410
base_text.append(&mut int_text);
14111411
base_text.push(b'#');
1412-
if (2..=16).contains(&base) {
1413-
Ok((
1414-
AbstractLiteral,
1415-
Value::AbstractLiteral(base_text, ast::AbstractLiteral::Integer(integer)),
1416-
))
1417-
} else {
1418-
Err(TokenError::range(
1412+
if !(2..=16).contains(&base) {
1413+
return Err(TokenError::range(
14191414
state.pos(),
14201415
pos_after_initial,
14211416
format!("Base must be at least 2 and at most 16, got {base}"),
1422-
))
1417+
));
14231418
}
1419+
// Optional exponent: value is base ** exp, per LRM 15.5.3
1420+
let value = if matches!(reader.peek()?, Some(b'e' | b'E')) {
1421+
base_text.push(reader.peek()?.unwrap());
1422+
reader.skip();
1423+
let (exp, mut exp_text) = parse_exponent(reader)?;
1424+
base_text.append(&mut exp_text);
1425+
if exp < 0 {
1426+
return Err(TokenError::range(
1427+
state.pos(),
1428+
reader.pos(),
1429+
"Integer literals may not have negative exponent",
1430+
));
1431+
}
1432+
match base
1433+
.checked_pow(exp as u32)
1434+
.and_then(|x| x.checked_mul(integer))
1435+
{
1436+
Some(v) => v,
1437+
None => {
1438+
return Err(TokenError::range(
1439+
state.pos(),
1440+
reader.pos(),
1441+
"Integer too large for 64-bit unsigned",
1442+
));
1443+
}
1444+
}
1445+
} else {
1446+
integer
1447+
};
1448+
Ok((
1449+
AbstractLiteral,
1450+
Value::AbstractLiteral(base_text, ast::AbstractLiteral::Integer(value)),
1451+
))
14241452
} else {
14251453
Err(TokenError::range(
14261454
state.pos(),
@@ -2695,6 +2723,64 @@ my_other_ident",
26952723
);
26962724
}
26972725

2726+
#[test]
2727+
fn tokenize_based_integer_with_exponent() {
2728+
// LRM 15.5.3: exponent is power of base
2729+
assert_eq!(
2730+
kind_value_tokenize("16#E#E1"),
2731+
vec![(
2732+
AbstractLiteral,
2733+
Value::AbstractLiteral(
2734+
Latin1String::new(b"16#E#E1"),
2735+
ast::AbstractLiteral::Integer(224)
2736+
)
2737+
),]
2738+
);
2739+
assert_eq!(
2740+
kind_value_tokenize("2#1#e4"),
2741+
vec![(
2742+
AbstractLiteral,
2743+
Value::AbstractLiteral(
2744+
Latin1String::new(b"2#1#e4"),
2745+
ast::AbstractLiteral::Integer(16)
2746+
)
2747+
),]
2748+
);
2749+
assert_eq!(
2750+
kind_value_tokenize("10#5#E+2"),
2751+
vec![(
2752+
AbstractLiteral,
2753+
Value::AbstractLiteral(
2754+
Latin1String::new(b"10#5#E+2"),
2755+
ast::AbstractLiteral::Integer(500)
2756+
)
2757+
),]
2758+
);
2759+
assert_eq!(
2760+
kind_value_tokenize("16#FF#E0"),
2761+
vec![(
2762+
AbstractLiteral,
2763+
Value::AbstractLiteral(
2764+
Latin1String::new(b"16#FF#E0"),
2765+
ast::AbstractLiteral::Integer(255)
2766+
)
2767+
),]
2768+
);
2769+
}
2770+
2771+
#[test]
2772+
fn tokenize_based_integer_negative_exponent() {
2773+
let code = Code::new("2#1#e-1");
2774+
let (tokens, _) = code.tokenize_result();
2775+
assert_eq!(
2776+
tokens,
2777+
vec![Err(Diagnostic::syntax_error(
2778+
code.pos(),
2779+
"Integer literals may not have negative exponent",
2780+
))]
2781+
);
2782+
}
2783+
26982784
#[test]
26992785
fn tokenize_illegal_integer() {
27002786
let code = Code::new("100k");

0 commit comments

Comments
 (0)