Add basic support for del key word in Python->Laurel - #1054
Add basic support for del key word in Python->Laurel#1054thanhnguyen-aws wants to merge 14 commits into
del key word in Python->Laurel#1054Conversation
|
@keyboardDrummer-bot Please add similar support for list's negative index and slice. |
- Fix List_remove to handle negative indices (via List_remove_non_neg) - Add List_remove_slice for del xs[start:stop] - Add Any_remove_slice runtime function - Handle Slice case in translateDel - Add tests for del with negative index and slice
|
@thanhnguyen-aws I've opened PR #1055 with support for list negative index and slice in Changes:
Both new tests pass verification, and all existing del tests continue to pass. The PR targets |
|
@keyboardDrummer-bot Please merge PR #1055 to this PR. Don't create a new one. |
…' into supportdel
fec063c
| function List_remove_non_neg(l: ListAny, i: int) : ListAny | ||
| requires i >= 0 && i < List_len(l) | ||
| { | ||
| List_extend(List_take(l, i),List_drop(l, i + 1)) | ||
| }; | ||
|
|
||
| function List_remove(l: ListAny, i: int) : ListAny | ||
| requires i >= - List_len(l) && i < List_len(l) | ||
| { | ||
| if i >= 0 then List_remove_non_neg(l, i) | ||
| else List_remove_non_neg(l, List_len(l) + i) | ||
| }; | ||
|
|
||
| function List_remove_slice(l: ListAny, start: int, stop: int) : ListAny | ||
| { | ||
| List_extend( | ||
| List_take(l, if start >= 0 then int_min(start, List_len(l)) else int_max(List_len(l) + start, 0)), | ||
| List_drop(l, if stop >= 0 then int_min(stop, List_len(l)) else int_max(List_len(l) + stop, 0))) | ||
| }; |
There was a problem hiding this comment.
Proof coverage — four cheap theorems that would lock in Python semantics.
All four new runtime functions have closed-form element/length specifications. These can live in a new StrataTest/Languages/Python/PythonRuntimeLaurelPartProofs.lean or, more in keeping with the rest of the tree, in the existing Python runtime test file as #guard-style snapshots. rfl should handle most of them once the surface syntax is lowered.
-
List_remove_non_neg_length:∀ l i, 0 ≤ i < List_len(l) → List_len(List_remove_non_neg(l, i)) = List_len(l) - 1 -
List_remove_non_neg_get(the element-preservation property — this is the one that catches accidental off-by-one ini+1):∀ l i j, 0 ≤ i < List_len(l) → 0 ≤ j < List_len(l) - 1 → List_get(List_remove_non_neg(l, i), j) = if j < i then List_get(l, j) else List_get(l, j + 1) -
List_remove_slice_length(the one that would catch concern (1) above as a proof failure):∀ l start stop, let start_c = clamp(start, List_len(l)) let stop_c = clamp(stop, List_len(l)) List_len(List_remove_slice(l, start, stop)) = if start_c >= stop_c then List_len(l) else List_len(l) - (stop_c - start_c)With the current implementation, for
l=[1,2,3,4,5], start=3, stop=1, LHS = 7 and RHS = 5 — the proof doesn't go through, forcing a rewrite. -
List_remove_roundtrip(sanity check tyingList_remove_non_negandList_remove_slicetogether):∀ l i, 0 ≤ i < List_len(l) → List_remove_non_neg(l, i) = List_remove_slice(l, i, i + 1)
Even if only (3) lands, that's the regression-proof version of the concern above, and is the highest-value theorem in the set.
Similarly worth adding for DictStrAny_remove:
-
DictStrAny_remove_contains_false:∀ d k, DictStrAny_contains(DictStrAny_remove(d, k), k) = false -
DictStrAny_remove_other(other keys preserved):∀ d k k' v, k ≠ k' → DictStrAny_get(DictStrAny_remove(d, k), k') = DictStrAny_get(d, k') (when d contains k')
There was a problem hiding this comment.
I added the proofs for List's theorems in Laurel and add the guard test for them. The Dict theorems cannot be proved because it requires that the Dict is constructed by Dict_insert. The runtime is slow, so it is just a temporary approach. We need to have a Lean backend so that the theorems can be proved efficiently.
Co-authored-by: Michael Tautschnig <mt@debian.org>
Co-authored-by: Michael Tautschnig <mt@debian.org>
This PR adds
delstatement support to handlelist[index/slice]anddict[key]whenlistanddictare variables, which also supports:Negative indices:
del xs[-1]now correctly converts negative indices to positive before removal, following the same pattern asList_get.Slice deletion:
del xs[start:stop]removes a range of elements, keeping elements beforestartand fromstoponwards. Supports both positive and negative slice bounds.Changes
Runtime (
PythonRuntimeLaurelPart.lean):List_removeintoList_remove_non_neg(requiresi >= 0) andList_remove(handles negative-to-positive conversion)List_remove_slicefor removing a range of elementsAny_remove_slicewith exception handling and slice unpackingTranslator (
PythonToLaurel.lean):translateDelnow matches on.Sliceto callAny_remove_slice, falling back toAny_removefor single-index deletionTests:
test_del_list_negative_index.py:del xs[-1]on[1, 2, 3, 4]test_del_list_slice.py:del xs[1:3]on[1, 2, 3, 4, 5]All existing del tests continue to pass.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.