Skip to content

Commit

Permalink
resolve #189: allow arbitrary expressions as slice size operands
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed Dec 28, 2023
1 parent 249e1c3 commit b767434
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 59 deletions.
73 changes: 71 additions & 2 deletions src/expr/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,44 @@ impl expr::Expr
}


pub fn eval_usize_with_ctx<'provider>(
&self,
report: &mut diagn::Report,
ctx: &mut EvalContext,
provider: EvalProvider)
-> Result<usize, ()>
{
self
.eval_with_ctx(
report,
ctx,
provider)?
.expect_usize(
report,
self.span())
}


pub fn try_eval_usize<'provider>(
&self)
-> Option<usize>
{
let value = self.eval_with_ctx(
&mut diagn::Report::new(),
&mut EvalContext::new(),
&mut dummy_eval_query);

if let Ok(expr::Value::Integer(bigint)) = value
{
bigint.maybe_into::<usize>()
}
else
{
None
}
}


pub fn eval_nonzero_usize<'provider>(
&self,
report: &mut diagn::Report,
Expand Down Expand Up @@ -636,12 +674,43 @@ impl expr::Expr
}
}

&expr::Expr::BitSlice(span, _, left, right, ref inner) =>
&expr::Expr::Slice(span, _, ref left_expr, ref right_expr, ref inner) =>
{
match propagate!(
inner.eval_with_ctx(report, ctx, provider)?).get_bigint()
{
Some(ref x) =>
{
let left = left_expr.eval_usize_with_ctx(report, ctx, provider)? + 1;
let right = right_expr.eval_usize_with_ctx(report, ctx, provider)?;

Ok(expr::Value::make_integer(
x.checked_slice(
report,
span,
left,
right)?))
}
None => Err(report.error_span("invalid argument type to slice", span))
}
}

&expr::Expr::SliceShort(span, _, ref size_expr, ref inner) =>
{
match propagate!(
inner.eval_with_ctx(report, ctx, provider)?).get_bigint()
{
Some(ref x) => Ok(expr::Value::make_integer(x.slice(left, right))),
Some(ref x) =>
{
let size = size_expr.eval_usize_with_ctx(report, ctx, provider)?;

Ok(expr::Value::make_integer(
x.checked_slice(
report,
span,
size,
0)?))
}
None => Err(report.error_span("invalid argument type to slice", span))
}
}
Expand Down
22 changes: 12 additions & 10 deletions src/expr/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ pub enum Expr
UnaryOp(diagn::Span, diagn::Span, UnaryOp, Box<Expr>),
BinaryOp(diagn::Span, diagn::Span, BinaryOp, Box<Expr>, Box<Expr>),
TernaryOp(diagn::Span, Box<Expr>, Box<Expr>, Box<Expr>),
BitSlice(diagn::Span, diagn::Span, usize, usize, Box<Expr>),
Slice(diagn::Span, diagn::Span, Box<Expr>, Box<Expr>, Box<Expr>),
SliceShort(diagn::Span, diagn::Span, Box<Expr>, Box<Expr>),
Block(diagn::Span, Vec<Expr>),
Call(diagn::Span, Box<Expr>, Vec<Expr>),
Asm(diagn::Span, Vec<syntax::Token>),
Expand Down Expand Up @@ -78,15 +79,16 @@ impl Expr
{
match self
{
&Expr::Literal (span, ..) => span,
&Expr::Variable (span, ..) => span,
&Expr::UnaryOp (span, ..) => span,
&Expr::BinaryOp (span, ..) => span,
&Expr::TernaryOp(span, ..) => span,
&Expr::BitSlice (span, ..) => span,
&Expr::Block (span, ..) => span,
&Expr::Call (span, ..) => span,
&Expr::Asm (span, ..) => span,
&Expr::Literal (span, ..) => span,
&Expr::Variable (span, ..) => span,
&Expr::UnaryOp (span, ..) => span,
&Expr::BinaryOp (span, ..) => span,
&Expr::TernaryOp (span, ..) => span,
&Expr::Slice (span, ..) => span,
&Expr::SliceShort(span, ..) => span,
&Expr::Block (span, ..) => span,
&Expr::Call (span, ..) => span,
&Expr::Asm (span, ..) => span,
}
}
}
Expand Down
45 changes: 38 additions & 7 deletions src/expr/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ impl expr::Expr

