Skip to content

Normalize all n-ary operators in the RARE rewrite engine#125

Open
caotic123 wants to merge 2 commits into
ufmg-smite:mainfrom
caotic123:feat/rare-list-nary-normalization
Open

Normalize all n-ary operators in the RARE rewrite engine#125
caotic123 wants to merge 2 commits into
ufmg-smite:mainfrom
caotic123:feat/rare-list-nary-normalization

Conversation

@caotic123

@caotic123 caotic123 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Broadens RARE meta-term normalization from and/or to every n-ary (right-assoc-nil) operator carcara represents.

Stacked on #123 (rare-list checking / parameterized Sort::RareList). The bitvector cases cannot even be built without #123's compute_sort unwrapping, so this builds on it and also carries #124's And/Or engine refactor (which it supersedes). Until #123 lands, the diff here will also show #123's changes.

What

get_rules() now carries, per operator and in this order, a flatten rule (Op (RareList ..x..)) ~> (Op x), a singleton rule (Op x) ~> x, and — where the nil terminator is a static term — an empty rule (Op) ~> <nil>:

  • Arithmetic: + (nil 0), * (nil 1)
  • Strings: str.++ (nil "")
  • Regex: re.++, re.union (nil re.none), re.inter (nil re.all)
  • Bitvector: bvand, bvor, bvxor, bvadd, bvmul, concat

Flatten must precede singleton — check_rewrites returns the first match and (Op x) also matches a single rare-list argument, so flatten-first is what splices a list instead of collapsing it.

Width-dependent bitvector nils

Bitvector nil terminators depend on the operand width and can't be static constants. They're synthesized in bv_nil_if_empty: when an n-ary bitvector op's argument list becomes empty, recover the width and build the nil (0 for bvadd/bvor/bvxor, all-ones for bvand, 1 for bvmul, the zero-width empty bitvector for concat). When the width is genuinely unrecoverable (a bare operator with no operands), the term is left un-normalized rather than guessing.

Known limitations

  • re.++ empty case omitted (its nil (str.to_re "") is not a zero-arg term; flatten + singleton suffice).
  • (+)/(*) empties emit integer 0/1; Real-sorted empties would want 0.0/1.0 (only matters in the all-empty degenerate case).
  • set.* / Tuple are out of scope (absent from carcara's Operator enum).

Tiago Campos added 2 commits June 9, 2026 13:50
Change `Sort::RareList` from a unit variant to `Sort::RareList(Rc<Term>)`
so that a rare list carries the sort of its elements. This lets sort
computation, sort comparison, substitution, and the parser correctly
look through a rare list to its element sort (e.g. for bitvector width
inference, array select/store, polymorphism checks, and `:list`
parameters in RARE rules).

Also splice many-term reconstruction results into operator arguments in
`reconstruct_meta_terms`, which is required to expand `:list` arguments
when reconstructing a rewritten term.

Adds the `bv-concat-extract-merge` example (rule with bitvector `:list`
parameters) as a regression test and fixture.
Extend the meta-term rewrite engine to normalize every n-ary
(right-assoc-nil) operator that carcara represents, not just `and`/`or`:
arithmetic (`+`, `*`), strings (`str.++`), regular expressions (`re.++`,
`re.union`, `re.inter`), and bitvectors (`bvand`, `bvor`, `bvxor`,
`bvadd`, `bvmul`, `concat`).

For each operator `get_rules` now carries, in this order, a flatten rule
`(Op (RareList ..x..)) ~> (Op x)`, a singleton rule `(Op x) ~> x`, and —
for operators whose nil terminator is a static term — an empty rule
`(Op) ~> <nil>`. Flatten must precede singleton: `check_rewrites` returns
the first matching rule and `(Op x)` also matches a single rare-list
argument, so flatten-first is what splices a list instead of collapsing
it.

Bitvector nil terminators are width-dependent and cannot be written as
static constants, so they are synthesized in `bv_nil_if_empty`: when an
n-ary bitvector operator's argument list becomes empty, recover the
operand width and build the nil (0 for bvadd/bvor/bvxor, all-ones for
bvand, 1 for bvmul, the zero-width empty bitvector for concat). When the
width is genuinely unrecoverable (a bare operator with no operands) the
term is left un-normalized rather than guessing.

This builds on the parameterized `Sort::RareList` change: without it,
constructing a bitvector term that holds a rare-list argument fails sort
computation.

Adds engine unit tests for the arithmetic/string/regex/bitvector flatten,
singleton, empty, and nil-synthesis paths, and end-to-end `rare_rewrite`
checker tests for an arithmetic and a bitvector `:list` rule.

@bpandreotti bpandreotti left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, a few merge conflicts and formatting stuff. Otherwise, looks good to me.

Comment thread carcara/src/rare/mod.rs
Comment on lines -12 to -13
build_equation!((Or) ~> true),
build_equation!((And) ~> false),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why were these two nil cases removed? From the comment above, I assumed they were required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants