aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 09:48:11 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 09:48:11 +0000
commit98f9e13829204d32c7dea8a58f8f11efc2a38550 (patch)
treeb96940376e0f2f306ded70736018afa92f6c0c89 /src
parentMerge branch 'worktree-agent-a96a10dc1489688ea' into ccr-0054c02e-dfdvnd (diff)
zc0: byte string types, u/m/d as names, enhancements in asm
bstr locals, globals, parameters and returns are byte strings for + , the comparisons, indexing, slicing, len and put_s; b"..." globals; embed inside a function. u, m and d are ordinary names: a recast is the letter with the star touching it (u*x), tags .u .m .d are accepted on types and bare. an enhancement named inside asm { } emits its opcode. zc0's image of the genome and language server sources is unchanged.
Diffstat (limited to 'src')
-rw-r--r--src/tools/zc0.c175
1 files changed, 125 insertions, 50 deletions
diff --git a/src/tools/zc0.c b/src/tools/zc0.c
index b0ab29f..9faf883 100644
--- a/src/tools/zc0.c
+++ b/src/tools/zc0.c
@@ -4,8 +4,10 @@
the same image layout the resident compiler's image mode produces. the cores
embed that file with #embed (src/core/genome.h). no lifetime analysis, no quote, no
parse words, no block expressions: those only exist in compiler.zc. tags
- (m, d, -> u) and casts (as u, as d) are parsed and recorded in signatures
- but not checked. */
+ (.m and .d on parameters, .u on a return) are parsed and recorded in
+ signatures but not checked. u, m and d are ordinary names, as in the
+ resident compiler: a recast is the letter with the star touching it, u*x,
+ and it makes no code. */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
@@ -176,7 +178,10 @@ static void next(void)
ts[i] = 0;
if (i == 1 && ts[0] == 'b' && src[pos] == '"') {
pos++; i = 0;
- while (src[pos] && src[pos] != '"' && i < 4095) ts[i++] = (char)esc();
+ while (src[pos] && src[pos] != '"') {
+ if (i >= 4095) { die("a byte string literal is at most 4095 bytes; embed larger data"); break; }
+ ts[i++] = (char)esc();
+ }
ts[i] = 0; if (src[pos] == '"') pos++; tk = T_BSTR; return;
}
if (src[pos] == '.' && (!strcmp(ts,"rt") || !strcmp(ts,"run") || !strcmp(ts,"core") || !strcmp(ts,"lsp") || !strcmp(ts,"std") || !strcmp(ts,"icon"))) {
@@ -187,7 +192,14 @@ static void next(void)
tk = T_ID; return;
}
if (src[pos] == '\'') { pos++; tv = esc(); if (src[pos++] != '\'') die("bad char literal"); tk = T_NUM; strcpy(ts, "'c'"); return; }
- if (src[pos] == '"') { pos++; i = 0; while (src[pos] && src[pos] != '"') ts[i++] = (char)esc(); ts[i] = 0; pos++; tk = T_STR; return; }
+ if (src[pos] == '"') {
+ pos++; i = 0;
+ while (src[pos] && src[pos] != '"') {
+ if (i >= 4095) { die("a string literal is at most 4095 bytes"); break; }
+ ts[i++] = (char)esc();
+ }
+ ts[i] = 0; pos++; tk = T_STR; return;
+ }
tk = T_OP;
for (i = 0; ops3[i]; i++) if (!strncmp(src + pos, ops3[i], 3)) { strcpy(ts, ops3[i]); pos += 3; return; }
for (i = 0; ops2[i]; i++) if (!strncmp(src + pos, ops2[i], 2)) { strcpy(ts, ops2[i]); pos += 2; return; }
@@ -200,9 +212,16 @@ static int is(const char *s) { return (tk == T_OP || tk == T_ID) && !strcmp(ts,
static int accept(const char *s) { if (is(s)) { next(); return 1; } return 0; }
static void expect(const char *s) { if (!accept(s)) { char b[64]; sprintf(b, "expected '%s'", s); die(b); } }
static char *ident(void) { static char b[8][256]; static int k; char *r = b[k++ & 7]; if (tk != T_ID) die("expected a name"); strcpy(r, ts); next(); return r; }
+/* u, m and d are ordinary names. u*x, m*x and d*x are recasts when the star touches
+ the letter, which the raw character after the name tells: u * x is a product, and
+ so is u*=x. the current token is the name, so pos is just past it. */
+static int isrecast(void)
+{
+ return tk == T_ID && ts[1] == 0 && (ts[0] == 'u' || ts[0] == 'm' || ts[0] == 'd') && src[pos] == '*' && src[pos + 1] != '=';
+}
/* ---- symbols ---- */
-typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string */
+typedef struct { char name[64]; int kind, sig, defd, str; int64_t val; } Sym; /* str: a global holding a byte string, or a function returning one */
static Sym sy[16384]; static int nsy;
typedef struct { int at, sym; } Fix;
static Fix fx[65536]; static int nfx;
@@ -237,7 +256,7 @@ static void fnref(const char *n) /* push a function address, fixing it up later
}
/* ---- locals ---- */
-typedef struct { char name[64]; int slot, arr, flt, str; } Loc;
+typedef struct { char name[64]; int slot, arr, flt, str, dstr; } Loc; /* dstr: declared bstr, so always a byte string */
static Loc lo[1024]; static int nlo, nslot, infn;
static Loc *findl(const char *n) { int i; for (i = nlo; i--;) if (!strcmp(lo[i].name, n)) return &lo[i]; return 0; }
static void laddr(int k) { lit(ZFP); op(O_LD); if (k) { lit(k); op(O_ADD); } }
@@ -349,25 +368,32 @@ static int64_t cexpr(void)
for a growable handle or anything scalar. */
static int ptype0(void);
static int ptype(void);
+static int ptagname(void);
+static int ptstr; /* the type just read is bstr itself, with no stars, brackets or parameters after it */
static int ptype1(void)
{
- int n, e;
+ int n, e, isb;
+ ptstr = 0;
if (is("[")) { die("array types are postfix: T[N], T[] or T[?]"); return 1; }
if (is("fixed")) { next(); return ptype0(); }
if (is("fn")) { next(); return 1; } /* fn(int): the parameters follow, in ptype */
if (tk != T_ID) { die("a type name"); return 1; }
+ isb = !strcmp(ts, "bstr");
ident();
if (accept("<")) { /* generic arguments: vec<node*> */
+ isb = 0;
do { ptype(); } while (accept(","));
if (is(">>")) ts[1] = 0; /* >> closes two lists: leave the second > */
else expect(">");
}
+ ptstr = isb;
return 1;
}
static int ptype0(void)
{
int n = ptype1();
while (is("*") || is("[")) {
+ ptstr = 0;
if (accept("*")) n = 1;
else {
int count = 0, cap = 8, *dims = malloc(cap * sizeof(int));
@@ -390,19 +416,27 @@ static int ptype(void)
{
int n = ptype0();
while (accept("(")) {
- if (!accept(")")) { do { ptype(); if (accept(".")) ident(); } while (accept(",")); expect(")"); }
- n = 1;
+ if (!accept(")")) { do { ptype(); if (accept(".")) ptagname(); } while (accept(",")); expect(")"); }
+ n = 1; ptstr = 0;
}
return n;
}
+/* the name of an ownership tag, after its dot: 1 for m, 2 for d, 3 for u. zc0 does
+ nothing with them but record .m and .d on parameters and .u on a return. */
+static int ptagname(void)
+{
+ int t = 0;
+ if (is("m")) t = 1; else if (is("d")) t = 2; else if (is("u")) t = 3; else die("a tag is .u, .m or .d");
+ next();
+ return t;
+}
/* the ownership tag after a type, .u .m or .d: 0 when there is none */
static int ptag;
static void ptagopt(void)
{
ptag = 0;
if (!accept(".")) return;
- if (is("m")) ptag = 1; else if (is("d")) ptag = 2; else if (is("u")) ptag = 3; else die("a tag is .u, .m or .d");
- next();
+ ptag = ptagname();
}
/* an optional type in front of a name. the words it needs, or -1 when there is
no type here at all, which is how an untyped name is told from a type. the
@@ -423,7 +457,7 @@ static int notatype(void)
static int optype(void)
{
Lex L; int n;
- if (notatype() || (tk != T_ID && !is("["))) return -1;
+ if (notatype() || isrecast() || (tk != T_ID && !is("["))) return -1;
lexsave(&L); pguess = 1; pbad = 0; n = ptype(); ptagopt(); pguess = 0;
if (tk == T_ID && !pbad) return n;
lexrest(&L);
@@ -451,20 +485,26 @@ static void strlit(void) /* inline word string: jump over it, push its address *
for (i = 0; i < n; i++) emit((unsigned char)ts[i]);
emit(0); setlit(j, here); lit(a); next();
}
-/* b"...": length word, then bytes packed 8 per word, little-endian, plus a 0 */
-static void bstrlit(void)
+/* a byte string's words: the length, then the bytes packed 8 per word, little-endian,
+ and a 0 after the last byte when the length is a multiple of 8, as bstrdata does in
+ compiler.zc. the result is the address of the length word, which is the string. */
+static int bstrwords(const unsigned char *b, long n)
{
- int j = here, a, i = 0, k, n = (int)strlen(ts);
- const char *b = ts;
- uint64_t w;
- lit(0); op(O_JMP); a = here;
+ int a = here; long i = 0; int k; uint64_t w;
emit(n);
while (i < n) {
w = 0; k = 0;
- while (k < 8 && i < n) { w |= (uint64_t)(unsigned char)b[i] << (k * 8); k++; i++; }
+ while (k < 8 && i < n) { w |= (uint64_t)b[i] << (k * 8); k++; i++; }
emit((int64_t)w);
}
if ((n & 7) == 0) emit(0);
+ return a;
+}
+/* b"...": the words inline, jumped over, then the address pushed */
+static void bstrlit(void)
+{
+ int j = here, a;
+ lit(0); op(O_JMP); a = bstrwords((const unsigned char *)ts, (long)strlen(ts));
setlit(j, here); lit(a); next();
}
static int returns_bstr(const char *n)
@@ -503,7 +543,7 @@ static void primary(void)
else if (!strcmp(n, "len")) fnref("len_s");
else fnref(n);
op(O_CALL);
- xs = returns_bstr(n); xf = 0;
+ xs = returns_bstr(n) || (s && s->kind == K_FN && s->str); xf = 0;
return;
}
fnref(n);
@@ -512,7 +552,7 @@ static void incdec(int post, int d)
{
if (!lv) die("++/-- needs an lvalue");
lv = 0;
- if (lvi >= 0) lo[lvi].str = 0;
+ if (lvi >= 0) lo[lvi].str = lo[lvi].dstr;
xs = 0;
if (post) { lit(0); op(O_PICK); op(O_LD); lit(0); op(O_PICK); lit(d); op(O_ADD); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); }
else { lit(0); op(O_PICK); op(O_LD); lit(d); op(O_ADD); lit(0); op(O_PICK); lit(2); op(O_PICK); op(O_ST); op(O_SWAP); op(O_DROP); }
@@ -552,7 +592,7 @@ static void unary(void)
else if (accept("&")) { unary(); if (!lv) die("& needs an lvalue"); lv = 0; xs = 0; }
else if (accept("++")) { unary(); incdec(0, 1); }
else if (accept("--")) { unary(); incdec(0, -1); }
- else if (tk == T_ID && (!strcmp(ts, "u") || !strcmp(ts, "m") || !strcmp(ts, "d"))) { next(); expect("*"); unary(); } /* u*x: a recast, no code */
+ else if (isrecast()) { next(); expect("*"); unary(); rval(); lvi = -1; } /* u*x: a recast, no code, and not a place to store */
else postfix();
}
static void bin(int minp)
@@ -586,7 +626,7 @@ static void expr(void)
if (!lv) die("assignment needs an lvalue");
next(); expr(); rval();
op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0;
- if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs; }
+ if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = xs || lo[dst].dstr; }
} else if (tk == T_OP && strlen(ts) >= 2 && ts[strlen(ts) - 1] == '=' && strcmp(ts, "==") && strcmp(ts, "!=") && strcmp(ts, "<=") && strcmp(ts, ">=")) {
if (!lv) die("assignment needs an lvalue");
lf = xf; strcpy(o, ts); o[strlen(o) - 1] = 0; next();
@@ -594,7 +634,7 @@ static void expr(void)
if ((lf || xf) && fis(o)) fbinop(o, lf, xf);
else { binop(o); xf = 0; }
op(O_SWAP); lit(1); op(O_PICK); op(O_SWAP); op(O_ST); lv = 0;
- if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = 0; }
+ if (dst >= 0) { lo[dst].flt = xf; lo[dst].str = lo[dst].dstr; }
}
}
@@ -616,6 +656,7 @@ static void asmblock(void)
if (strlen(ts) == 1 && (c = strchr(mn, ts[0]))) { op((int)(c - mn)); next(); continue; }
if (tk != T_ID) die("bad asm token");
if ((l = findl(ts))) { laddr(l->slot); next(); continue; }
+ if ((s = find(ts)) && s->kind == K_ENH) { op(22 + (int)s->val); next(); continue; } /* an enhancement is its opcode */
if ((s = find(ts)) && s->kind != K_FN) { lit(s->val); next(); continue; }
if (s) { fnref(ts); next(); continue; }
strcpy(rn[nr], ts); ra[nr++] = here; lit(0); next(); /* a label of this block */
@@ -631,6 +672,7 @@ static void asmblock(void)
static int brk[256][64], nbrk[256], cont[256], nloop;
static int epi[1024], nepi;
static void stmt(void);
+static void embeddecl(int local);
static void block(void)
{
int save = nlo;
@@ -652,7 +694,14 @@ static void stmt(void)
if (accept(";")) return;
if (is("{")) { block(); return; }
{
- int nw = optype(), n;
+ int nw = optype(), n, tstr = 0;
+ if (nw >= 0) tstr = ptstr;
+ else if (is(".")) { next(); ptagname(); nw = 1; } /* a bare tag: .m p = new(3); */
+ else if (is("embed")) { /* embed "file" as [type] name; makes a local */
+ Lex L; lexsave(&L); next();
+ if (tk == T_STR) { embeddecl(1); return; }
+ lexrest(&L);
+ }
if (nw >= 0 || accept("let") || accept("var")) {
if (nw < 0) nw = 1;
do {
@@ -662,8 +711,8 @@ static void stmt(void)
if (accept("[")) { n = cexpr(); expect("]"); nslot += n; l->slot = nslot - n; l->arr = 1; }
else l->slot = nslot++;
}
- l->flt = 0; l->str = 0;
- if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs; }
+ l->flt = 0; l->dstr = tstr && !l->arr; l->str = l->dstr;
+ if (accept("=")) { expr(); rval(); laddr(l->slot); op(O_ST); l->flt = xf; l->str = xs || l->dstr; }
} while (accept(","));
expect(";"); return;
}
@@ -684,7 +733,7 @@ static void stmt(void)
if (accept("for")) {
int save = nlo;
expect("(");
- if (!is(";")) { if (is("let") || is("var") || tk == T_ID || is("[")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); }
+ if (!is(";")) { if (is("let") || is("var") || tk == T_ID || is("[") || is(".")) { stmt(); goto cond; } expr(); rval(); op(O_DROP); }
expect(";");
cond: c = here;
if (!is(";")) { expr(); rval(); ftest(); } else { lit(1); xf = 0; }
@@ -708,18 +757,18 @@ static void stmt(void)
}
/* ---- top level ---- */
-static void fndef(int ret)
+static void fndef(int ret, int rstr) /* rstr: the return type is bstr */
{
char *n = ident(); Sym *s; int np = 0, tags = 0, t, frame, i, helper, busy, done, check, node;
if (dsn && !dnm[0]) strcpy(dnm, n);
- char pn[16][64];
+ char pn[16][64]; int pstr[16];
char full_name[256]; qualify(full_name, sizeof full_name, n); s = find_raw(full_name);
if (!s || s->kind != K_FN) { s = add(n, K_FN, 0); s->defd = 0; }
expect("(");
if (!accept(")")) {
do {
- t = 0; ptag = 0;
- if (optype() >= 0) t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; /* the type is read and dropped */
+ t = 0; ptag = 0; pstr[np & 15] = 0;
+ if (optype() >= 0) { t = ptag == 1 ? 1 : ptag == 2 ? 2 : 0; pstr[np & 15] = ptstr; } /* the type is read and dropped */
else if (accept(".")) { /* .m name, .d name */
if (is("m")) t = 1; else if (is("d")) t = 2; else die("a parameter tag is .m or .d");
next();
@@ -729,7 +778,7 @@ static void fndef(int ret)
} while (accept(","));
expect(")");
}
- s->sig = ret + 2 * np + 64 * tags;
+ s->sig = ret + 2 * np + 64 * tags; s->str = rstr;
if (accept(";")) return; /* prototype */
if (s->defd) die("function defined twice");
s->val = here; s->defd = 1;
@@ -745,7 +794,7 @@ static void fndef(int ret)
}
#endif
nlo = 0; nslot = 0; nepi = 0; infn = 1;
- for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].str = 0; lo[nlo++].slot = nslot++; }
+ for (i = 0; i < np; i++) { strcpy(lo[nlo].name, pn[i]); lo[nlo].arr = 0; lo[nlo].flt = 0; lo[nlo].dstr = lo[nlo].str = pstr[i & 15]; lo[nlo++].slot = nslot++; }
/* retain the caller's pointer until the frame has its final segment. */
helper = !find("frame_busy") || !strcmp(s->name, "rt.frame_grow") || !strcmp(s->name, "rt.frame_leave");
lit(ZFP); op(O_LD); if (!helper) { lit(0); op(O_PICK); }
@@ -798,6 +847,9 @@ static void enhdef(void)
}
/* embed "path" as [type] name; at the top level: a global holding the file's bytes,
read when the image is built. the path is relative to the working directory.
+ in a function it declares a local: the bytes of a bstr or char* sit in the code,
+ jumped over, and the local starts as their address; a number is stored in the
+ local. an array takes the top level.
bstr (the default) packs the bytes; char* keeps one character to a word; a
number takes them little-endian; T[] and T[n] have one element per file chunk
of T's size. the checks are the resident compiler's (kw_embed). zc0 drops
@@ -813,9 +865,14 @@ static int embed_width(const char *t, int star)
if (!strcmp(t, "int") || !strcmp(t, "word") || !strcmp(t, "bool") || !strcmp(t, "float") || !strcmp(t, "f64")) return 8;
return 0;
}
-static void embeddecl(void)
+static Loc *newloc(const char *n)
{
- char path[1024], ty[64] = "", *n; int star = 0, arr = -2, w, i, a; long len; unsigned char *buf; FILE *f;
+ Loc *l = &lo[nlo++]; strcpy(l->name, n); l->slot = nslot++; l->arr = l->flt = l->str = l->dstr = 0;
+ return l;
+}
+static void embeddecl(int local)
+{
+ char path[1024], ty[64] = "", *n; int star = 0, arr = -2, w, i, a, j; long len; unsigned char *buf; FILE *f; Loc *l;
if (tk != T_STR) die("embed takes a path in quotes: embed \"file\" as name;");
snprintf(path, sizeof path, "%s", ts); next();
expect("as");
@@ -835,13 +892,23 @@ static void embeddecl(void)
if (!buf || fread(buf, 1, (size_t)len, f) != (size_t)len) die("can't read the file named by embed");
fclose(f);
if (arr == -2 && (!ty[0] || (!strcmp(ty, "bstr") && !star))) {
- a = here; add(n, K_GVAR, a)->str = 1; emit(0); img[a - GORG] = here; emit(len);
- for (i = 0; i < len; i += 8) { uint64_t u = 0; for (w = 0; w < 8 && i + w < len; w++) u |= (uint64_t)buf[i + w] << (w * 8); emit((int64_t)u); }
- if (!(len & 7)) emit(0);
+ if (local) {
+ j = here; lit(0); op(O_JMP); a = bstrwords(buf, len); setlit(j, here);
+ l = newloc(n); l->str = l->dstr = 1; lit(a); laddr(l->slot); op(O_ST);
+ } else {
+ a = here; add(n, K_GVAR, a)->str = 1; emit(0); w = bstrwords(buf, len); img[a - GORG] = w;
+ }
} else if (arr == -2 && !strcmp(ty, "char") && star == 1) {
- a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here;
- for (i = 0; i < len; i++) emit(buf[i]);
- emit(0);
+ if (local) {
+ j = here; lit(0); op(O_JMP); a = here;
+ for (i = 0; i < len; i++) emit(buf[i]);
+ emit(0); setlit(j, here);
+ l = newloc(n); lit(a); laddr(l->slot); op(O_ST);
+ } else {
+ a = here; add(n, K_GVAR, a); emit(0); img[a - GORG] = here;
+ for (i = 0; i < len; i++) emit(buf[i]);
+ emit(0);
+ }
} else if (arr == -2) {
uint64_t u = 0; w = embed_width(ty, star);
if (!w) die("embed declares a bstr, a char*, a number or an array of numbers");
@@ -851,9 +918,15 @@ static void embeddecl(void)
for (i = 0; i < len; i++) u |= (uint64_t)buf[i] << (i * 8);
if (ty[0] == 'i' && w < 8 && (u >> (w * 8 - 1) & 1)) u |= ~(uint64_t)0 << (w * 8);
if (!strcmp(ty, "f32")) u = fop_s2f(u);
- a = here; add(n, K_GVAR, a); emit((int64_t)u);
+ if (local) {
+ l = newloc(n); l->flt = !strcmp(ty, "float") || !strcmp(ty, "f64") || !strcmp(ty, "f32");
+ lit((int64_t)u); laddr(l->slot); op(O_ST);
+ } else {
+ a = here; add(n, K_GVAR, a); emit((int64_t)u);
+ }
} else {
int count; w = embed_width(ty, star);
+ if (local) die("zc0 embeds an array at the top level, not in a function");
if (!w) die("embed declares a bstr, a char*, a number or an array of numbers");
if (len % w) die("the embedded file's size is not a whole number of elements");
count = (int)(len / w);
@@ -880,18 +953,19 @@ static void top1(void)
expect(";"); return;
}
if (accept("include")) { do { ident(); } while (accept(",")); expect(";"); return; }
- if (accept("embed")) { embeddecl(); return; }
+ if (accept("embed")) { embeddecl(0); return; }
{
if (accept("unchecked")) { top1(); return; }
- if (accept("fn")) { fndef(0); return; }
- if (accept(".")) { if (strcmp(ident(), "u")) die("the only return tag is .u"); fndef(1); return; } /* .u name(...) */
+ if (accept("fn")) { fndef(0, 0); return; }
if (accept("const")) { optype(); do { char *n = ident(); if (dsn && !dnm[0]) strcpy(dnm, n); expect("="); add(n, K_CONST, cexpr()); } while (accept(",")); expect(";"); return; }
if (accept("enhance")) { enhdef(); return; }
{
- int nw, typed, k, a, rt;
- ptag = 0; nw = optype(); typed = nw >= 0; rt = ptag == 3;
+ int nw, typed, k, a, rt, gstr = 0; Sym *gs;
+ ptag = 0; nw = optype(); typed = nw >= 0; if (typed) gstr = ptstr; else ptag = 0;
+ if (!typed && is(".")) { next(); ptag = ptagname(); typed = 1; nw = 1; } /* .u name(...), or a bare tag on a global */
+ rt = ptag == 3;
if (typed || accept("let") || accept("var")) {
- if (isfn()) { fndef(rt); return; }
+ if (isfn()) { if (ptag && !rt) die("the only return tag is .u"); fndef(rt, gstr); return; }
do {
char *n = ident();
if (dsn && !dnm[0]) strcpy(dnm, n);
@@ -901,9 +975,10 @@ static void top1(void)
if (k > 1) {
a = here; while (k--) emit(0); add(n, K_GARR, a);
} else {
- a = here; add(n, K_GVAR, a); emit(0);
+ a = here; gs = add(n, K_GVAR, a); gs->str = gstr; emit(0);
if (accept("=")) {
if (tk == T_STR) { int i, l = (int)strlen(ts); img[a - GORG] = here; for (i = 0; i < l; i++) emit((unsigned char)ts[i]); emit(0); next(); }
+ else if (tk == T_BSTR) { int d = bstrwords((const unsigned char *)ts, (long)strlen(ts)); img[a - GORG] = d; gs->str = 1; next(); }
else img[a - GORG] = cexpr();
}
}