aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/compiler.zc124
1 files changed, 117 insertions, 7 deletions
diff --git a/src/compiler/compiler.zc b/src/compiler/compiler.zc
index 86e00c5..8335ab9 100644
--- a/src/compiler/compiler.zc
+++ b/src/compiler/compiler.zc
@@ -3981,6 +3981,44 @@ int dtype() {
}
return 0;
}
+// the struct whose body or methods are being compiled, when it declares constants: a bare
+// name there is looked up as Struct.name before the global scope
+char* mstruct;
+// does a constant declaration start in a struct body? const NAME = e; const T NAME = e;
+// (a field of a const type has no initializer, so the = tells them apart)
+int struct_const_start() {
+ if (!is_id(s_const)) return 0;
+ peekat(0); if (pk_tk != T_ID) return 0;
+ peekat(1);
+ if (pk_tk == T_OP && pk_tv == s_eq) return 1;
+ if (pk_tk == T_ID) { peekat(2); return pk_tk == T_OP && pk_tv == s_eq; }
+ if (pk_tk == T_OP && pk_tv == s_star) { peekat(2); if (pk_tk != T_ID) return 0; peekat(3); return pk_tk == T_OP && pk_tv == s_eq; }
+ return 0;
+}
+// const NAME = e, NAME = e; inside struct sn: a compile-time constant named Struct.NAME that takes no
+// space in the layout. the optional type is read and dropped, as in gconst.
+int struct_const(char* sn) {
+ int off, s; char* cn;
+ next();
+ if (tk == T_ID) { peekat(0); if (pk_tk == T_ID || (pk_tk == T_OP && pk_tv == s_star)) ptype(); }
+ mstruct = sn;
+ while (1) {
+ off = toff; cn = name();
+ s = lookup(methname(sn, cn));
+ if (s >= ubase && s >= 0) serrn(cn, "is defined twice in this struct");
+ expect(s_eq);
+ s = addsym(methname(sn, cn), K_CONST, cexpr());
+ rs_put(off, RS_SYM, rs_sym(s), s);
+ if (!is_op(s_comma) && !is_op(s_semi)) { // a call or a variable's value follows what cprim could read
+ serr("a struct constant must be a constant expression");
+ while (!is_op(s_semi) && !is_op(s_rb) && tk != T_EOF) next();
+ break;
+ }
+ if (!accept(s_comma)) break;
+ }
+ accept(s_semi);
+ return 0;
+}
// struct name { type field, ... } or struct name<A, B> { ... }
// the struct keyword. fields go in a scratch buffer first (name, type pairs),
// then are laid out: each field takes tsz(type) words, so offsets are words.
@@ -3989,7 +4027,7 @@ int dtype() {
// and call its siblings. a generic struct keeps its fields unresolved and
// can't have methods; each instance is laid out later by tresolve.
int kw_struct() {
- char* n; word t; int s, nf = 0; word buf = chk_resize(0, 0, 64); int bufcap = 64, i; word fs; int off = 0, gen = 0, ft, nm = 0, mcap = STRUCT_METHODS; char** mn = chk_resize(0, 0, mcap); word mq = chk_resize(0, 0, mcap), mst = chk_resize(0, 0, mcap), mty = chk_resize(0, 0, mcap), mrt = chk_resize(0, 0, mcap), mrg = chk_resize(0, 0, mcap); int bo, k, stat = 0; char* fname = 0;
+ char* n; word t; int s, nf = 0; word buf = chk_resize(0, 0, 64); int bufcap = 64, i; word fs; int off = 0, gen = 0, ft, nm = 0, mcap = STRUCT_METHODS; char** mn = chk_resize(0, 0, mcap); word mq = chk_resize(0, 0, mcap), mst = chk_resize(0, 0, mcap), mty = chk_resize(0, 0, mcap), mrt = chk_resize(0, 0, mcap), mrg = chk_resize(0, 0, mcap); int bo, k, stat = 0; char* fname = 0; char* msv = mstruct;
next();
n = module_symbol(name());
if (is_op(s_lt)) { // generic
@@ -4011,6 +4049,7 @@ int kw_struct() {
while (!is_op(s_rb)) {
if (tk == T_EOF) { die("missing }"); break; }
stat = 0;
+ if (struct_const_start()) { struct_const(n); continue; }
if (is_id(s_unchecked)) { next(); stat = 2; }
if (is_id(s_static)) { // static fn name(params) { body }, or static T name(params) { body }: a function of the struct, called as Struct.name(args), with no this
next();
@@ -4099,6 +4138,7 @@ int kw_struct() {
i = 0;
while (i < nm) { fn_method_compile = 1; methdef(n, mn[i], mq[i], mst[i], mty[i], mrt[i], mrg[i]); fn_method_compile = 0; i++; }
mdg = 0;
+ mstruct = msv;
return s;
}
// ---- methods ----
@@ -6800,6 +6840,7 @@ int primary() {
return C_PLAIN;
}
s = source_lookup(n);
+ if (mstruct && !ginst) { ms = lookup(methname(mstruct, n)); if (ms >= 0 && skind[ms] == K_CONST) s = ms; }
if (s < 0 && rt.eq_w(n, "new")) s = source_lookup(s_new);
if (s >= 0) rs_put(toff, RS_SYM, rs_sym(s), s);
if (s >= 0 && skind[s] == K_KW) {
@@ -6812,8 +6853,8 @@ int primary() {
c = toff + toklen;
next();
if (probe_off >= 0 && cmode && !dry && !ginst && incdepth == 0 && probe_off >= c && ((tk == T_ID && probe_off <= toff + toklen) || (tk != T_ID && probe_off <= toff))) { probe_static = n; probe_has = 3; chkabort = 1; }
- k = toff; mn = name();
- ms = lookup(methname(n, mn));
+ k = toff; sv = tpack(); mn = name();
+ ms = lookup(methname(sname[s], mn));
if (ms >= 0) rs_put(k, RS_SYM, rs_sym(ms), ms);
if (ms >= 0 && skind[ms] == K_CONST) { // Enum.MEMBER
xvk = 1; xvv = sval[ms];
@@ -6829,7 +6870,11 @@ int primary() {
if (!is_op(s_lp)) die("a static function is called with ( after its name");
return call(ms);
}
- serrn(mn, "is not a static function of this struct");
+ if (!panic && !dry) {
+ c = tpack(); tunpack(sv); // the error sits on the member name, where the quick fix replaces it
+ errat(); rt.put_s(mn); rt.put_s(": is not a static function of this struct"); suggest_member(sname[s], 0, mn, 1); errend();
+ tunpack(c);
+ }
lit(0);
xty = tword;
return C_PLAIN;
@@ -6949,7 +6994,7 @@ int incdec(int post, int dlt) {
word field(int c) {
int readonly = treadonly(xty);
if_value = 0;
- int n, fieldoff; word st;
+ int n, fieldoff, fieldpos, sv; word st;
word borrowed = xaf, base_mask = effect_mask(c); af_use(xaf, tpack(), c); xaf = 0; xvk = 0; int i, b = xbor, k = 0, scanned = 0, ms, de, pmatch = 0;
de = toff + toklen;
next();
@@ -6958,7 +7003,7 @@ word field(int c) {
if (tk == T_ID) { if (probe_off <= toff + toklen) pmatch = 1; }
else if (probe_off <= toff) pmatch = 1;
}
- n = name(); fieldoff = nameoff;
+ fieldpos = tpack(); n = name(); fieldoff = nameoff;
st = xty;
if (st[0] == TY_ARR || st[0] == TY_DYN) {
if (pmatch) probe_member(st);
@@ -7023,7 +7068,11 @@ word field(int c) {
}
if (scanned) i = fldi; else i = tfindfield(st, n);
if (i < 0) {
- if (!panic && !dry) { errat(); tprint(st); rt.put_s(" has no field "); rt.put_s(n); errend(); }
+ if (!panic && !dry) {
+ sv = tpack(); tunpack(fieldpos); // the error sits on the field name, where the quick fix replaces it
+ errat(); tprint(st); rt.put_s(" has no field "); rt.put_s(n); suggest_member(st[2], st, n, 0); errend();
+ tunpack(sv);
+ }
xty = tword; lv = 1; lvk = LV_OTHER; xbor = -1; return C_PLAIN;
}
rs_put(fieldoff, RS_FIELD, field_decl(st, n), st[4][i * 3 + 1]);
@@ -7651,6 +7700,7 @@ word cprim() {
if (accept(s_lp)) { v = cexpr(); expect(s_rp); return v; }
if (tk == T_ID) {
s = lookup(tv);
+ if (mstruct && !ginst) { mc = lookup(methname(mstruct, tv)); if (mc >= 0 && skind[mc] == K_CONST) s = mc; }
if (s >= 0 && (skind[s] == K_CONST || skind[s] == K_GARR || (skind[s] == K_FN && (sflag[s] & F_DEF)) || skind[s] == K_GVAR)) {
next();
return sval[s];
@@ -9572,6 +9622,66 @@ int similar(int n) {
if (bd > lim) return 0;
return best;
}
+// the field or method of struct sn closest to n (statics is 0), or the static function or constant
+// (statics is 1), when it is close enough to be a likely typo. the limits are those of similar.
+char* similar_member(char* sn, word st, char* n, int statics) {
+ int j, bd = 99, dist, k, ln = rt.len_w(sn), lim = rt.len_w(n) / 3, nf; char* best = 0; char* tail;
+ if (rt.len_w(n) < 3) return 0;
+ if (lim < 1) lim = 1;
+ if (lim > 3) lim = 3;
+ if (!statics && st && st[0] == TY_STRUCT) {
+ nf = st[3]; k = 0;
+ while (k < nf) {
+ tail = st[4][k * 3]; dist = editdist(n, tail);
+ if (dist == 2 && swapped(n, tail)) dist = 1;
+ if (dist > 0 && dist < bd) { bd = dist; best = tail; }
+ k++;
+ }
+ }
+ j = 0;
+ while (j < nsym) {
+ k = skind[j];
+ if (((k == K_FN && ((sflag[j] & F_STATIC) != 0) == statics) || (statics && k == K_CONST)) && rt.len_w(sname[j]) > ln + 1 && sname[j][ln] == '.') {
+ tail = sname[j] + ln + 1;
+ if (has_prefix(sname[j], sn, ln) && !has_dot(tail)) {
+ dist = editdist(n, tail);
+ if (dist == 2 && swapped(n, tail)) dist = 1;
+ if (dist > 0 && dist < bd) { bd = dist; best = tail; }
+ }
+ }
+ j++;
+ }
+ if (bd > lim) return 0;
+ return best;
+}
+// are a and b the same but for two neighbouring chars swapped (itao for itoa)? a swap counts as one typo.
+int swapped(char* a, char* b) {
+ int i = 0, la = rt.len_w(a);
+ if (la != rt.len_w(b)) return 0;
+ while (a[i] && a[i] == b[i]) i++;
+ if (!a[i] || !a[i + 1] || a[i] != b[i + 1] || a[i + 1] != b[i]) return 0;
+ i = i + 2;
+ while (a[i] && a[i] == b[i]) i++;
+ return !a[i];
+}
+// does s start with the first n chars of p?
+int has_prefix(char* s, char* p, int n) {
+ int i = 0;
+ while (i < n && s[i] == p[i]) i++;
+ return i == n;
+}
+// does the string contain a dot?
+int has_dot(char* s) {
+ int i = 0;
+ while (s[i]) { if (s[i] == '.') return 1; i++; }
+ return 0;
+}
+// ends an error message with "; did you mean 'x'?" when a member of struct sn is close to n
+int suggest_member(char* sn, word st, char* n, int statics) {
+ char* best = similar_member(sn, st, n, statics);
+ if (best) { rt.put_s("; did you mean '"); rt.put_s(best); rt.put_s("'?"); }
+ return 0;
+}
// a copy of s[a..e) as a string
char* lib_copy(char* s, int a, int e) {
char* b = chk_resize(0, 0, e - a + 1); rt.copy_w(b, s + a, e - a); b[e - a] = 0;