From 7edcccdf822f72e0a11c6e907e120297590a70c6 Mon Sep 17 00:00:00 2001 From: dzaima Date: Sun, 4 Feb 2024 23:18:54 +0200 Subject: [PATCH] initial pointer objects --- src/ffi.c | 375 ++++++++++++++++++++++++++++++++--------- test/cases/ffi.bqn | 164 +++++++++++++----- test/cases/imports.bqn | 2 +- test/cases/syntax.bqn | 2 +- 4 files changed, 417 insertions(+), 126 deletions(-) diff --git a/src/ffi.c b/src/ffi.c index 18c8776b..451c67f0 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -167,8 +167,8 @@ typedef struct BoundFn { i32 xLen; // 0: length 0 array; else, ↑ #endif } BoundFn; -GLOBAL NFnDesc* boundFnDesc; -GLOBAL NFnDesc* foreignFnDesc; +STATIC_GLOBAL NFnDesc* boundFnDesc; +STATIC_GLOBAL NFnDesc* foreignFnDesc; B boundFn_c1(B t, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn1)c->w_c1)(makeX(inc(c->obj)), makeX(x))); } B boundFn_c2(B t, B w, B x) { BoundFn* c = c(BoundFn,t); return getB(((bqn_boundFn2)c->w_c2)(makeX(inc(c->obj)), makeX(w), makeX(x))); } @@ -234,20 +234,29 @@ DEF_FREE(ffiFn) { dec(((BoundFn*)x)->obj); } typedef struct BQNFFIEnt { union { - B o; // usual case - TAlloc* structData; // pointer stored in last element of cty_struct + struct { // regular BQNFFIEnt usage + union { + B o; // usual case + TAlloc* structData; // pointer stored in last element of cty_struct + }; + #if FFI==2 + ffi_type t; + #endif + // generic ffi_parseType ffi_parseTypeStr cty_ptr cty_repr + union { u8 extra; u8 onW; u8 canRetype; u8 mutPtr; u8 reType; }; + union { u8 extra2; u8 mutates; /*mutates*/ u8 reWidth; }; + union { + struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType + u16 offset; // cty_struct, cty_starr + }; + u16 staticOffset; // only at the top level; offset into allocation + }; + + struct { // second element of a pointer holder + void* ptrh_ptr; // actual pointer + ux ptrh_stride; // stride size + }; }; -#if FFI==2 - ffi_type t; -#endif - // generic ffi_parseType ffi_parseTypeStr cty_ptr cty_repr - union { u8 extra; u8 onW; u8 canRetype; u8 mutPtr; u8 reType; }; - union { u8 extra2; u8 mutates; /*mutates*/ u8 reWidth; }; - union { - struct { u8 wholeArg; u8 resSingle; }; // ffi_parseType - u16 offset; // cty_struct - }; - u16 staticOffset; // only at the top level; offset into allocation } BQNFFIEnt; typedef struct BQNFFIType { struct Value; @@ -328,14 +337,15 @@ static char* const sty_names[] = { [sty_f32]="f32", [sty_f64]="f64" }; enum CompoundTy { - cty_ptr, // *... / &... - cty_repr, // something:type - cty_struct, // {...} - cty_starr, // struct-based array - cty_tlarr, // top-level array + cty_ptr, // *... / &...; .a = {{.mutPtr = startsWith("&"), .o=eltype}} + cty_tlarr, // top-level array; {{.o=eltype}} + cty_struct, // {...}; .a = {{.o=el0}, {.o=el1}, ..., {.o=elLast}, {.structData = ffi_type**}} + cty_starr, // struct-based array; same as cty_struct + cty_repr, // something:type; {{.o=el, .t=rt, .reType=one of "icuf", .reWidth=bit width of retype}} + cty_ptrh, // data holder for pointer objects; {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}} }; -static B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) { +static NOINLINE B m_bqnFFIType(BQNFFIEnt** rp, u8 ty, usz ia) { BQNFFIType* r = mm_alloc(fsizeof(BQNFFIType, a, BQNFFIEnt, ia), t_ffiType); r->ty = ty; r->ia = ia; @@ -348,7 +358,7 @@ static u32 readUInt(u32** p) { u32* c = *p; u32 r = 0; while (*c>='0' & *c<='9') { - if (r >= U32_MAX/10 - 10) thrM("FFI: number literal too large"); + if (r >= U32_MAX/10 - 10) thrM("Type parser: number literal too large"); r = r*10 + *c-'0'; c++; } @@ -364,19 +374,19 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty u32 myWidth = 0; // used if parseRepr switch (c0) { default: - if (c0 == '\0') thrF("FFI: Error parsing type: Unexpected end of input"); - thrF("FFI: Error parsing type: Unexpected character '%c'", c0); + if (c0 == '\0') thrF("Type parser: Unexpected end of input"); + thrF("Type parser: Unexpected character '%c'", c0); case '[': { u32 n = readUInt(&c); - if (*c++!=']') thrM("FFI: Bad array type"); - if (n==0) thrM("FFI: 0-item arrays not supported"); + if (*c++!=']') thrM("Type parser: Bad array type"); + if (n==0) thrM("Type parser: 0-item arrays not supported"); BQNFFIEnt e = ffi_parseTypeStr(&c, top, false); if (top) { // largely copy-pasted from `case '*': case '&':` myWidth = sizeof(void*); BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_tlarr, 1); rp[0] = e; - if (n>U16_MAX) thrM("FFI: Top-level array too large; limit is 65535 elements"); + if (n>U16_MAX) thrM("Type parser: Top-level array too large; limit is 65535 elements"); c(BQNFFIType, ro)->arrCount = n; mut|= rp[0].mutates; parseRepr = rp[0].canRetype; @@ -398,8 +408,8 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty rt.type = FFI_TYPE_STRUCT; TALLOC(size_t, offsets, n); - if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("FFI: Failed getting array offsets"); - if (rt.size>=U16_MAX) thrM("FFI: Array too large; limit is 65534 bytes"); + if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting array offsets"); + if (rt.size>=U16_MAX) thrM("Type parser: Array too large; limit is 65534 bytes"); PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; c(BQNFFIType, ro)->structSize = rt.size; TFREE(offsets); @@ -412,7 +422,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty if (c0=='f') { if (n==32) rt = ffi_type_float; else if (n==64) rt = ffi_type_double; - else thrM("FFI: Bad float width"); + else thrM("Type parser: Bad float width"); ro = m_c32(n==32? sty_f32 : sty_f64); } else { u32 scty; @@ -420,7 +430,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty else if (n==16) { scty = c0=='i'? sty_i16 : sty_u16; rt = c0=='i'? ffi_type_sint16 : ffi_type_uint16; } else if (n==32) { scty = c0=='i'? sty_i32 : sty_u32; rt = c0=='i'? ffi_type_sint32 : ffi_type_uint32; } else if (n==64) { scty = c0=='i'? sty_i64 : sty_u64; rt = c0=='i'? ffi_type_sint64 : ffi_type_uint64; } - else thrM("FFI: Bad integer width"); + else thrM("Type parser: Bad integer width"); ro = m_c32(scty); } parseRepr = !inPtr; myWidth = sty_w[o2cG(ro)]; @@ -437,7 +447,7 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty case '*': case '&': { myWidth = sizeof(void*); - if (c0=='*' && (0==*c || ':'==*c)) { + if (c0=='*' && (0==*c || ':'==*c || '}'==*c || ','==*c)) { ro = m_c32(sty_ptr); parseRepr = !inPtr; canRetype = inPtr; @@ -458,10 +468,10 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty TSALLOC(BQNFFIEnt, es, 4); while (true) { BQNFFIEnt e = TSADD(es, ffi_parseTypeStr(&c, false, false)); - if (e.mutates) thrM("FFI: Structs currently cannot contain mutable references"); + if (e.mutates) thrM("Type parser: Structs currently cannot contain mutable references"); u32 m = *c++; if (m=='}') break; - if (m!=',') thrM("FFI: Improper struct separator or end"); + if (m!=',') thrM("Type parser: Improper struct separator or end"); } usz n = TSSIZE(es); BQNFFIEnt* rp; ro = m_bqnFFIType(&rp, cty_struct, n+1); @@ -478,8 +488,8 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty rt.type = FFI_TYPE_STRUCT; TALLOC(size_t, offsets, n); - if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("FFI: Failed getting struct offsets"); - if (rt.size>=U16_MAX) thrM("FFI: Struct too large; limit is 65534 bytes"); + if (ffi_get_struct_offsets(FFI_DEFAULT_ABI, &rt, offsets) != FFI_OK) thrM("Type parser: Failed getting struct offsets"); + if (rt.size>=U16_MAX) thrM("Type parser: Struct too large; limit is 65534 bytes"); PLAINLOOP for (usz i = 0; i < n; i++) rp[i].offset = offsets[i]; c(BQNFFIType, ro)->structSize = rt.size; TFREE(offsets); @@ -491,14 +501,14 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty u8 t = *c++; u32 n = readUInt(&c); if (t=='i' || t=='c') { - if (n!=8 & n!=16 & n!=32) { badW: thrF("FFI: Unsupported width in :%c%i", (u32)t, n); } + if (n!=8 & n!=16 & n!=32) { badW: thrF("Type parser: Unsupported width in :%c%i", (u32)t, n); } } else if (t=='u') { if (n!=1) goto badW; } else if (t=='f') { if (n!=64) goto badW; - } else thrM("FFI: Unexpected character after \":\""); + } else thrM("Type parser: Unexpected character after \":\""); - if (isC32(ro) && n > myWidth*8) thrF("FFI: Representation wider than the value for \"%S:%c%i\"", sty_names[o2cG(ro)], (u32)t, n); + if (isC32(ro) && n > myWidth*8) thrF("Type parser: Representation wider than the value for \"%S:%c%i\"", sty_names[o2cG(ro)], (u32)t, n); // TODO figure out what to do with i32:i32 etc B roP = ro; @@ -508,25 +518,32 @@ BQNFFIEnt ffi_parseTypeStr(u32** src, bool inPtr, bool top) { // parse actual ty *src = c; return (BQNFFIEnt){.t=rt, .o=ro, .canRetype=canRetype, .extra2=mut}; } - +static NOINLINE u32* parseType_pre(B arg, ux ia) { // doesn't consume; arg can be freed immediately after + TALLOC(u32, xp, ia+1); + xp[ia] = 0; + COPY_TO(xp, el_c32, 0, arg, 0, ia); + return xp; +} +static NOINLINE void parseType_post(u32* xp0, u32* xp, usz ia) { + if (xp0+ia != xp) thrM("Type parser: Garbage at end of type"); + TFREE(xp0); +} BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument side & other global decorators vfyStr(arg, "FFI", "type"); usz ia = IA(arg); if (ia==0) { - if (!forRes) thrM("FFI: Argument type empty"); + if (!forRes) thrM("Type parser: Type was empty"); return (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=false}; } arg = chr_squeezeChk(incG(arg)); - MAKE_MUT_INIT(tmp, ia+1, el_c32); MUTG_INIT(tmp); - mut_copyG(tmp, 0, arg, 0, ia); - mut_setG(tmp, ia, m_c32(0)); - u32* xp = tmp->a; - u32* xpN = xp + ia; + u32* xp0 = parseType_pre(arg, ia); + u32* xp = xp0; BQNFFIEnt t; if (xp[0]=='&' && xp[1]=='\0') { t = (BQNFFIEnt){.t = ffi_type_void, .o=m_c32(sty_void), .resSingle=true}; + xp+= 1; } else { u8 side = 0; bool whole = false; @@ -543,13 +560,14 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { // doesn't consume; parse argument t = ffi_parseTypeStr(&xp, false, true); // printI(arg); printf(": "); printFFIType(stdout, t.o); printf("\n"); - if (xp!=xpN) thrM("FFI: Garbage at end of type"); + t.onW = side; // keep .mutates t.wholeArg = whole; t.resSingle = false; } - mut_pfree(tmp, 0); + parseType_post(xp0, xp, ia); + decG(arg); return t; } @@ -614,16 +632,17 @@ NOINLINE B readU8Bits(B x) { usz ia=IA(x); u8* xp=tyarr_ptr(x); i16* rp; B r=m NOINLINE B readU16Bits(B x) { usz ia=IA(x); u16* xp=tyarr_ptr(x); i32* rp; B r=m_i32arrv(&rp, ia); for (usz i=0; iia-1; M_HARR(r, ia); for (usz i = 0; i < ia; i++) { void* c = ptr + t->a[i].offset; - HARR_ADD(r, i, readAny(t->a[i], c)); + HARR_ADD(r, i, readAny(t->a[i].o, c)); } return HARR_FV(r); } +B m_ptrobj_s(void* ptr, B o); // consumes o, sets stride to size of o +B m_ptrobj(void* ptr, B o, ux stride); // consumes o B readSimple(u8 resCType, u8* ptr) { B r; switch(resCType) { default: UD; // thrM("FFI: Unimplemented type"); case sty_void: r = m_c32(0); break; - case sty_ptr: thrM("FFI: \"*\" not yet implemented"); break; + case sty_ptr: r = m_ptrobj_s(*(void**)ptr, m_c32(sty_void)); break; case sty_a: r = getB(*(BQNV*)ptr); break; case sty_i8: r = m_i32(*( i8*)ptr); break; case sty_u8: r = m_i32(*( u8*)ptr); break; case sty_i16: r = m_i32(*(i16*)ptr); break; case sty_u16: r = m_i32(*(u16*)ptr); break; @@ -764,18 +784,19 @@ B readRe(BQNFFIEnt e, u8* ptr) { return makeRe(reT, reW, ptr, etw); } -B readAny(BQNFFIEnt e, u8* ptr) { - if (isC32(e.o)) { - return readSimple(o2cG(e.o), ptr); +B readAny(B o, u8* ptr) { // doesn't consume + if (isC32(o)) { + return readSimple(o2cG(o), ptr); } else { - BQNFFIType* t = c(BQNFFIType, e.o); + BQNFFIType* t = c(BQNFFIType, o); if (t->ty == cty_repr) { // cty_repr, scalar:x return readRe(t->a[0], ptr); - } else if (t->ty==cty_struct || t->ty==cty_starr) { // {...} - return readStruct(c(BQNFFIType, e.o), ptr); + } else if (t->ty==cty_struct || t->ty==cty_starr) { // {...}, [n]... + return readStruct(c(BQNFFIType, o), ptr); } } - thrM("FFI: Unimplemented struct field type for reading"); + + thrM("FFI: Unimplemented in-memory type for reading"); } B buildObj(BQNFFIEnt ent, bool anyMut, B* objs, usz* objPos) { @@ -894,9 +915,11 @@ B libffiFn_c2(B t, B w, B x) { BQNFFIType* t = c(BQNFFIType, ents[0].o); if (t->ty == cty_repr) { // cty_repr, scalar:x r = readRe(t->a[0], res); - } else { // {...} + } else if (t->ty == cty_struct) { // {...} r = readStruct(c(BQNFFIType, ents[0].o), res); - } + } else if (t->ty == cty_ptr) { // *... + r = m_ptrobj_s(*(void**)res, inc(t->a[0].o)); + } else UD; } TFREE(tmpAlloc); @@ -939,23 +962,25 @@ BQNFFIEnt ffi_parseType(B arg, bool forRes) { -static u64 atomSize(B chr) { +static u64 calcAtomSize(B chr) { return o2cG(chr)==sty_a? sizeof(BQNV) : sizeof(ffi_arg)>8? sizeof(ffi_arg) : 8; } -static u64 calcStaticSize(BQNFFIEnt e) { - if (isC32(e.o)) { // scalar - return atomSize(e.o); - } else { - BQNFFIType* t = c(BQNFFIType, e.o); - if (t->ty==cty_ptr || t->ty==cty_tlarr) return sizeof(void*); // *any / &any / top-level [n]any - else if (t->ty==cty_struct || t->ty==cty_starr) return t->structSize; // {...} - else if (t->ty==cty_repr) { // any:any - B o2 = t->a[0].o; - if (isC32(o2)) return atomSize(o2); - if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation"); - return sizeof(void*); - } else thrM("FFI: Unimplemented type (size calculation)"); - } +static NOINLINE u64 calcMemSizeComplex(B o) { + BQNFFIType* t = c(BQNFFIType, o); + if (t->ty==cty_ptr || t->ty==cty_tlarr) return sizeof(void*); // *any / &any / top-level [n]any + else if (t->ty==cty_struct || t->ty==cty_starr) return t->structSize; // {...} + else if (t->ty==cty_repr) { // any:any + B o2 = t->a[0].o; + if (isC32(o2)) return calcAtomSize(o2); + if (c(BQNFFIType,o2)->ty != cty_ptr) thrM("FFI: Bad type with reinterpretation"); + return sizeof(void*); + } else thrM("FFI: Unimplemented type (size calculation)"); +} +static u64 calcMemSize(B o) { + return isC32(o)? sty_w[o2cG(o)] : calcMemSizeComplex(o); +} +static u64 calcStaticSize(B o) { + return isC32(o)? calcAtomSize(o) : calcMemSizeComplex(o); } B ffiload_c2(B t, B w, B x) { @@ -974,20 +999,27 @@ B ffiload_c2(B t, B w, B x) { { B atomType; usz size; - if (isC32(tRes.o)) { atomType = tRes.o; goto calcRes; } + if (isC32(tRes.o)) { atomType = tRes.o; goto calcAtomSize; } BQNFFIType* t = c(BQNFFIType, tRes.o); if (t->ty == cty_repr) { B o2 = t->a[0].o; if (!isC32(o2)) thrM("FFI: Unimplemented result type"); atomType = o2; - goto calcRes; + goto calcAtomSize; } if (t->ty==cty_struct) { size = t->structSize; goto allocRes; } if (t->ty==cty_tlarr) thrM("FFI: Cannot return array"); + if (t->ty==cty_ptr) { + size = sizeof(void*); + goto allocRes; + } thrM("FFI: Unimplemented result type"); - calcRes:; size = atomSize(atomType); + calcAtomSize:; + size = calcAtomSize(atomType); + goto allocRes; + allocRes:; STATIC_ALLOC(tRes, size); } @@ -1000,7 +1032,7 @@ B ffiload_c2(B t, B w, B x) { for (usz i = 0; i < argn; i++) { BQNFFIEnt e = ffi_parseType(GetU(x,i+2), false); - STATIC_ALLOC(e, calcStaticSize(e)); + STATIC_ALLOC(e, calcStaticSize(e.o)); args[i+1] = e; if (e.wholeArg) whole[e.onW] = true; @@ -1063,7 +1095,9 @@ B ffiload_c2(B t, B w, B x) { #define FFI_TYPE_FLDS(OBJ, PTR) \ BQNFFIType* t = (BQNFFIType*) x; \ BQNFFIEnt* arr=t->a; usz ia=t->ia; \ - if (t->ty==cty_struct || t->ty==cty_starr) { \ + if (t->ty==cty_ptrh) { \ + ia = 1; \ + } if (t->ty==cty_struct || t->ty==cty_starr) { \ ia--; \ if (arr[ia].structData!=NULL) { \ PTR(arr[ia].structData); \ @@ -1089,9 +1123,184 @@ void ffiType_print(FILE* f, B x) { fprintf(f, "⟩"); } + + +STATIC_GLOBAL Body* ptrobj_ns; +STATIC_GLOBAL NFnDesc* ptrReadDesc; +STATIC_GLOBAL NFnDesc* ptrWriteDesc; +STATIC_GLOBAL NFnDesc* ptrCastDesc; +STATIC_GLOBAL NFnDesc* ptrAddDesc; +STATIC_GLOBAL NFnDesc* ptrSubDesc; +STATIC_GLOBAL NFnDesc* ptrFieldDesc; + +// ptrh - pointer holder - BQNFFIType object of {{.o=eltype}, {.ptrh_ptr, .ptrh_stride}} +// eltype of sty_void is used for an untyped pointer +static B ptrh_type(B n) { return c(BQNFFIType, n)->a[0].o; } +static BQNFFIEnt* ptrh_more(B n) { return &c(BQNFFIType, n)->a[1]; } +static void* ptrh_ptr(B n) { return ptrh_more(n)->ptrh_ptr; } +static ux ptrh_stride(B n) { return ptrh_more(n)->ptrh_stride; } + +B m_ptrobj_s(void* ptr, B o) { + return m_ptrobj(ptr, o, calcMemSize(o)); +} +B m_ptrobj(void* ptr, B o, ux stride) { + BQNFFIEnt* op; + B obj = m_bqnFFIType(&op, cty_ptrh, 2); + op[0].o = o; + op[1].ptrh_ptr = ptr; + op[1].ptrh_stride = stride; + + return m_nns(ptrobj_ns, + m_nfn(ptrReadDesc, incG(obj)), + m_nfn(ptrWriteDesc, incG(obj)), + m_nfn(ptrCastDesc, incG(obj)), + m_nfn(ptrAddDesc, incG(obj)), + m_nfn(ptrSubDesc, incG(obj)), + m_nfn(ptrFieldDesc, obj) + ); +} + +static B ptrobj_checkget_holder(B x) { // doesn't consume + if (!isNsp(x)) thrF("Expected pointer object, got %S", genericDesc(x)); + if (c(NS,x)->desc != ptrobj_ns->nsDesc) thrM("Expected pointer object, got some other kind of namespace"); + return nfn_objU(c(NS,x)->sc->vars[0]); +} +static NOINLINE void* ptrobj_checkget(B x) { + return ptrh_ptr(ptrobj_checkget_holder(x)); +} + +static void* ptrobj_elbase(B h, B off, bool negate) { // doesn't consume h, off doesn't matter + if (ptrh_type(h).u == m_c32(sty_void).u) thrM("Cannot offset an untyped pointer"); + char* ptr = ptrh_ptr(h); + i64 el = o2i64(off); + if (negate) el = -el; + return ptr + ptrh_stride(h)*el; +} +static B ptrobjRead_c1(B t, B x) { + B h = nfn_objU(t); + return readAny(ptrh_type(h), ptrobj_elbase(h, x, false)); +} +static B ptrobjWrite_c2(B t, B w, B x) { + B h = nfn_objU(t); + genObj(ptrh_type(h), x, false, ptrobj_elbase(h, w, false)); + dec(x); + return m_f64(1); +} +static B ptrobjWrite_c1(B t, B x) { return ptrobjWrite_c2(t, m_f64(0), x); } + +static B ptrobjCast_c1(B t, B x) { + vfyStr(x, "Type parser", "Pointer type"); + usz ia = IA(x); + void* ptr = ptrh_ptr(nfn_objU(t)); + if (ia==0) { + decG(x); + return m_ptrobj(ptr, m_c32(sty_void), 0); + } + + u32* xp0 = parseType_pre(x, ia); + u32* xp = xp0; + decG(x); + BQNFFIEnt parsed = ffi_parseTypeStr(&xp, false, false); + B r = m_ptrobj_s(ptr, parsed.o); + parseType_post(xp0, xp, ia); + return r; +} +static B ptrobjField_c1(B t, B x) { + B h = nfn_objU(t); + + u64 fld = o2u64(x); + B el = ptrh_type(h); + if (isC32(el)) thrM("Cannot get a field of a pointer to a scalar"); + BQNFFIType* elc = c(BQNFFIType, el); + + char* ptr = ptrh_ptr(h); + B elNew; + if (elc->ty == cty_starr || elc->ty == cty_struct) { + bool isStruct = elc->ty == cty_struct; + u64 n = elc->ia-1; + if (fld >= n) thrF(isStruct? "Cannot get field %l of a struct with %s fields" : "Cannot get pointer to element %l of an array with %s elements", fld, n); + BQNFFIEnt* fldptr = &elc->a[fld]; + elNew = inc(fldptr->o); + ptr+= fldptr->offset; + } else { + thrM("Pointer object: Field must be applied to a pointer to a struct or array"); + } + return m_ptrobj(ptr, elNew, ptrh_stride(h)); +} +static B ptrobjAdd_c1(B t, B x) { + B h = nfn_objU(t); + return m_ptrobj(ptrobj_elbase(h, x, false), inc(ptrh_type(h)), ptrh_stride(h)); +} +static B ptrty_simplify(B x) { + assert(!isC32(x)); + BQNFFIType* xt = c(BQNFFIType,x); + if (xt->ty == cty_repr) return xt->a[0].o; + return x; +} +static bool ptrty_equal(B a, B b) { + if (!isC32(a)) a = ptrty_simplify(a); + if (a.u == m_c32(sty_ptr).u) return true; + + if (!isC32(b)) b = ptrty_simplify(b); + if (b.u == m_c32(sty_ptr).u) return true; + if (isC32(a) || isC32(b)) return a.u==b.u; // if only one is a character, this test trivially fails + + BQNFFIType* at = c(BQNFFIType,a); + BQNFFIType* bt = c(BQNFFIType,b); + if (at->ty != bt->ty) return false; + switch (at->ty) { default: UD; + case cty_ptr: case cty_tlarr: { + return ptrty_equal(at->a[0].o, bt->a[0].o); + } + case cty_repr: { + UD; // should be handled by ptrty_simplify + // if (at->a[0].reType != bt->a[0].reType) return false; + // if (at->a[0].reWidth != bt->a[0].reWidth) return false; + // if (!ptrty_equal(at->a[0].o, bt->a[0].o)) return false; + // return true; + } + case cty_struct: case cty_starr: { + if (at->ia != bt->ia) return false; + ux n = at->ia-1; + for (ux i = 0; i < n; i++) { + if (!ptrty_equal(at->a[i].o, bt->a[i].o)) return false; + } + return true; + } + } +} +static B ptrobjSub_c1(B t, B x) { + B h = nfn_objU(t); + if (isNum(x)) return m_ptrobj(ptrobj_elbase(h, x, true), inc(ptrh_type(h)), ptrh_stride(h)); + if (isNsp(x)) { + B h2 = ptrobj_checkget_holder(x); + B t1 = ptrh_type(h); + B t2 = ptrh_type(h2); + if (t1.u==m_c32(sty_void).u || t2.u==m_c32(sty_void).u) thrM("(pointer).Sub ptr: Both pointers must be typed"); + ux stride = ptrh_stride(h); + if (stride!=ptrh_stride(h2)) thrM("(pointer).Sub ptr: Arguments must have the same stride"); + if (!ptrty_equal(t1, t2)) thrM("(pointer).Sub ptr: Arguments must have compatible types"); + ptrdiff_t diff = ptrh_ptr(h) - ptrh_ptr(h2); + ptrdiff_t eldiff = diff / (ptrdiff_t)stride; + if (eldiff*stride != diff) thrM("(pointer).Sub ptr: Distance between pointers isn't an exact multiple of stride"); + decG(x); + return m_f64(eldiff); + } + thrF("(pointer).Sub: Unexpected argument type: %S", genericDesc(x)); +} + + + void ffi_init(void) { boundFnDesc = registerNFn(m_c8vec_0("(foreign function)"), boundFn_c1, boundFn_c2); foreignFnDesc = registerNFn(m_c8vec_0("(foreign function)"), directFn_c1, directFn_c2); + ptrobj_ns = m_nnsDesc("read","write","cast","add","sub","field"); // first field must be an nfn whose object is the ptrh (needed for ptrobj_checkget_holder) + ptrReadDesc = registerNFn(m_c8vec_0("(pointer).Read"), ptrobjRead_c1, c2_bad); + ptrWriteDesc = registerNFn(m_c8vec_0("(pointer).Write"), ptrobjWrite_c1, ptrobjWrite_c2); + ptrCastDesc = registerNFn(m_c8vec_0("(pointer).Cast"), ptrobjCast_c1, c2_bad); + ptrAddDesc = registerNFn(m_c8vec_0("(pointer).Add"), ptrobjAdd_c1, c2_bad); + ptrSubDesc = registerNFn(m_c8vec_0("(pointer).Sub"), ptrobjSub_c1, c2_bad); + ptrFieldDesc = registerNFn(m_c8vec_0("(pointer).Field"), ptrobjField_c1, c2_bad); TIi(t_ffiType,freeO) = ffiType_freeO; TIi(t_ffiType,freeF) = ffiType_freeF; TIi(t_ffiType,visit) = ffiType_visit; diff --git a/test/cases/ffi.bqn b/test/cases/ffi.bqn index 0296d63b..940d8b6a 100644 --- a/test/cases/ffi.bqn +++ b/test/cases/ffi.bqn @@ -6,25 +6,26 @@ # bad •FFI invocation # generally weird !"•FFI: Path must be a list of characters" % 1‿2 •FFI ""‿"bqn_init" -"file that doesn't exist" •FFI⎊'e' ""‿"abort" %% 'e' -@ •FFI⎊'e' ""‿"bqn_this symbol doesn't exist" %% 'e' +!"This function can't be called monadically" % •FFI ""‿"bqn_init" +@ •FFI⎊{𝕊: 28↑•CurrentError@} ""‿"bqn_this symbol doesn't exist" %% "FFI: Failed to find symbol: " !"FFI: Type must be a string" % @•FFI "hello" -!"FFI: Error parsing type: Unexpected character '?'" % @•FFI""‿"bqn_init"‿"?" -!"FFI: Bad array type" % @•FFI""‿"bqn_init"‿"[" -!"FFI: Bad array type" % @•FFI""‿"bqn_init"‿"[0" -!"FFI: Error parsing type: Unexpected end of input" % @•FFI""‿"bqn_init"‿"[1]" -!"FFI: Error parsing type: Unexpected end of input" % @•FFI""‿"bqn_init"‿"{" -!"FFI: Bad integer width" % @•FFI""‿"bqn_init"‿"i" -!"FFI: Bad float width" % @•FFI""‿"bqn_init"‿"f80" -!"FFI: Bad float width" % @•FFI""‿"bqn_init"‿"f128" -!"FFI: Bad float width" % @•FFI""‿"bqn_init"‿"f16" -!"FFI: Bad float width" % @•FFI""‿"bqn_init"‿"f1" -!"FFI: Bad integer width" % @•FFI""‿"bqn_init"‿"u1" -!"FFI: Bad integer width" % @•FFI""‿"bqn_init"‿"i0" -!"FFI: Bad integer width" % @•FFI""‿"bqn_init"‿"i10" -!"FFI: Bad integer width" % @•FFI""‿"bqn_init"‿"u128" -!"FFI: number literal too large" % @•FFI""‿"bqn_init"‿"u99999999999999" !"FFI: Too many arguments" % @•FFI""‿"bqn_init"∾70000⥊<"i32" +!"Type parser: Unexpected character '?'" % @•FFI""‿"bqn_init"‿"?" +!"Type parser: Bad array type" % @•FFI""‿"bqn_init"‿"[" +!"Type parser: Bad array type" % @•FFI""‿"bqn_init"‿"[0" +!"Type parser: Unexpected end of input" % @•FFI""‿"bqn_init"‿"[1]" +!"Type parser: Unexpected end of input" % @•FFI""‿"bqn_init"‿"{" +!"Type parser: Bad integer width" % @•FFI""‿"bqn_init"‿"i" +!"Type parser: Bad float width" % @•FFI""‿"bqn_init"‿"f80" +!"Type parser: Bad float width" % @•FFI""‿"bqn_init"‿"f128" +!"Type parser: Bad float width" % @•FFI""‿"bqn_init"‿"f16" +!"Type parser: Bad float width" % @•FFI""‿"bqn_init"‿"f1" +!"Type parser: Bad integer width" % @•FFI""‿"bqn_init"‿"u1" +!"Type parser: Bad integer width" % @•FFI""‿"bqn_init"‿"i0" +!"Type parser: Bad integer width" % @•FFI""‿"bqn_init"‿"i10" +!"Type parser: Bad integer width" % @•FFI""‿"bqn_init"‿"u128" +!"Type parser: Type was empty" % @•FFI""‿"bqn_init"‿"" +!"Type parser: number literal too large" % @•FFI""‿"bqn_init"‿"u99999999999999" # >/𝕨/𝕩 !"FFI: Multiple occurrences of '>' within one argument" % @•FFI""‿"bqn_init"‿">>i32" @@ -37,38 +38,37 @@ !"FFI: Multiple arguments on 𝕨 specified, some with '>'" % @•FFI""‿"bqn_init"‿">𝕨i32"‿"𝕨i32" # arrays -!"FFI: 0-item arrays not supported" % @•FFI""‿"bqn_init"‿"[0]i32" -!"FFI: number literal too large" % @•FFI""‿"bqn_init"‿"[999999999999]i32" -!"FFI: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[65536]i32" -!"FFI: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[4000000000]i32" -!"FFI: Array too large; limit is 65534 bytes" % @•FFI""‿"bqn_init"‿"*[20000]i32" +!"Type parser: 0-item arrays not supported" % @•FFI""‿"bqn_init"‿"[0]i32" +!"Type parser: number literal too large" % @•FFI""‿"bqn_init"‿"[999999999999]i32" +!"Type parser: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[65536]i32" +!"Type parser: Top-level array too large; limit is 65535 elements" % @•FFI""‿"bqn_init"‿"[4000000000]i32" +!"Type parser: Array too large; limit is 65534 bytes" % @•FFI""‿"bqn_init"‿"*[20000]i32" !"FFI: Cannot return array" % @•FFI"[4]i32"‿"bqn_init" # structs -!"FFI: Improper struct separator or end" % @•FFI""‿"bqn_init"‿"{*i32*i32}" -!"FFI: Improper struct separator or end" % @•FFI""‿"bqn_init"‿"{*i32" -!"FFI: Error parsing type: Unexpected end of input" % @•FFI""‿"bqn_init"‿"{*i32," -!"FFI: Structs currently cannot contain mutable references" % @•FFI""‿"bqn_init"‿"{&i32}" +!"Type parser: Improper struct separator or end" % @•FFI""‿"bqn_init"‿"{*i32*i32}" +!"Type parser: Improper struct separator or end" % @•FFI""‿"bqn_init"‿"{*i32" +!"Type parser: Unexpected end of input" % @•FFI""‿"bqn_init"‿"{*i32," +!"Type parser: Structs currently cannot contain mutable references" % @•FFI""‿"bqn_init"‿"{&i32}" +# !"Type parser: Structs currently cannot contain mutable references" % @•FFI""‿"bqn_init"‿"{[2]&i32}" # TODO # : -!"FFI: Unexpected character after "":""" % @•FFI""‿"bqn_init"‿"i32:" -!"FFI: Unexpected character after "":""" % f←@•FFI""‿"bqn_init"‿">*u64:q8" -!"FFI: Unsupported width in :i0" % @•FFI""‿"bqn_init"‿"i32:i" -!"FFI: Unsupported width in :i9" % @•FFI""‿"bqn_init"‿"i32:i9" -!"FFI: Unsupported width in :u16" % @•FFI""‿"bqn_init"‿"i32:u16" -!"FFI: Unsupported width in :i64" % @•FFI"i32:i64"‿"bqn_init" -!"FFI: Unsupported width in :f32" % @•FFI""‿"bqn_init"‿"i32:f32" -!"FFI: number literal too large" % @•FFI""‿"bqn_init"‿"i32:u9999999999999999" -!"FFI: Representation wider than the value for ""i32:f64""" % @•FFI""‿"bqn_init"‿"i32:f64" -!"FFI: Garbage at end of type" % @•FFI""‿"bqn_init"‿"i32 hello world" +!"Type parser: Unexpected character after "":""" % @•FFI""‿"bqn_init"‿"i32:" +!"Type parser: Unexpected character after "":""" % f←@•FFI""‿"bqn_init"‿">*u64:q8" +!"Type parser: Unsupported width in :i0" % @•FFI""‿"bqn_init"‿"i32:i" +!"Type parser: Unsupported width in :i9" % @•FFI""‿"bqn_init"‿"i32:i9" +!"Type parser: Unsupported width in :u16" % @•FFI""‿"bqn_init"‿"i32:u16" +!"Type parser: Unsupported width in :i64" % @•FFI"i32:i64"‿"bqn_init" +!"Type parser: Unsupported width in :f32" % @•FFI""‿"bqn_init"‿"i32:f32" +!"Type parser: number literal too large" % @•FFI""‿"bqn_init"‿"i32:u9999999999999999" +!"Type parser: Representation wider than the value for ""i32:f64""" % @•FFI""‿"bqn_init"‿"i32:f64" +!"Type parser: Garbage at end of type" % @•FFI""‿"bqn_init"‿"i32 hello world" # return value !"FFI: Function specification must have at least two items" % @•FFI ⟨"&"⟩ !"FFI: Return value is specified as ""&"", but there are 0 mutated values" % @•FFI"&"‿"bqn_init"‿">*i32" !"FFI: Return value is specified as ""&"", but there are 2 mutated values" % @•FFI"&"‿"bqn_init"‿"&i32"‿"&i32" !"FFI: Cannot return array" % @•FFI"[4]i32"‿"bqn_init" -!"FFI: Unimplemented result type" % @•FFI"*i32"‿"bqn_init" -!"FFI: Unimplemented result type" % @•FFI"&i32"‿"bqn_init" # correct elements @@ -87,7 +87,7 @@ # bad array elements -!"FFI: Structs currently cannot contain mutable references" % %USE defs ⋄ f←@•FFI"&"‿"memcpy"‿"&{&i16}"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⋈∘⋈¨¯32768‿32767 +!"Type parser: Structs currently cannot contain mutable references" % size_t ← "u64" ⋄ size_tw ← 64 ⋄ f←@•FFI"&"‿"memcpy"‿"&{&i16}"‿"*i8"‿size_t ⋄ G←{F⟨𝕩,↕0,0⟩} ⋄ G ⋈∘⋈¨¯32768‿32767 !"FFI: Array provided for &u8 contained 299" % f←@•FFI"&"‿"bqn_init"‿">&u8" ⋄ F ↕300 !"FFI: Array provided for &u8 contained ¯99" % f←@•FFI"&"‿"bqn_init"‿">&u8" ⋄ F -↕100 @@ -166,11 +166,93 @@ # unimplemented stuff -!"FFI: ""*"" unimplemented" % f←@•FFI""‿"bqn_init"‿">*" ⋄ F 1 !"FFI: ""*i64"" argument type not yet implemented" % f←@•FFI""‿"bqn_init"‿">&i64" ⋄ F ↕10 !"FFI: ""*i64"" argument type not yet implemented" % f←@•FFI""‿"bqn_init"‿">*i64" ⋄ F ↕10 !"FFI: ""*u64"" argument type not yet implemented" % f←@•FFI""‿"bqn_init"‿">&u64" ⋄ F ↕10 !"FFI: Pointer element type not implemented" % f←@•FFI""‿"bqn_init"‿">**u64" ⋄ F ⟨↕2⟩ + + +# pointer objects +%DEF mm %USE defs ⋄ Alloc ← {1‿𝕩 {𝕏𝕨} @•FFI⟨"*"∾𝕨, "calloc", size_t, size_t⟩} ⋄ free ← @•FFI""‿"free"‿">*" +%DEF WallocE Cpy ← +´(2⋆8×↕8)×256|(@•FFI"*:i8"‿"memcpy"‿"*"‿"*"‿"u64"){𝔽} ⋄ {%USE mm ⋄ allocs←⟨⟩ ⋄ Clean←{Free¨allocs⋄𝕩} ⋄ Clean 𝕏⎊{𝕊: !•CurrentError Clean @} {{r ⊣ allocs∾↩ r← 𝕨 Alloc 𝕩}}} +%DEF Walloc Cpy ← +´(2⋆8×↕8)×256|(@•FFI"*:i8"‿"memcpy"‿"*"‿"*"‿"u64"){𝔽} ⋄ {%USE mm ⋄ allocs←⟨⟩ ⋄ Clean←{Free¨allocs⋄𝕩} ⋄ Clean 𝕏 {{r ⊣ allocs∾↩ r← 𝕨 Alloc 𝕩}}} + +# .Field +%USE Walloc {{𝕊al: 1∘("{i32,i32,i32}" Al 99).Field⎊0¨ ¯2+↕8}} %% 0‿0‿1‿1‿1‿0‿0‿0 +%USE Walloc {{𝕊al: 1∘("[5]u8" Al 99).Field⎊0¨ ¯2+↕9}} %% 0‿0‿1‿1‿1‿1‿1‿0‿0 +%USE Walloc {{𝕊al: 1∘("[5]u8" Al 99).Field⎊0¨ ⟨∞,¯∞,0÷0,@,"foo",{⇐}⟩}} %% 6⥊0 + +# .Write, .Read +%USE Walloc {{𝕊al: p←"{i16,i16,i16,i64}" Al 99 ⋄ p.Write ¯4‿10000‿¯30000‿7e12 ⋄ s←-○{Cpy 0∾˜⋈˜𝕩}´ ⟨p.Field 2, p⟩ ⋄ Cpy ⟨p.Add 1, p, s⟩ ⋄ p.Read¨ ↕2}} %% ⟨¯4‿10000‿¯30000‿7e12, ¯4‿10000‿0‿0⟩ +%USE Walloc {{𝕊al: p←"{i32,{i64,i64}}" Al 999 ⋄ {𝕩 p.Write ⟨𝕩×10,𝕩×20‿30⟩}¨ ↕4 ⋄ ((p.Field 1).Field 0).Read¨ ↕4 }} %% 20×↕4 +!"Cannot get a field of a pointer to a scalar" % %USE WallocE {{𝕊al: ("i32" Al 99).Field 0}} + +# garbage arguments +!"Expected integer, got character" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Add '!'}} +!"(pointer).Sub: Unexpected argument type: character" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Sub '!'}} +!"Expected non-negative integer, got character" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Field '!'}} +!"Type parser: Pointer type must be a string" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Cast '!'}} +!"Expected integer, got character" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Read '!'}} +!"FFI: improper value for i8" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Write '!'}} + +# untyped pointers +!"Cannot get a field of a pointer to a scalar" % %USE WallocE {{𝕊al: ("" Al 99).Field 0}} +# %USE Walloc {{𝕊al: p←"i32" Al 99 ⋄ p.Write 123 ⋄ (((p.Cast "").Add 0).Cast"i32").Read 0}} %% 123 +!"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 0}} +!"Cannot offset an untyped pointer" % %USE WallocE {{𝕊al: ("" Al 99).Add 1}} + +# .Add, .Sub, .Cast +%USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Cast "{i32,*i32}"}} %% 0 +%USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Add 123}} %% ¯123 +%USE Walloc {{𝕊al: p←"{i32,*}" Al 1 ⋄ p.Sub p.Sub 123}} %% 123 + +# ptr1.Sub ptr2 +!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p←"" Al 1 ⋄ p.Sub p}} +!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "").Sub p.Cast "{{i8,i16},i32}"}} +!"(pointer).Sub ptr: Both pointers must be typed" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "{{i8,i16},i32}").Sub p.Cast ""}} +!"(pointer).Sub ptr: Arguments must have the same stride" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ ((p.Cast "{{i8,i16},i32}").Field 0).Sub p.Cast "{i8,i16}"}} +!"(pointer).Sub ptr: Arguments must have compatible types" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ p.Sub p.Cast "u8"}} +!"(pointer).Sub ptr: Distance between pointers isn't an exact multiple of stride" % %USE WallocE {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "i32").Sub (p.Add 6).Cast "i32"}} +!"Type parser: Structs currently cannot contain mutable references" % @•FFI ""‿"bqn_init"‿"{&i8}" +# !"" % %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "{{i8,i16},i32}").Sub p.Cast "{{u8,i16},i32}"}} +# !"" % %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ (p.Cast "{{i8,*i16},i32}").Sub p.Cast "{{u8,*u16},i32}"}} + +# type comparison +%USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ t←"{{i8,i16,**i8,[12]i8,*:i32,i64:f64,*,*f64},i32,i64:c8}" ⋄ (p.Cast t).Sub p.Cast t}} %% 0 # ⎊-less test of most passing things to run under heapverify +%DEF TyEq {exp𝕊a‿b: %USE Walloc {{𝕊al: p←"i8" Al 1 ⋄ ! (exp⊑99‿0) ≡ ((p.Cast a).Sub)⎊99 p.Cast b}}} +1 %USE TyEq ⟨"{*i16}", "{*i16}"⟩ +0 %USE TyEq ⟨"{*i16}", "{*u16}"⟩ +1 %USE TyEq ⟨"{*i16}", "{*}"⟩ +1 %USE TyEq ⟨"{*}", "{*i16}"⟩ +1 %USE TyEq ⟨"{*:i8}", "{*i16}"⟩ +1 %USE TyEq ⟨"{*:i8}", "{*i16:i8}"⟩ +1 %USE TyEq ⟨"{*:i8}", "{*i16:i32}"⟩ + +1 %USE TyEq ⟨"{{i8,i16},i32}", "{{i8,i16},i32}"⟩ +0 %USE TyEq ⟨"{{i8,i16},i32}", "{{u8,i16},i32}"⟩ +1 %USE TyEq ⟨"{[3]i32,[5]i32}", "{[3]i32,[5]i32}"⟩ +1 %USE TyEq ⟨"{[3]i32,[5]i32}", "{[3]i32,[5]i32:i8}"⟩ +0 %USE TyEq ⟨"{[3]i32,[5]i32}", "{[3]i32,[5]u32}"⟩ +0 %USE TyEq ⟨"{[3]i32,[5]i32}", "{[5]i32,[3]i32}"⟩ +0 %USE TyEq ⟨"[4]i32", "[3]i32"⟩ +0 %USE TyEq ⟨"*[4]i32", "*[3]i32"⟩ +0 %USE TyEq ⟨"{i32}", "i32"⟩ +0 %USE TyEq ⟨"i16", "i32"⟩ +0 %USE TyEq ⟨"i16", "u16"⟩ +1 %USE TyEq ⟨"{i32:i8,i64}", "{i32:i8,i64}"⟩ +0 %USE TyEq ⟨"{i32:i8,i64}", "{u32:i8,i64}"⟩ +0 %USE TyEq ⟨"{i32:i8,i64}", "{i16:i8,i64}"⟩ +0 %USE TyEq ⟨"*{i32:i8}", "*{i16:i8}"⟩ +1 %USE TyEq ⟨"{i32:i8,i64}", "{i32:i16,i64}"⟩ +1 %USE TyEq ⟨"{i32:i32,i64}","{i32:u1,i64}"⟩ +1 %USE TyEq ⟨"{i32:i8,i64}", "{i32,i64}"⟩ +1 %USE TyEq ⟨"*i32:i8", "*"⟩ +1 %USE TyEq ⟨"*i32:i8", "*:c32"⟩ + + +# !"FFI: Unimplemented result type" % @•FFI"*i32"‿"bqn_init" +# !"FFI: Unimplemented result type" % @•FFI"&i32"‿"bqn_init" + # unchecked stuff -# !"FFI: Bad array corresponding to ""i64:c8"": expected 8 elements, got 1" % f←@•FFI""‿"bqn_init"‿">i64:c8" ⋄ F 8⥊2 \ No newline at end of file +# !"FFI: Bad array corresponding to ""i64:c8"": expected 8 elements, got 1" % f←@•FFI""‿"bqn_init"‿">i64:c8" ⋄ F 8⥊2 diff --git a/test/cases/imports.bqn b/test/cases/imports.bqn index 181081f7..8e585074 100644 --- a/test/cases/imports.bqn +++ b/test/cases/imports.bqn @@ -24,4 +24,4 @@ !"•Import: cyclic import of "".../cyclic_B.bqn""" % %USE file ⋄ •Import⎊CleanPath File "cyclic_B.bqn" !"•Import: cyclic import of "".../cyclic_A.bqn""" % %USE file ⋄ ⟨⟩•Import⎊CleanPath File "cyclic_B.bqn" !"•Import: cyclic import of "".../cyclic_B.bqn""" % %USE file ⋄ ⟨⟩•Import⎊CleanPath File "cyclic_A.bqn" -%USE file ⋄ 3 •Import File "notCyclic.bqn" %% 30 \ No newline at end of file +%USE file ⋄ 3 •Import File "notCyclic.bqn" %% 30 diff --git a/test/cases/syntax.bqn b/test/cases/syntax.bqn index 6136bf5b..e66faa7f 100644 --- a/test/cases/syntax.bqn +++ b/test/cases/syntax.bqn @@ -78,4 +78,4 @@ X←1‿2{a⇐𝕘-𝕗}3‿4 ⋄ ⟨a⟩←x ⋄ +´a %% 4 {𝕊: a←"ab" ⋄ ⟨(b←"c")∘"d", a, b⟩}¨ ↕4 %% 4⥊<⟨"c"∘"d", "ab", "c"⟩ {𝕊: a←"ab" ⋄ ⟨(a⋈↩"c")‿"d", a⟩}¨ ↕4 %% 4⥊<⟨⟨"ab","c"⟩‿"d", ⟨"ab","c"⟩⟩ {𝕊: a←"ab" ⋄ ⟨(a↩"c")‿"d", a⟩}¨ ↕4 %% 4⥊<⟨"c"‿"d", "c"⟩ -{𝕊: a←"ab" ⋄ ⟨(b←"c")‿"d", a, b⟩}¨ ↕4 %% 4⥊<⟨"c"‿"d", "ab", "c"⟩ \ No newline at end of file +{𝕊: a←"ab" ⋄ ⟨(b←"c")‿"d", a, b⟩}¨ ↕4 %% 4⥊<⟨"c"‿"d", "ab", "c"⟩