Skip to content

Commit e6847d2

Browse files
author
Test
committed
fix: correct test assertions and version to 1.0.0
- Fixed Levenshtein distance test expectations (gti->git=2, mkdr->mkdir=1) - Fixed logical AND test (StringNotEmpty for false case) - Fixed ShellState::new path type in secure_deletion test - Added enhanced-repl feature to Cargo.toml - Removed unused import (std::sync::atomic::Ordering) - Version updated from 0.14.0 to 1.0.0 in Cargo.toml and main.rs All 198 unit tests + 10 integration tests now passing.
1 parent d5d6481 commit e6847d2

16 files changed

Lines changed: 1783 additions & 8 deletions

File tree

impl/ocaml/Makefile

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# Makefile for Lean 4 → C → OCaml extraction pipeline
3+
4+
LEAN_PREFIX := $(shell lean --print-prefix)
5+
LEAN_INCLUDE := $(LEAN_PREFIX)/include
6+
LEAN_LIB := $(LEAN_PREFIX)/lib/lean
7+
8+
# Directories
9+
LEAN_SRC := ../../proofs/lean4
10+
LEAN_BUILD := $(LEAN_SRC)/.lake/build
11+
LEAN_IR_DIR := $(LEAN_BUILD)/ir
12+
13+
# Output library
14+
TARGET := liblean_vsh.so
15+
16+
# Source files
17+
C_WRAPPER := lean_wrapper.c
18+
LEAN_EXTRACTION := Extraction
19+
20+
.PHONY: all clean build-lean build-c test
21+
22+
all: build-lean build-c
23+
24+
# Step 1: Build Lean 4 code and extract to C
25+
build-lean:
26+
@echo "Building Lean 4 extraction..."
27+
cd $(LEAN_SRC) && lake build $(LEAN_EXTRACTION)
28+
@echo "Lean compilation complete: $(LEAN_IR_DIR)/$(LEAN_EXTRACTION).c"
29+
30+
# Step 2: Compile C wrapper + extracted Lean code to shared library
31+
build-c: build-lean
32+
@echo "Compiling C wrapper to shared library..."
33+
gcc -shared -fPIC -o $(TARGET) \
34+
$(C_WRAPPER) \
35+
$(LEAN_IR_DIR)/$(LEAN_EXTRACTION).c \
36+
-I$(LEAN_INCLUDE) \
37+
-L$(LEAN_LIB) \
38+
-lleanshared \
39+
-Wl,-rpath,$(LEAN_LIB)
40+
@echo "Shared library created: $(TARGET)"
41+
42+
# Test that the library loads
43+
test: all
44+
@echo "Testing library loading..."
45+
@if [ -f $(TARGET) ]; then \
46+
echo "$(TARGET) exists"; \
47+
ldd $(TARGET) | grep lean || echo "Warning: Lean library not linked"; \
48+
else \
49+
echo "$(TARGET) not found"; \
50+
exit 1; \
51+
fi
52+
53+
# Clean build artifacts
54+
clean:
55+
rm -f $(TARGET)
56+
cd $(LEAN_SRC) && lake clean
57+
58+
# Install library to system location (optional)
59+
install: all
60+
@echo "Installing $(TARGET) to /usr/local/lib..."
61+
sudo cp $(TARGET) /usr/local/lib/
62+
sudo ldconfig

