diff options
Diffstat (limited to 'src/compiler')
| -rw-r--r-- | src/compiler/compiler.zc | 124 |
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; |
