aboutsummaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/compiler.zc185
1 files changed, 166 insertions, 19 deletions
diff --git a/src/compiler/compiler.zc b/src/compiler/compiler.zc
index 40d83cf..9c24af9 100644
--- a/src/compiler/compiler.zc
+++ b/src/compiler/compiler.zc
@@ -2128,7 +2128,8 @@ int add_binop(char* nm, int prec, char* code) {
int p = intern(nm), i = binfind(p), a, n;
if (i < 0) { reserve_binary(nbin + 1); i = nbin; nbin++; }
bname[i] = p; bprec[i] = prec;
- if (prec < 0) { bprec[i] = 0 - prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; }
+ // && (-1) and || (-2) short-circuit: they bind below |, and && above ||, as in c
+ if (prec < 0) { bprec[i] = 3 + prec; blen[i] = prec; bseq[i] = 0; addkop(p); return i; }
bseq[i] = seqasm(code); blen[i] = seqtop - bseq[i];
addkop(p);
// and its compound assignment, x op= y
@@ -6861,6 +6862,11 @@ int primary() {
}
if (s >= 0 && skind[s] == K_GARR) { lit(sval[s]); if (stype[s]) xty = stype[s]; return C_PLAIN; }
if (s >= 0 && skind[s] == K_ENH) return enhcall(s);
+ if (s < 0 && is_op(s_dot)) {
+ // Text.itoa with no std: a library's static function, reported as one missing name
+ peekat(0);
+ if (pk_tk == T_ID && export_owner(methname(n, pk_tv))) { next(); n = methname(n, tv); next(); }
+ }
if (s < 0) s = fwdfn_at(n, callpos);
if (is_op(s_lt) && (sflag[s] & F_GEN)) { // f<int, char*>(...)
next(); ngbind = 0;
@@ -7696,6 +7702,7 @@ int stmt_inner() {
s = lookup(tv);
if (s >= 0 && skind[s] == K_KW && (sflag[s] & F_STMT)) { kwcall(s, 0); return 0; }
}
+ if (untyped_const()) { next(); localdecl(2); return 0; }
if (tstart()) { localdecl(1); return 0; }
if (tk == T_OP) {
if (tv == s_semi) { next(); return 0; }
@@ -7776,8 +7783,16 @@ int lt_note(int o, int t) {
return 0;
}
// compile a local declaration, typed or let-style.
+// does `const name =` start here? a const with no type takes the initializer's type.
+int untyped_const() {
+ if (tk != T_ID || tv != s_const) return 0;
+ peekat(0); if (pk_tk != T_ID || istypename(pk_tv)) return 0;
+ peekat(1); return pk_tk == T_OP && (pk_tv == s_eq || pk_tv == s_semi || pk_tv == s_comma);
+}
+// typed is 1 after a type, 0 after let, and 2 after an untyped const.
int localdecl(int typed) {
- int n, i, c, k, t = 0, tg = 0, pp, kc, kp = ifkp, gty = 0, gok = !typed && ifon && !dry, o, vr; word iq, it;
+ int n, i, c, k, t = 0, tg = 0, pp, kc, kp = ifkp, gty = 0, gok = !typed && ifon && !dry, o, vr, ro = typed == 2; word iq, it;
+ if (ro) typed = 0;
if (typed) { dtype(); t = dty; tg = dtag; if (t == tvoid) serr("a variable can't be void"); }
if ((o3on || o3one) && !typed) serr("o3 variables need a type");
while (1) {
@@ -7823,9 +7838,11 @@ int localdecl(int typed) {
else ltype[i] = xty;
}
assigncheck(LV_LOCAL, i, c);
+ if (ro) { k = ltype[i]; if (!k) { k = tword; if (isnum(xty)) k = xty; } ltype[i] = tconst(k); }
if (gok) { k = xty; if (isagg(k)) k = ltype[i]; if (!ifpure(k) || (gty && gty != k)) gok = 0; else gty = k; }
}
- } else if (gok && !larr[i]) gok = 0; // no initializer: nothing says what it is
+ } else if (ro) serr("a const needs an initializer");
+ else if (gok && !larr[i]) gok = 0; // no initializer: nothing says what it is
if (larr[i] && ltype[i][0] == TY_ARR && ltype[i][3] < 0) serr("T[] needs an initializer to determine its length");
if (larr[i]) { k = 1; if (ltype[i][0] == TY_ARR) k = ltype[i][3]; lafact[i] = af_new(k, 1, pp); laaddr[i] = lafact[i]; if (lafact[i]) lafact[i][5] = i; }
af_declaration(o, lafact[i]); os_at(o, i);
@@ -9508,10 +9525,27 @@ word editdist(word a, word b) {
}
return row[lb];
}
+// the part of a symbol a bare name is compared with: put_c for rt.put_c, the whole name otherwise
+char* similar_tail(char* name) {
+ int k = 0; char* tail = name;
+ while (name[k] && name[k] != '.') k++;
+ if (name[k] && module_named(name, k)) tail = name + k + 1;
+ return tail;
+}
+// is name[0..k) a module the compiler knows?
+int module_named(char* name, int k) {
+ char* prefix = chk_resize(0, 0, k + 1); int known;
+ rt.copy_w(prefix, name, k); prefix[k] = 0;
+ known = module_known(prefix);
+ rt.free(prefix);
+ return known;
+}
// the defined function, global or constant whose name is closest to n, if it
-// is close enough to be a likely typo; 0 when none is
+// is close enough to be a likely typo; 0 when none is. a module's export is
+// compared without its prefix, so putc finds rt.put_c
int similar(int n) {
int j = 0, best = 0, bd = 99, dist, k, lim = rt.len_w(n) / 3;
+ if (rt.len_w(n) < 3) return 0; // one edit turns g into gc: too short to guess from
if (lim < 1) lim = 1;
if (lim > 3) lim = 3;
while (j < nsym) {
@@ -9519,7 +9553,7 @@ int similar(int n) {
if (k == K_FN || k == K_GVAR || k == K_GARR || k == K_CONST) {
if (sflag[j] & F_DEF) {
if (sname[j] != n) {
- dist = editdist(n, sname[j]);
+ dist = editdist(n, similar_tail(sname[j]));
if (dist < bd) { bd = dist; best = sname[j]; }
}
}
@@ -9529,24 +9563,137 @@ int similar(int n) {
if (bd > lim) return 0;
return best;
}
-// end a compile unit from token from onward, and report what it defined.
-// the module that exports a bare name, when one does: put_c is rt's. the compiler's
-// own modules come first, then the libraries this unit has loaded. 0 when none does.
-char* export_owner(char* name) {
- int k = 0; word p = included_modules;
+// 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;
+ return b;
+}
+// group.name, as a fresh string: the lists below outlive the name table of a check
+char* lib_join(char* group, char* name) {
+ int g = rt.len_w(group), n = rt.len_w(name); char* b = chk_resize(0, 0, g + n + 2);
+ rt.copy_w(b, group, g); b[g] = '.'; rt.copy_w(b + g + 1, name, n); b[g + n + 1] = 0;
+ return b;
+}
+// the declarations of a library, read from its source text, as a list of [next, tail, path]:
+// a top-level name's path is the name itself, a static method's is Group.name. the scan
+// only looks at shapes: a name before ( or a declarator's = ; , at the top, or inside a
+// struct's braces before (. qualified calls (run.statement_hook(...)) are not declarations.
+word lib_scan(char* s) {
+ int i = 0, depth = 0, paren = 0, init = 0, la = -1, le = 0, dotted = 0, after_dot = 0, words = 0, skip = 0, gpend = 0, gdepth = -1, c, a, k;
+ char* group = 0; char* path; word list = 0, entry, back = 0;
+ while (s[i]) {
+ c = s[i];
+ if (c == '/' && s[i + 1] == '/') { while (s[i] && s[i] != 10) i++; continue; }
+ if (c == '/' && s[i + 1] == '*') { i = i + 2; while (s[i] && !(s[i] == '*' && s[i + 1] == '/')) i++; if (s[i]) i = i + 2; continue; }
+ if (c == 34 || c == 39) { i++; while (s[i] && s[i] != c) { if (s[i] == 92 && s[i + 1]) i++; i++; } if (s[i]) i++; la = -1; continue; }
+ if (c == 32 || c == 9 || c == 10 || c == 13) { i++; continue; }
+ if (isdig(c)) { while (isidc(s[i])) i++; la = -1; continue; }
+ if (isal(c)) {
+ a = i; while (isidc(s[i])) i++;
+ if (gpend) { group = lib_copy(s, a, i); gpend = 0; entry = chk_resize(0, 0, 3); entry[0] = list; entry[1] = group; entry[2] = group; list = entry; }
+ if (depth == 0 && paren == 0 && !init) {
+ if (words == 0 && ((i - a == 9 && rt.eq_w(lib_copy(s, a, i), "namespace")) || (i - a == 7 && rt.eq_w(lib_copy(s, a, i), "include")))) skip = 1;
+ if (i - a == 6 && rt.eq_w(lib_copy(s, a, i), "struct")) { gpend = 1; group = 0; }
+ words++;
+ }
+ la = a; le = i; dotted = after_dot; after_dot = 0;
+ continue;
+ }
+ after_dot = c == '.';
+ if (c == '<' && s[i + 1] != '<' && s[i + 1] != '=' && paren == 0 && !init && (depth == 0 || (depth == 1 && group))) {
+ // generic parameters: int find<T>(...) is still find
+ k = 1; i++; while (s[i] && k) { if (s[i] == '<') k++; if (s[i] == '>') k--; i++; }
+ continue;
+ }
+ if (c == '(' && paren == 0 && !init && la >= 0 && !dotted && !skip) {
+ if (depth == 0 || (depth == 1 && group && gdepth == 0)) {
+ path = lib_copy(s, la, le);
+ entry = chk_resize(0, 0, 3); entry[0] = list; entry[1] = path; entry[2] = path;
+ if (depth == 1) entry[2] = lib_join(group, path);
+ list = entry;
+ }
+ }
+ if (((c == '=' && s[i + 1] != '=') || c == ';' || c == ',' || c == '[') && depth == 0 && paren == 0 && !init && la >= 0 && !dotted && !skip && words > 1) {
+ entry = chk_resize(0, 0, 3); entry[0] = list; entry[1] = lib_copy(s, la, le); entry[2] = entry[1]; list = entry;
+ }
+ if (c == '=' && s[i + 1] != '=' && depth == 0 && paren == 0) init = 1;
+ if (c == '(' || c == '[') paren++;
+ if ((c == ')' || c == ']') && paren > 0) paren--;
+ if (c == '{') { if (depth == 0 && paren == 0 && group) gdepth = 0; else if (depth == 0) group = 0; depth++; }
+ if (c == '}' && depth > 0) { depth--; if (depth == 0) { group = 0; gdepth = -1; words = 0; skip = 0; init = 0; } }
+ if (c == ';' && depth == 0 && paren == 0) { init = 0; words = 0; skip = 0; group = 0; }
+ if (c == ',' && depth == 0 && paren == 0) init = 0;
+ la = -1; i++;
+ }
+ // first declared first: Text.itoa comes before Bytes.itoa
+ while (list) { entry = list[0]; list[0] = back; back = list; list = entry; }
+ return back;
+}
+// the libraries under src/lib, scanned once: std and icon are embedded, actor is read like its include
+word std_names, icon_names, actor_names; int names_read;
+word lib_names(char* module) {
+ char* save = incres;
+ if (!names_read) {
+ names_read = 1;
+ std_names = lib_scan(std_text()); icon_names = lib_scan(icon_text());
+ char* text = inc_open("src/lib/actor.zc");
+ if (text) actor_names = lib_scan(text);
+ rt.free(text);
+ incres = save;
+ }
+ if (rt.eq_w(module, "std")) return std_names;
+ if (rt.eq_w(module, "icon")) return icon_names;
+ return actor_names;
+}
+// where name is in a library's list: Text.itoa is itself, a bare itoa is a top-level
+// declaration first, then a static method of some group. 0 when it is not there.
+char* lib_path(word list, char* name) {
+ word p = list; int k = 0;
+ while (p) { if (rt.eq_w(p[2], name)) return p[2]; p = p[0]; }
while (name[k]) { if (name[k] == '.') return 0; k++; }
- if (export_in("rt", name)) return "rt";
- if (export_in("core", name)) return "core";
- if (export_in("run", name)) return "run";
- while (p) { if (export_in(p[1], name)) return p[1]; p = p[0]; }
- return 0;
+ p = list;
+ while (p) { if (rt.eq_w(p[1], name)) return p[2]; p = p[0]; }
+ return 0;
+}
+// the module that exports a bare name, and the path after its prefix: put_c is rt's put_c,
+// itoa is std's Text.itoa. every owner, best first, is a list of [next, module, path]: the
+// compiler's own modules, then the modules this unit loaded, then the libraries, which are
+// read from their source whether this unit loaded them or not.
+word export_list, export_tail; char* export_path;
+int export_add(char* module, char* path) {
+ word p = export_list;
+ if (!path) return 0;
+ while (p) { if (rt.eq_w(p[1], module)) return 0; p = p[0]; }
+ p = chk_resize(0, 0, 3); p[1] = module; p[2] = path;
+ if (export_tail) export_tail[0] = p; else export_list = p;
+ export_tail = p;
+ return 0;
+}
+word export_owners(char* name) {
+ word p = included_modules;
+ export_list = 0; export_tail = 0;
+ export_add("rt", export_in("rt", name));
+ export_add("core", export_in("core", name));
+ export_add("run", export_in("run", name));
+ while (p) { export_add(p[1], export_in(p[1], name)); p = p[0]; }
+ export_add("std", lib_path(lib_names("std"), name));
+ export_add("icon", lib_path(lib_names("icon"), name));
+ export_add("actor", lib_path(lib_names("actor"), name));
+ return export_list;
+}
+char* export_owner(char* name) {
+ word p = export_owners(name);
+ if (!p) return 0;
+ export_path = p[2]; return p[1];
}
-// does module define name: a function, a global, a constant or a type of it.
-int export_in(char* module, char* name) {
+// name, when module defines it: a function, a global, a constant or a type of it. 0 otherwise.
+char* export_in(char* module, char* name) {
int s = lookup_raw(methname(module, name));
if (s < 0) return 0;
- return skind[s] != K_DOC && (skind[s] != K_FN || (sflag[s] & F_DEF));
+ if (skind[s] != K_DOC && (skind[s] != K_FN || (sflag[s] & F_DEF))) return name;
+ return 0;
}
+// end a compile unit from token from onward, and report what it defined.
int unit_end(int from) {
int i = from, best; char* owner;
effect_finish(from);
@@ -9564,7 +9711,7 @@ int unit_end(int from) {
if (owner) {
rt.put_s("; did you mean to ");
if (!module_imported(owner)) { rt.put_s("include "); rt.put_s(owner); rt.put_s(" and "); }
- rt.put_s("use "); rt.put_s(owner); rt.put_c('.'); rt.put_s(sname[i]); rt.put_s("?");
+ rt.put_s("use "); rt.put_s(owner); rt.put_c('.'); rt.put_s(export_path); rt.put_s("?");
}
else if (best) { rt.put_s("; did you mean '"); rt.put_s(best); rt.put_s("'?"); }
errend();