Skip to content

Laurel Language Enhancements#385

Open
fabiomadge wants to merge 301 commits intomainfrom
jverify-strata-backend
Open

Laurel Language Enhancements#385
fabiomadge wants to merge 301 commits intomainfrom
jverify-strata-backend

Conversation

@fabiomadge
Copy link
Contributor

@fabiomadge fabiomadge commented Feb 4, 2026

Summary

Expands the Laurel language with additional operators, data types, and verification constructs. Improves formatting across multiple dialects.

Key Changes

Laurel Grammar & Translator

  • New operators: -, *, /, %, /t, %t (truncating), ==>, !, unary -
  • Array support: Array<T> type, indexing, length
  • Sequence operations: Seq.From, Seq.Take, Seq.Drop, Seq.Contains
  • Constrained types with constraint injection into quantifiers
  • Quantifiers: forall, exists
  • Return statement works with postconditions
  • While loops with multiple invariants
  • Multiple requires clauses per procedure

Infrastructure

  • NewlineSepBy separator type for formatting preservation
  • Java codegen: list separator preservation, SourceRange overloads
  • Improved pretty printing for C_Simp, B3, Core

CLI Commands

  • laurelParse, laurelAnalyze, laurelToCore, laurelPrint

Bug Fixes

  • Fixed SMT encoding for multi-argument functions and Map type
  • Added substFvarLifting for proper de Bruijn index handling

Tests

  • Reorganized Laurel tests T01-T17
  • Added regression test for assignment lifting

fabiomadge and others added 5 commits February 12, 2026 21:06
…aints

- Don't generate arrayElemAssumes for compound statements (Block, IfThenElse,
  While). Their sub-statements handle their own assumes via recursive
  translateStmt calls. Previously, assumes for array accesses inside branches
  were incorrectly hoisted before the if/while statement.
- Add TODO comment explaining why constraint injection is array-specific:
  Array.Get is a built-in (SMT select), not a procedure call, so there's
  no contract to assume from.
Copy link
Contributor

@joehendrix joehendrix left a comment

Choose a reason for hiding this comment

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

I added a few suggestions, but this looks reasonable to me. I'll approve this if you don't like the suggestions.

- Parser.lean: use Id.run do + early return for comment parsing, replace
  startsWith with utf8ByteSize check (comment 6)
- Java/Gen.lean: match on DDM category name (Init.Num/Init.Decimal) instead
  of Java type string for builder parameter generation (comment 7)
- Format.lean: collapse comma/space/newline seq formatting into single
  sepBy arm, eliminating duplication (comment 8)
- Java/Gen.lean: consolidate getSeparator with SepFormat.toIonName by
  adding fromCategoryName? and moving Ion-specific functions to
  Strata.DDM.Ion (comment 9)
@fabiomadge fabiomadge force-pushed the jverify-strata-backend branch from 80b2cc2 to 88d1a43 Compare February 13, 2026 02:21
@@ -1 +1 @@
/-
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the rename of this test?

body := body
}) .empty

def arrayTypeSynonym : Core.Decl := .type (.syn { name := "Array", typeArgs := ["T"], type := .tcons "Map" [.int, .ftvar "T"] })
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you move these static Core definition to CorePrelude.lean ?

explicitPreconditions := explicitPreconditions ++ [(s!"{proc.name}_pre_{i}", check)]
let preconditions := inputConstraints ++ arrayLenConstraints ++ explicitPreconditions
-- Generate constraint checks for output parameters with constrained types
let outputConstraints : List (Core.CoreLabel × Core.Procedure.Check) ←
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you generate the implicit pre and postconditions in a separate pass in a separate file?

| some expr => pure (some (s!"{proc.name}_input_{p.name}_constraint", { expr, md := p.type.md }))
| none => pure none)
-- Array lengths are implicitly >= 0
let arrayLenConstraints : List (Core.CoreLabel × Core.Procedure.Check) :=
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you move everything related to arrays to a separate file? Can you elaborate on the design of Array support? How come it's not a Laurel library? The length constraint seems like a type invariant or constrained type.


/-- Extract sequence bounds from Seq.From/Take/Drop chain.
Note: Seq.From only works with simple Identifier arguments; complex expressions are not supported. -/
def translateSeqBounds (env : TypeEnv) (expr : StmtExprMd) : Except String SeqBounds :=
Copy link
Contributor

Choose a reason for hiding this comment

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

Can everything related to sequences be in a separate file? Can it be a separate pass?

@@ -0,0 +1,42 @@
/-
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you move and Sequence support tests into a separate folder?

@keyboardDrummer
Copy link
Contributor

Can you add Laurel test Idiomaticity.lean that compares it's expectation against the generated Core code? That way it'll be easier to see how idiomatic the generated code is. I'm asking for this in this PR because some of the changes are pretty complicated and it's difficult for me to see from the translations what the end result is.

match outputParams.head? with
| some _ => do
let coreExpr ← translateExpr ctMap tcMap env stmt
mkReturnStmts (some coreExpr)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the scenario where we return "naturally" because we reach the end of a block? Since this is not a jump, should we not call something different than mkReturnStmt which does do a jump?

op assume (cond : StmtExpr) : StmtExpr => "assume " cond ";";
op return (value : StmtExpr) : StmtExpr => "return " value ";";
op block (stmts : Seq StmtExpr) : StmtExpr => @[prec(1000)] "{" stmts "}";
op block (stmts : NewlineSepBy StmtExpr) : StmtExpr => @[prec(1000)] "{" indent(2, "\n" stmts) "\n}";
Copy link
Contributor

@keyboardDrummer keyboardDrummer Feb 16, 2026

Choose a reason for hiding this comment

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

The indent(2, is never used anywhere, right? I don't think we're ever printing Laurel through the DDM. Why did you add this?

return commands
let (some tree, true) ← runChecked <| elabCommand leanEnv
| return commands
-- Prevent infinite loop if parser makes no progress
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great!!

| q`Init.SpacePrefixSepBy, 1 =>
let inner := mkCApp ``Array #[args[0]]
return if addAnn then mkCApp ``Ann #[inner, annType] else inner
| q`Init.NewlineSepBy, 1 =>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you say more about this NewlineSepBy operator? What is it for?

github-merge-queue bot pushed a commit that referenced this pull request Feb 16, 2026
Contains a subset of the changes from
#385

### Changes
- Fix to Strata/DDM/Parser.lean so it can handle parsing comments `//`
in languages that have a division operator
- New operators: `-`, `*`, `/`, `%`, `/t`, `%t` (truncating), `==>`,
`!`, unary `-`
- Quantifiers: `forall`, `exists`
- While loops with multiple invariants

### Testing
- Added tests for the new features

---------

Co-authored-by: Andrew Wells <130512013+andrewmwells-amazon@users.noreply.github.com>
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.

6 participants

Comments