expr::Expr::Literal(_, expr::Value::Integer(util::BigInt { size: Some(size), .. })) =>
Some(*size),

expr::Expr::Literal(..) => None,

expr::Expr::UnaryOp(..) => None,

expr::Expr::BinaryOp(_, _, expr::BinaryOp::Concat, ref lhs, ref rhs) =>
{
Expand All @@ -91,9 +95,28 @@ impl expr::Expr

Some(lhs_size + rhs_size)
}

expr::Expr::BinaryOp(..) => None,

expr::Expr::BitSlice(_, _, left, right, _) =>
Some(left - right),
expr::Expr::Slice(_, _, left_expr, right_expr, _) =>
{
let left = left_expr.try_eval_usize()? + 1;
let right = right_expr.try_eval_usize()?;

if right > left
{
return None;
}

Some(left - right)
}

expr::Expr::SliceShort(_, _, size_expr, _) =>
{
let size = size_expr.try_eval_usize()?;

Some(size)
}

expr::Expr::TernaryOp(_, _, ref true_branch, ref false_branch) =>
{
Expand Down Expand Up @@ -134,8 +157,8 @@ impl expr::Expr
None
}
}
_ => None

expr::Expr::Asm(..) => None,
}
}

Expand Down Expand Up @@ -166,6 +189,8 @@ impl expr::Expr
}

expr::Expr::Literal(_, _) => true,

expr::Expr::UnaryOp(..) => false,

expr::Expr::BinaryOp(_, _, _, ref lhs, ref rhs) =>
{
Expand All @@ -175,7 +200,13 @@ impl expr::Expr
lhs_known && rhs_known
}

expr::Expr::BitSlice(_, _, _, _, ref expr) =>
expr::Expr::Slice(_, _, ref left_expr, ref right_expr, ref expr) =>
left_expr.is_value_statically_known(provider) &&
right_expr.is_value_statically_known(provider) &&
expr.is_value_statically_known(provider),

expr::Expr::SliceShort(_, _, ref size_expr, ref expr) =>
size_expr.is_value_statically_known(provider) &&
expr.is_value_statically_known(provider),

expr::Expr::TernaryOp(_, ref condition, ref true_branch, ref false_branch) =>
Expand Down Expand Up @@ -238,8 +269,8 @@ impl expr::Expr
false
}
}
_ => false

expr::Expr::Asm(..) => false,
}
}

Expand Down
56 changes: 18 additions & 38 deletions src/expr/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,13 @@ impl<'a, 'tokens> ExpressionParser<'a, 'tokens>
(syntax::TokenKind::Slash, expr::BinaryOp::Div),
(syntax::TokenKind::Percent, expr::BinaryOp::Mod)
],
|s| s.parse_bitslice())
|s| s.parse_slice())
}


fn parse_bitslice(&mut self) -> Result<expr::Expr, ()>
fn parse_slice(&mut self) -> Result<expr::Expr, ()>
{
let inner = self.parse_size()?;
let inner = self.parse_slice_short()?;

if self.walker.next_is_linebreak()
{ return Ok(inner); }
Expand All @@ -337,46 +337,29 @@ impl<'a, 'tokens> ExpressionParser<'a, 'tokens>
None => return Ok(inner)
};

let leftmost = {
let tk_leftmost = self.walker.expect(self.report, syntax::TokenKind::Number)?;
syntax::excerpt_as_usize(
self.report,
tk_leftmost.span,
tk_leftmost.excerpt.as_ref().unwrap())?
};
let leftmost = self.parse_expr()?;

self.walker.expect(self.report, syntax::TokenKind::Colon)?;

let rightmost = {
let tk_rightmost = self.walker.expect(self.report, syntax::TokenKind::Number)?;
syntax::excerpt_as_usize(
self.report,
tk_rightmost.span,
tk_rightmost.excerpt.as_ref().unwrap())?
};
let rightmost = self.parse_expr()?;

let tk_close_span = self.walker
.expect(self.report, syntax::TokenKind::BracketClose)?
.span;


let slice_span = tk_open.span.join(tk_close_span);
let span = inner.span().join(tk_close_span);

if leftmost < rightmost
{
self.report.error_span(
"invalid bit slice range",
slice_span);

return Err(());
}

Ok(expr::Expr::BitSlice(span, slice_span, leftmost + 1, rightmost, Box::new(inner)))
Ok(expr::Expr::Slice(
span,
slice_span,
Box::new(leftmost),
Box::new(rightmost),
Box::new(inner)))
}


