summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorglenda <glenda@cirno>2022-09-18 10:02:25 +0000
committerglenda <glenda@cirno>2022-09-18 10:02:25 +0000
commit0719e6cf67a4644282f97431024f6a350b45ff46 (patch)
tree5b0841c0055d2ad09b1d79a74a9f3b1bad855290
parent917596b0ab7e0c15b38594c3cafa70f92719c231 (diff)
Implement ⎕SERIAL
-rw-r--r--quadnames.c99
-rw-r--r--symbol.c5
2 files changed, 102 insertions, 2 deletions
diff --git a/quadnames.c b/quadnames.c
index 8c7490c..143eef1 100644
--- a/quadnames.c
+++ b/quadnames.c
@@ -33,6 +33,7 @@ Array *quaducs(Array *);
Array *quaddl(Array *);
Array *quadthreads1(Array *);
Array *quadthreads2(Array *, Array *);
+Array *quadserial(Array *, Array *);
static Rune *quadquotebuf = nil;
static Array *session = nil;
@@ -57,6 +58,7 @@ QuadnameDef quadnames[] = {
{L"⎕UCS", FunctionTag, nil, nil, quaducs, nil},
{L"⎕DL", FunctionTag, nil, nil, quaddl, nil},
{L"⎕THREADS", FunctionTag, nil, nil, quadthreads1, quadthreads2},
+ {L"⎕SERIAL", FunctionTag, nil, nil, nil, quadserial},
{nil, 0, nil, nil, nil, nil} /* MUST BE LAST */
};
@@ -490,5 +492,102 @@ quadthreads2(Array *thread, Array *property)
return threadproperty(thread->intdata[0], property->intdata[0]);
}
+/* ⎕SERIAL */
+Array *
+quadserial(Array *mode, Array *a)
+{
+ if(GetType(mode) != AtypeInt || GetSize(mode) != 1)
+ throwerror(nil, EDomain);
+ int m = mode->intdata[0];
+ if(m != 0 && m != 1)
+ throwerror(L"Left argument of ⎕serial must be 0 or 1", EDomain);
+
+ /* TODO: byte ordering */
+ Array *result;
+ if(m == 0){ /* serialize */
+ Array *header = allocarray(AtypeInt, 1, 2+GetRank(a));
+ header->shape[0] = GetSize(header);
+ header->intdata[0] = GetType(a);
+ header->intdata[1] = GetRank(a);
+ for(int i = 0; i < GetRank(a); i++)
+ header->intdata[2+i] = a->shape[i];
+
+ Array *body;
+ if(GetType(a) == AtypeArray) /* nested */
+ body = rundfn(L"⊃,⌿0⎕SERIAL¨,⍵", nil, nil, nil, a);
+ else{
+ int len = datasizes[GetType(a)] * GetSize(a);
+ body = allocarray(AtypeInt, 1, len);
+ body->shape[0] = len;
+ for(int i = 0; i < len; i++)
+ body->intdata[i] = a->rawdata[i];
+ }
+
+ result = fnCatenateFirst(header, body);
+ freearray(header);
+ freearray(body);
+ }else{ /* un-serialize */
+ if(GetRank(a) != 1 || GetType(a) != AtypeInt)
+ throwerror(nil, EDomain);
+
+ int type = a->intdata[0];
+ int rank = a->intdata[1];
+ vlong size = 1;
+ for(int i = 0; i < rank; i++)
+ size *= a->intdata[i+2];
+
+ if(type == AtypeArray){ /* nested */
+ int skips[512];
+ int depth = 0;
+ Array *starts = allocarray(AtypeInt, 1, GetSize(a));
+ starts->shape[0] = GetSize(starts);
+ for(int i = 0; i < GetSize(starts); i++)
+ starts->intdata[i] = 0;
+ int offset = 2+rank;
+ skips[0] = 0;
+ while(offset < GetSize(a)){
+ if(depth == 0 && skips[0] == 0)
+ starts->intdata[offset] = 1;
+ int type = a->intdata[offset];
+ int rank = a->intdata[offset+1];
+ vlong size = 1;
+ for(int i = 0; i < rank; i++)
+ size *= a->intdata[i+offset+2];
+ offset += 2+rank;
+ if(type == AtypeArray){
+ depth++;
+ skips[depth] = size;
+ }else{
+ if(skips[depth] > 0)
+ skips[depth]--;
+ if(skips[depth] == 0 && depth > 0){
+ depth--;
+ if(skips[depth] > 0)
+ skips[depth]--;
+ }
+ offset += size*datasizes[type];
+ }
+ }
+ Array *parts = rundfn(L"1⎕SERIAL¨(+⍀⍺)⊆⍵", nil, nil, starts, a);
+ Array *shape = allocarray(AtypeInt, 1, rank);
+ shape->shape[0] = rank;
+ for(int i = 0; i < rank; i++)
+ shape->intdata[i] = a->intdata[2+i];
+ result = fnReshape(shape, parts);
+ freearray(parts);
+ freearray(starts);
+ }else{
+ result = allocarray(type, rank, size);
+ for(int i = 0; i < rank; i++)
+ result->shape[i] = a->intdata[i+2];
+
+ int len = datasizes[type] * size;
+ for(int i = 0; i < len; i++)
+ result->rawdata[i] = a->intdata[i+2+rank];
+ }
+ }
+
+ return result;
+}
diff --git a/symbol.c b/symbol.c
index 2f5165b..029ab0c 100644
--- a/symbol.c
+++ b/symbol.c
@@ -148,9 +148,10 @@ pushdfnframe(Rune *code, DfnFrame *scope, Datum *lefto, Datum *righto, Array *le
new->left->array = left;
incarrayref(left);
}else
- new->left = nil;
+ new->left = nil;
new->right = right;
- incarrayref(right);
+ if(right)
+ incarrayref(right);
new->prev = td->currentdfn;
new->chain = scope;
new->errorguards = nil;