@cella-lang/c 0.1.202610130811 → 0.1.202610131327
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +7 -0
- package/README.md +39 -25
- package/c.cell +0 -0
- package/index.json +1246 -610
- package/package.json +5 -5
package/CHANGES.md
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
## 未發佈
|
|
6
6
|
|
|
7
|
+
- **不相容**:記憶體改成一份統一記憶體(照 CH2O)。`c.Mem t`、`c.memNew t`、`c.Allocated t` 換成 `c.CMem`、`c.memNew`、`c.Allocated`;指標 `c.Ptr` 是 `c.nullP` 或 `c.cptr 配置 路徑 索引`(不再是 cpp 的指標),指向 struct 成員的指標(`c.memberPtr m p "k"`)與指向變數、陣列格的指標是同一種,收 `int*`/`char*` 的函式對它們一樣運作;越過成員或陣列邊界的指標算術是越界。
|
|
8
|
+
- 讀寫與函式庫多了明確的元素型別參數:`c.load t m q ok`、`c.store t m q v ok`、`c.addrOf t m v`、`c.declArrM t m n z`、`c.idx t …`、`c.strlen pl t m q ok`、`c.strcmp pl t …`、`c.argS t m q`、`c.readableNB t …`、`c.readK t …`、`c.writeK t …`、`c.readable2 t …`、`c.at2 t …`、`c.qsort pl t …`、`c.bsearch pl t …`、`c.memset t …`、`c.memchr t …`、`c.strchr t …`……;`c.malloc ct m n fails`、`c.calloc`、`c.realloc` 收元素的 `c.CType`(整數是 `c.ctInt t`)。struct 不再有自己的記憶體(`c.smemNew` 拿掉),`c.arrowSet`、`c.arrow` 的前置條件是 `refl`(`c.objWritableB`、`c.objOkB`)。
|
|
9
|
+
- 前置條件 `c.DerefOk t m q`/`c.StoreOk t m q` 是 c 的資料型別(建構子 `c.derefOk`、`c.storeOk`,具體的值照舊 `refl`),條件寫在統一記憶體上(`c.readOkC`、`c.writeOkC`);`c.CStr t m q` 等字串前置條件是聚焦之後的 cpp 條件。原本收 `Mem t` 的版本留作平面層,名字加 F(`c.readKF`、`c.qsortF`、`c.readNF`、`c.readStrF`、`c.derefOkF`……)。
|
|
10
|
+
- 指標的指標:`c.addrOfPtr`、`c.loadP`、`c.storeP`(指標也在同一份記憶體);`c.Argv`、`c.TokRes`、`c.Fgot` 等不再帶平台或型別參數。
|
|
11
|
+
- 引理在新的表示上重證:`c.loadStore`、`c.storeDerefOk`、`c.writableAfterWriteK`、`c.writable2AfterWrite`、`c.mallocStoreOk`、`c.mallocFreeOk`、`c.fullDerefOk`/`c.loadFullK`、`c.loadToCpp`;新增 `c.storeObj`、`c.mallocObjWritable`。
|
|
12
|
+
- 經 `unsigned*` 讀 `int` 的物件(對應的有號/無號型別,直接或經 `void*`)判 unknown(`model_stricter`,`c.signVariantAccessB`);其他型別不符是嚴格別名,reject。
|
|
13
|
+
- 除錯器:`c.ptrText`、`c.varJsonPtr`。
|
|
7
14
|
- 模型比標準嚴的前置條件改由帶 `@[conservative]` 的定義表達:只卡在它們時判決是 **unknown**(`model_stricter`),不是 reject——讀不確定的值(`c.initElemB`、`c.initAtB`、`c.initNB`、`c.memberInitB`)、union 非作用中的成員(`c.uActiveB`)、`qsort` 比較相等的值相同(`c.qsortEqSameB`)、`bsearch` 至多一個相符(`c.bsearchUniqueB`)、對 `"r"` 開的檔案輸出(`c.notReadOnlyB`)、經 `void*` 用對應的有號/無號型別存取(`c.signVariantAccessB`)。翻譯器寫的證明照舊是 `refl`;以下前置條件的形狀改了(定義上相等,雜湊會變):
|
|
8
15
|
- `c.derefOk` 收 `c.readOkB m q = true`(活著且在界內 `cpp.liveB`,而且已初始化);`c.readableB`、`c.readableNB` 拆成「界內」與「已初始化」的 and;`c.scalar`/`c.ptrScalar` 收 `c.memberInitB x = true`;`c.uget` 收 `c.uActiveB ms u k = true`;`c.UncastOk` 收 `c.uncastOkB …`;`c.canWriteB` 是 `isOpenB` 與 `notReadOnlyB` 的 and;`c.qsortOkB`、`c.bsearchOkB` 併入比標準嚴的那一部分。
|
|
9
16
|
- 標準庫的 Int 名字改進 `Int` namespace(`add_int` → `Int.add`、`ltInt` → `Int.blt`、`addIntF` → `Int.addFast`…),套件的原始碼與產生的項都寫完整名稱。對照表見 cella-lang 的變更紀錄。
|
package/README.md
CHANGED
|
@@ -52,46 +52,53 @@ def s : c.get (c.at a (c.litV (c.tInt p) (pos 3) refl) (c.readable refl)) = pos
|
|
|
52
52
|
def t : c.Val (c.tInt p) := c.at a (c.litV (c.tInt p) (pos 5) refl) (c.readable refl) -- reject: a[5] is out of bounds
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
`c.
|
|
55
|
+
Memory follows CH2O (Krebbers, *The C standard formalized in Coq*): a program has one memory `c.CMem` (`c.memNew` is empty), every
|
|
56
|
+
allocation is a tree of values (integers, pointers, arrays, struct and union members), and a pointer `c.Ptr` is NULL or (allocation, path,
|
|
57
|
+
index) — the path walks struct members and array cells down to an array (a scalar counts as an array of length 1, 6.5.6p7), and the index is
|
|
58
|
+
the position in it. So `&x`, `&a[i]`, `&s.k` and `p->name` are the same kind of pointer, and a function taking `int *` or `char *` works on
|
|
59
|
+
all of them. `c.addrOf t m x` is `&x`, `c.load t m q (c.derefOk refl)` is `*q` (not NULL, allocation alive, in bounds, initialized, and the
|
|
60
|
+
object has type t — reading an `int` through a `long *` is strict aliasing, 6.5p7), `c.store t m q v (c.storeOk refl)` is `*q = v`, and
|
|
61
|
+
`c.memberPtr m p "k"` is `&p->k`. Arrays decay with `c.arrToMem`; `c.ptrAdd` / `c.idx` require the result to stay inside the same array or
|
|
62
|
+
one past its end — pointer arithmetic does not cross into the next member — and `c.ptrDiff` / `c.ptrLt` require both pointers to be in
|
|
63
|
+
the same array. `c.malloc ct m n fails` takes whether the allocation fails as a parameter (the environment's choice; failure returns NULL),
|
|
64
|
+
so a verdict must hold for both outcomes — dereferencing without a NULL check does not prove. `c.calloc` zero-fills; `c.free` requires NULL
|
|
65
|
+
or a live block from `malloc`, pointed at its start. `c.toVoid` / `c.fromVoid` convert through `void*`, back to the original type only.
|
|
66
|
+
`c.loadStore` proves that `*q = v; *q` reads `v` for any memory and pointer.
|
|
62
67
|
|
|
63
68
|
```cella
|
|
64
69
|
import c
|
|
65
70
|
def t : cpp.IType := cpp.tInt cpp.lp64P
|
|
66
|
-
def r : c.Allocated
|
|
67
|
-
def s : c.get (c.load (c.amem r) (c.aptr r) (c.derefOk refl)) = pos 5 := refl
|
|
68
|
-
def u : c.Val t := c.load
|
|
71
|
+
def r : c.Allocated := c.addrOf t c.memNew (c.litV t (pos 5) refl) -- int x = 5; int *q = &x;
|
|
72
|
+
def s : c.get (c.load t (c.amem r) (c.aptr r) (c.derefOk refl)) = pos 5 := refl -- accept: *q is 5
|
|
73
|
+
def u : c.Val t := c.load t c.memNew c.nullP (c.derefOk refl) -- reject: *NULL
|
|
69
74
|
```
|
|
70
75
|
|
|
71
76
|
Strings are NUL-terminated `char` arrays in that memory. `c.strArr p m "abc"` is `char s[] = "abc"`, `c.strArrN p m 10 "abc" refl` is
|
|
72
77
|
`char s[10] = "abc"` (zero-filled; exactly N bytes leaves no NUL), and `c.strLit p m "abc"` is `char *s = "abc"` — a read-only allocation, so
|
|
73
78
|
writing through it does not prove. `<string.h>`: `c.strlen`, `c.strcmp`, `c.strncmp`, `c.strcpy`, `c.strncpy` (writes no NUL when the source is
|
|
74
79
|
not shorter than n), `c.strcat`, `c.strchr`; their preconditions are `c.cstrOk refl` (a NUL is reachable), `c.cstrNOk refl` (the first n bytes
|
|
75
|
-
or a NUL), and `c.strcpyOk refl refl` etc. (the destination is writable and large enough, no overlap).
|
|
76
|
-
`c.argS m q` for `%s` (width, `-`, precision; with a
|
|
77
|
-
`isupper`, `islower`, `toupper`, `tolower`, whose argument
|
|
80
|
+
or a NUL), and `c.strcpyOk refl refl` etc. (the destination is writable and large enough, no overlap). They work on whatever array the pointer
|
|
81
|
+
is in, a member array included (`strcpy(p->name, "Ann")`). `printf` takes strings with `c.argS t m q` for `%s` (width, `-`, precision; with a
|
|
82
|
+
precision the array needs no NUL). `<ctype.h>`: `c.isdigit`, `isalpha`, `isspace`, `isupper`, `islower`, `toupper`, `tolower`, whose argument
|
|
83
|
+
must be an `unsigned char` value or `EOF` (`c.ctypeArg refl`).
|
|
78
84
|
|
|
79
85
|
```cella
|
|
80
86
|
import c
|
|
81
87
|
def p : cpp.Platform := c.lp64P
|
|
82
88
|
def C : cpp.IType := c.tChar p
|
|
83
|
-
def r : c.Allocated
|
|
84
|
-
def s : c.format p "[%-7s]" [c.argS (c.amem r) (c.aptr r)] refl = "[hello ]" := refl
|
|
85
|
-
def t : c.
|
|
89
|
+
def r : c.Allocated := c.strLit p c.memNew "hello"
|
|
90
|
+
def s : c.format p "[%-7s]" [c.argS C (c.amem r) (c.aptr r)] refl = "[hello ]" := refl -- accept
|
|
91
|
+
def t : c.CMem := c.store C (c.amem r) (c.aptr r) (c.litV C (pos 72) refl) (c.storeOk refl) -- reject: writes a string literal
|
|
86
92
|
```
|
|
87
93
|
|
|
88
94
|
Structs are described by their members, `c.mCons "x" (c.ctInt t) (c.mCons "next" c.ctPtr c.mNil)`, and a struct value has type
|
|
89
95
|
`c.Struct ms`; every scalar member may be uninitialized on its own. `c.initS ms [c.iInt x, …] refl` is an initializer list (missing members
|
|
90
96
|
are zero), `c.member ms "x" s` / `c.setMember ms "x" v s` access by name, and `c.scalar … refl` reads a member that has been initialized.
|
|
91
|
-
In memory,
|
|
92
|
-
`q->v
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
In memory, `c.varOf (c.ctStruct ms) s m` allocates a struct and `c.malloc (c.ctStruct ms) m n fails` allocates n of them; `c.arrow ms "v" m q refl`
|
|
98
|
+
is `q->v` and `c.arrowSet ms "v" x m q refl` is `q->v = x` — a `malloc`ed struct is initialized member by member, so this needs no other
|
|
99
|
+
member to be initialized. A member can also be reached through a pointer to it (`c.memberPtr`), so `swap(&s.a, &s.b)` is the same `swap` as
|
|
100
|
+
for two variables. Arrays of structs: `c.structArr`. A linked list whose every `malloc` may fail is in `tests/t_linked_list.cella`, and
|
|
101
|
+
pointers to members in `tests/t_member_ptr.cella`.
|
|
95
102
|
|
|
96
103
|
```cella
|
|
97
104
|
import c
|
|
@@ -99,23 +106,30 @@ def I : cpp.IType := c.tInt c.lp64P
|
|
|
99
106
|
def Point : c.Members := c.mCons "x" (c.ctInt I) (c.mCons "y" (c.ctInt I) c.mNil)
|
|
100
107
|
def p : c.Struct Point := c.initS Point [c.iInt (c.litV I (pos 1) refl)] refl
|
|
101
108
|
def y : c.get (c.scalar (c.member Point "y" p) refl) = pos 0 := refl -- accept: y is zero-filled
|
|
102
|
-
def u : c.Val I := c.scalar (c.member Point "y" (c.uninitS Point)) refl --
|
|
109
|
+
def u : c.Val I := c.scalar (c.member Point "y" (c.uninitS Point)) refl -- unknown: y is uninitialized (stricter than C)
|
|
110
|
+
def r : c.Allocated := c.varOf (c.ctStruct Point) p c.memNew -- struct Point s = {1};
|
|
111
|
+
def py : c.Ptr := c.memberPtr (c.amem r) (c.aptr r) "y" -- int *py = &s.y;
|
|
112
|
+
def z : c.get (c.load I (c.amem r) py (c.derefOk refl)) = pos 0 := refl -- accept: *py is 0
|
|
103
113
|
```
|
|
104
114
|
|
|
105
115
|
Array parameters are pointers, so a function does not know the length: `int sum(int a[], int n)` takes the precondition
|
|
106
|
-
`c.readableNB m a n = true` ("n cells after a are readable"), and `c.readK m a n ok k h` reads `a[k]` for `h : k < n` — the function is
|
|
116
|
+
`c.readableNB t m a n = true` ("n cells after a are readable"), and `c.readK t m a n ok k h` reads `a[k]` for `h : k < n` — the function is
|
|
107
117
|
checked once for any memory and pointer, and a caller with a concrete array proves the precondition by `refl`. Writing is the same with
|
|
108
118
|
`c.writableNB` / `c.writeK`, and `c.writableAfterWriteK` keeps the precondition through a loop. Pointers can be stored in memory
|
|
109
|
-
(`
|
|
119
|
+
(`c.addrOfPtr`, `c.loadP` for `*pp`), and `c.mkArgv p ["prog", "x"]` builds `argv` (NUL-terminated strings, a NULL at `argv[argc]`).
|
|
120
|
+
|
|
121
|
+
For a debugger, `c.ptrText names m q` shows a pointer the way the source would write it — `&x`, `a`, `&a[2]`, `a + 4`, `&s.k`, `NULL`
|
|
122
|
+
(`names` maps allocations to variable names; an unnamed allocation from `malloc` shows as `heap#b`) — and `c.varJsonPtr` gives a pointer
|
|
123
|
+
variable for the variables panel in the same JSON shape as the C++ and Python models.
|
|
110
124
|
|
|
111
125
|
Input: `c.scanf p "%d %s" [c.tgtInt t, c.tgtChars 8] st refl` reads from the input state shared with the C++ model (`cpp.ios "12 abc"`)
|
|
112
126
|
and returns what was read: `c.scanCount p r` (the number of assignments, or EOF), `c.assignInt r k t old` (the k-th assigned value, or the
|
|
113
127
|
old value when the conversion failed), `c.scannedBytes r k` and `c.scanSt r` (the rest of the input). The precondition rejects a target of the
|
|
114
128
|
wrong type, a result that does not fit (`%d` reading `99999999999`), and a `%s` that does not fit its array.
|
|
115
129
|
|
|
116
|
-
Multi-dimensional arrays: `c.arr2ToMem m R C inits refl` declares `T a[R][C]` (nested or flat initializers), and `c.at2 m a R C i j hi hj ok`
|
|
130
|
+
Multi-dimensional arrays: `c.arr2ToMem t m R C inits refl` declares `T a[R][C]` (nested or flat initializers), and `c.at2 t m a R C i j hi hj ok`
|
|
117
131
|
reads `a[i][j]` with the bounds checked per dimension — `a[0][C]` is out of bounds for the inner array even though it is inside the object.
|
|
118
|
-
A function taking `int a[][C]` gets `c.readable2 m a n C = true` and reads with `c.read2K`.
|
|
132
|
+
A function taking `int a[][C]` gets `c.readable2 t m a n C = true` and reads with `c.read2K`.
|
|
119
133
|
|
|
120
134
|
Floating point: `c.Double` and `c.Float` are IEEE 754 binary64 and binary32 (shared with the C++ model). `c.dlit m e` is the literal
|
|
121
135
|
m × 10^e; `c.toInt t x (c.truncOk refl)` truncates toward zero with the result required to fit; division needs a nonzero divisor.
|
package/c.cell
CHANGED
|
Binary file
|