fn parse_size(&mut self) -> Result<expr::Expr, ()>
fn parse_slice_short(&mut self) -> Result<expr::Expr, ()>
{
let inner = self.parse_unary()?;

Expand All @@ -389,16 +372,13 @@ impl<'a, 'tokens> ExpressionParser<'a, 'tokens>
None => return Ok(inner)
};

let tk_size = self.walker.expect(self.report, syntax::TokenKind::Number)?;
let size = syntax::excerpt_as_usize(
self.report,
tk_size.span,
tk_size.excerpt.as_ref().unwrap())?;

let span = inner.span().join(tk_size.span);
let size_span = tk_grave_span.join(tk_size.span);
let size = self.parse_leaf()?;

Ok(expr::Expr::BitSlice(span, size_span, size, 0, Box::new(inner)))
Ok(expr::Expr::SliceShort(
tk_grave_span.join(size.span()),
size.span(),
Box::new(size),
Box::new(inner)))
}


Expand Down
17 changes: 15 additions & 2 deletions src/test/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ fn test_ops_slice()
test("0xff`8", Pass(expr::Value::make_integer(util::BigInt::new(0xff, Some(8)))));
test("0x101`8", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));

test("0x101`8 + 1", Pass(expr::Value::make_integer(util::BigInt::new(0x2, None))));
test("0x101`(4 + 4)", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));
test("0x101`0x8", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));

test("0`0", Pass(expr::Value::make_integer(util::BigInt::new(0, Some(0)))));
test("0x0`0", Pass(expr::Value::make_integer(util::BigInt::new(0, Some(0)))));
test("0x100`0", Pass(expr::Value::make_integer(util::BigInt::new(0, Some(0)))));
Expand All @@ -247,6 +251,10 @@ fn test_ops_slice()
test("0x0f[8:1]", Pass(expr::Value::make_integer(util::BigInt::new(0x7, Some(8)))));
test("0xff[8:1]", Pass(expr::Value::make_integer(util::BigInt::new(0x7f, Some(8)))));

test("0x101[4 + 3:1 - 1]", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));
test("0x101[(4 + 3):(1 - 1)]", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));
test("0x101[0x7:0x0]", Pass(expr::Value::make_integer(util::BigInt::new(0x1, Some(8)))));

test("0x12345678[ 3: 0]", Pass(expr::Value::make_integer(util::BigInt::new(0x8, Some(4)))));
test("0x12345678[ 7: 4]", Pass(expr::Value::make_integer(util::BigInt::new(0x7, Some(4)))));
test("0x12345678[11: 8]", Pass(expr::Value::make_integer(util::BigInt::new(0x6, Some(4)))));
Expand All @@ -267,8 +275,13 @@ fn test_ops_slice()
test("-1[1000:1000]", Pass(expr::Value::make_integer(util::BigInt::new(1, Some(1)))));

test("0x00[0:7]", Fail(("test", 1, "invalid")));

test("0x00[0x1_ffff_ffff_ffff_ffff:7]", Fail(("test", 1, "large")));
test("0x00`{}", Fail(("test", 1, "expected integer")));
test("0x00`(1 == 2)", Fail(("test", 1, "expected integer")));
test("0x00`-1", Fail(("test", 1, "expected expression")));
test("0x00`(-1)", Fail(("test", 1, "out of supported range")));
test("0x00[7:-1]", Fail(("test", 1, "out of supported range")));
test("0x00[-1:-2]", Fail(("test", 1, "out of supported range")));
test("0x00[0x1_ffff_ffff_ffff_ffff:7]", Fail(("test", 1, "out of supported range")));
}


Expand Down
26 changes: 26 additions & 0 deletions src/util/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,11 @@ impl BigInt
right: usize)
-> BigInt
{
if left < right
{
panic!("invalid slice range");
}

let mut result = BigInt::from(0);

for i in 0..(left - right)
Expand All @@ -350,6 +355,27 @@ impl BigInt
}


pub fn checked_slice(
&self,
report: &mut diagn::Report,
span: diagn::Span,
left: usize,
right: usize)
-> Result<BigInt, ()>
{
if left < right
{
report.error_span(
"invalid slice range",
span);

return Err(());
}

Ok(self.slice(left, right))
}


pub fn concat(
&self,
lhs_slice: (usize, usize),
Expand Down
Loading

0 comments on commit b767434

Please sign in to comment.