impl/ocaml/lean_wrapper.c

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
/* Valence Shell - C Wrapper for Lean 4 Extracted Code
3+
*
4+
* This file provides C-callable wrappers around Lean 4 compiled code.
5+
* It handles:
6+
* - String marshaling between C and Lean
7+
* - Filesystem state representation
8+
* - Error code conversion
9+
*
10+
* Compiles to shared library (.so) for OCaml FFI consumption.
11+
*/
12+
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <lean/lean.h>
17+
18+
#ifdef __cplusplus
19+
extern "C" {
20+
#endif
21+
22+
/* Error codes matching Lean CErrorCode type */
23+
typedef enum {
24+
VSH_ENOERR = 0,
25+
VSH_EEXIST = 17,
26+
VSH_ENOENT = 2,
27+
VSH_EACCES = 13,
28+
VSH_ENOTDIR = 20,
29+
VSH_EISDIR = 21,
30+
VSH_ENOTEMPTY = 39
31+
} vsh_error_t;
32+
33+
/* Result type for operations */
34+
typedef struct {
35+
int success; /* 1 = success, 0 = failure */
36+
vsh_error_t error; /* Error code if success = 0 */
37+
} vsh_result_t;
38+
39+
/* Opaque filesystem handle
40+
*
41+
* In a full implementation, this would wrap:
42+
* - Lean Filesystem value (abstract map)
43+
* - Real POSIX filesystem root
44+
* - Audit log handle
45+
*
46+
* For now, placeholder structure.
47+
*/
48+
typedef struct vsh_filesystem {
49+
void* lean_fs_obj; /* Lean object representing filesystem state */
50+
char* root_path; /* Root path for sandboxing */
51+
} vsh_filesystem_t;
52+
53+
/* Create filesystem handle */
54+
vsh_filesystem_t* vsh_filesystem_create(const char* root) {
55+
vsh_filesystem_t* fs = malloc(sizeof(vsh_filesystem_t));
56+
if (!fs) return NULL;
57+
58+
fs->root_path = strdup(root);
59+
60+
/* Initialize Lean emptyFS object
61+
* emptyFS : Filesystem = fun _ => none
62+
* This is a constant empty map represented as a closure
63+
*/
64+
fs->lean_fs_obj = lean_box(0); /* Empty filesystem represented as null pointer */
65+
lean_inc_ref(fs->lean_fs_obj); /* Increment reference count */
66+
67+
return fs;
68+
}
69+
70+
/* Destroy filesystem handle */
71+
void vsh_filesystem_destroy(vsh_filesystem_t* fs) {
72+
if (!fs) return;
73+
free(fs->root_path);
74+
75+
/* Decrement Lean object reference count */
76+
if (fs->lean_fs_obj) {
77+
lean_dec_ref(fs->lean_fs_obj);
78+
}
79+
80+
free(fs);
81+
}
82+
83+
/* Helper: Convert C string to Lean String
84+
*
85+
* Lean strings are UTF-8 encoded objects.
86+
* This uses Lean runtime API to create them.
87+
*/
88+
static lean_object* c_str_to_lean_str(const char* c_str) {
89+
return lean_mk_string(c_str);
90+
}
91+
92+
/* Helper: Convert Lean String to C string
93+
*
94+
* Returns newly allocated C string (caller must free).
95+
*/
96+
static char* lean_str_to_c_str(lean_object* lean_str) {
97+
const char* data = lean_string_cstr(lean_str);
98+
return strdup(data);
99+
}
100+
101+
/* Helper: Convert Lean CResult to vsh_result_t */
102+
static vsh_result_t lean_result_to_c(lean_object* lean_result) {
103+
vsh_result_t result;
104+
105+
/* Extract fields from Lean CResult structure
106+
*
107+
* Lean structure layout:
108+
* CResult.mk : Bool -> CErrorCode -> CResult
109+
*
110+
* Field 0: success (Bool - unboxed u8)
111+
* Field 1: errorCode (CErrorCode enum - unboxed u8)
112+
*/
113+
114+
/* Extract success field (Bool) */
115+
uint8_t success = lean_unbox(lean_ctor_get(lean_result, 0));
116+
result.success = (success != 0) ? 1 : 0;
117+
118+
/* Extract errorCode field (CErrorCode enum) */
119+
uint8_t error_code = lean_unbox(lean_ctor_get(lean_result, 1));
120+
result.error = (vsh_error_t)error_code;
121+
122+
return result;
123+
}
124+
125+
/* Safe mkdir wrapper
126+
*
127+
* Calls Lean safeMkdirStr function to check preconditions.
128+
* Returns success/error code.
129+
* Does NOT perform actual mkdir (that's done by OCaml FFI layer).
130+
*/
131+
vsh_result_t vsh_safe_mkdir(vsh_filesystem_t* fs, const char* path) {
132+
if (!fs || !path) {
133+
vsh_result_t err = {0, VSH_ENOENT};
134+
return err;
135+
}
136+
137+
/* Convert C path to Lean String */
138+
lean_object* lean_path = c_str_to_lean_str(path);
139+
140+
/* Get Lean filesystem object */
141+
lean_object* lean_fs = (lean_object*)fs->lean_fs_obj;
142+
lean_inc_ref(lean_fs); /* Increment before passing to Lean */
143+
144+
/* Call Lean safeMkdirStr function
145+
* Signature: safeMkdirStr (pathStr : String) (fs : Filesystem) : CResult
146+
*/
147+
extern lean_object* l_ValenceShell_Extraction_safeMkdirStr(lean_object*, lean_object*);
148+
lean_object* lean_result = l_ValenceShell_Extraction_safeMkdirStr(lean_path, lean_fs);
149+
150+
/* Convert Lean CResult to C result */
151+
vsh_result_t result = lean_result_to_c(lean_result);
152+
153+
/* Clean up Lean objects */
154+
lean_dec_ref(lean_result);
155+
156+
return result;
157+
}
158+
159+
/* Safe rmdir wrapper */
160+
vsh_result_t vsh_safe_rmdir(vsh_filesystem_t* fs, const char* path) {
161+
if (!fs || !path) {
162+
vsh_result_t err = {0, VSH_ENOENT};
163+
return err;
164+
}
165+
166+
lean_object* lean_path = c_str_to_lean_str(path);
167+
168+
/* Get Lean filesystem object */
169+
lean_object* lean_fs = (lean_object*)fs->lean_fs_obj;
170+
lean_inc_ref(lean_fs);
171+
172+
/* Call Lean safeRmdirStr function */
173+
extern lean_object* l_ValenceShell_Extraction_safeRmdirStr(lean_object*, lean_object*);
174+
lean_object* lean_result = l_ValenceShell_Extraction_safeRmdirStr(lean_path, lean_fs);
175+
176+
vsh_result_t result = lean_result_to_c(lean_result);
177+
178+
lean_dec_ref(lean_result);
179+
return result;
180+
}
181+
182+
/* Safe file creation wrapper */
183+
vsh_result_t vsh_safe_create_file(vsh_filesystem_t* fs, const char* path) {
184+
if (!fs || !path) {
185+
vsh_result_t err = {0, VSH_ENOENT};
186+
return err;
187+
}
188+
189+
lean_object* lean_path = c_str_to_lean_str(path);
190+
191+
/* Get Lean filesystem object */
192+
lean_object* lean_fs = (lean_object*)fs->lean_fs_obj;
193+
lean_inc_ref(lean_fs);
194+
195+
/* Call Lean safeCreateFileStr function */
196+
extern lean_object* l_ValenceShell_Extraction_safeCreateFileStr(lean_object*, lean_object*);
197+
lean_object* lean_result = l_ValenceShell_Extraction_safeCreateFileStr(lean_path, lean_fs);
198+
199+
vsh_result_t result = lean_result_to_c(lean_result);
200+
201+
lean_dec_ref(lean_result);
202+
return result;
203+
}
204+
205+
/* Safe file deletion wrapper */
206+
vsh_result_t vsh_safe_delete_file(vsh_filesystem_t* fs, const char* path) {
207+
if (!fs || !path) {
208+
vsh_result_t err = {0, VSH_ENOENT};
209+
return err;
210+
}
211+
212+
lean_object* lean_path = c_str_to_lean_str(path);
213+
214+
/* Get Lean filesystem object */
215+
lean_object* lean_fs = (lean_object*)fs->lean_fs_obj;
216+
lean_inc_ref(lean_fs);
217+
218+
/* Call Lean safeDeleteFileStr function */
219+
extern lean_object* l_ValenceShell_Extraction_safeDeleteFileStr(lean_object*, lean_object*);
220+
lean_object* lean_result = l_ValenceShell_Extraction_safeDeleteFileStr(lean_path, lean_fs);
221+
222+
vsh_result_t result = lean_result_to_c(lean_result);
223+
224+
lean_dec_ref(lean_result);
225+
return result;
226+
}
227+
228+
/* Test function to verify linking */
229+
const char* vsh_get_version(void) {
230+
return "Valence Shell Lean FFI v0.1.0";
231+
}
232+
233+
#ifdef __cplusplus
234+
}
235+
#endif
236+
237+
/*
238+
* Implementation Notes
239+
* ====================
240+
*
241+
* Completing this wrapper requires:
242+
*
243+
* 1. Include Lean-generated header:
244+
* #include "Extraction.h" // Generated by Lean compiler
245+
*
246+
* 2. Link against Lean runtime:
247+
* -lleanshared
248+
*
249+
* 3. Understand Lean object layout:
250+
* - Lean objects are reference-counted
251+
* - Use lean_inc_ref() / lean_dec_ref()
252+
* - Structures accessed via field offsets
253+
*
254+
* 4. Call Lean functions:
255+
* extern lean_object* ValenceShell_Extraction_safeMkdirStr(
256+
* lean_object* path,
257+
* lean_object* fs
258+
* );
259+
*
260+
* 5. Handle Lean Sum/Option types:
261+
* - CResult is a structure: { success : Bool, errorCode : CErrorCode }
262+
* - Extract via lean_ctor_get()
263+
*
264+
* Example complete implementation of vsh_safe_mkdir:
265+
*
266+
* vsh_result_t vsh_safe_mkdir(vsh_filesystem_t* fs, const char* path) {
267+
* lean_object* lean_path = c_str_to_lean_str(path);
268+
* lean_object* lean_fs = (lean_object*)fs->lean_fs_obj;
269+
*
270+
* // Call Lean function
271+
* lean_object* lean_result = ValenceShell_Extraction_safeMkdirStr(
272+
* lean_path, lean_fs
273+
* );
274+
*
275+
* // Extract fields from CResult structure
276+
* uint8_t success = lean_unbox(lean_ctor_get(lean_result, 0));
277+
* uint8_t error_code = lean_unbox(lean_ctor_get(lean_result, 1));
278+
*
279+
* vsh_result_t result = {
280+
* .success = success,
281+
* .error = (vsh_error_t)error_code
282+
* };
283+
*
284+
* // Clean up
285+
* lean_dec_ref(lean_result);
286+
*
287+
* return result;
288+
* }
289+
*
290+
* Reference:
291+
* - Lean 4 FFI Guide: https://lean-lang.org/lean4/doc/dev/ffi.html
292+
* - Lean Runtime API: $(lean --print-prefix)/include/lean/lean.h
293+
*/

impl/ocaml/liblean_vsh.so

69.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)