aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 08:30:01 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 08:30:01 +0000
commitd274751e89f75cc0f0e25c6e5e87ef91b64597ee (patch)
treeef76f2cfe9d2a09f3fe5d27278cf775649bc162f /src
parentlsp: quick fixes for hints and bare library names; comment cleanup (diff)
zc: untyped local const, c precedence for && and ||, import fixes for every module
const name = value; inside a function declares a local constant that takes the initializer's type. the let quick fix now writes a bare const. the self-hosted compiler bound || tighter than &&, unlike zc0 and the readme. no existing source mixed them without parentheses, so only new code was affected. missing-name diagnostics and their quick fixes now search every module: rt, core, run, loaded files, and std, icon and actor scanned from source, including static groups (std.Text.itoa). typos suggest module exports (putc -> rt.put_c) and get a change-to fix.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/compiler.zc185
-rw-r--r--src/lsp/server.zc75
2 files changed, 218 insertions, 42 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();
diff --git a/src/lsp/server.zc b/src/lsp/server.zc
index 70f9efd..2587c3b 100644
--- a/src/lsp/server.zc
+++ b/src/lsp/server.zc
@@ -3149,10 +3149,21 @@ int do_completion(int i) {
// import fixes are derived from current compiler errors and qualified exports.
// each edit is tied to the document version that was checked.
word ca_list; int ca_i, ca_start, ca_end, ca_insert, ca_crlf;
-int ca_add(char*.d owner, int qualify) {
- word p = ca_list;
+// one fix: include owner, and when path is set, replace from..ca_end with owner.path.
+// path belongs to the compiler's export list and lives until the next check.
+int ca_add(char* owner, char* path, int from) {
+ word p = ca_list; int n = rt.len_w(owner); char* copy;
while (p) { if (rt.eq_w(p[1], owner)) return 0; p = p[0]; }
- p = rt.new(3); p[0] = ca_list; p[1] = owner; p[2] = qualify; ca_list = p;
+ copy = rt.new(n + 1); rt.copy_w(copy, owner, n + 1);
+ p = rt.new(4); p[0] = ca_list; p[1] = copy; p[2] = path; p[3] = from; ca_list = p;
+ return 0;
+}
+// the fixes for every module that exports name, best first
+int ca_owners(char* name, int from) {
+ word p = run.export_owners(name), q = 0, next;
+ // ca_add prepends, so the list goes in backwards and comes out best first
+ while (p) { next = rt.new(2); next[0] = q; next[1] = p; q = next; p = p[0]; }
+ while (q) { p = q[1]; ca_add(p[1], p[2], from); next = q[0]; rt.free(q); q = next; }
return 0;
}
int ca_import_point(int i) {
@@ -3176,7 +3187,7 @@ int ca_import_point(int i) {
int ca_newline() { if (ca_crlf) hov_put(13); hov_put(10); return 0; }
// quick fixes for hints. each record is one edit of the document: it replaces start..end
// with text built from its kind. the hint's own range and words say what to change.
-const int HF_RENAME = 1, HF_CONST = 2, HF_SHIFT = 3, HF_STACK = 4;
+const int HF_RENAME = 1, HF_CONST = 2, HF_SHIFT = 3, HF_STACK = 4, HF_TYPO = 5;
word hf_list; // [next, kind, start, end, a, b]
int hf_add(int kind, int start, int end, int a, int b) {
word p = lsp_grow(0, 0, 6);
@@ -3241,7 +3252,7 @@ int hf_operand(int i, int op) {
}
if (o == op || depth) return 0 - 1;
b = o; while (b > 0 && (dc(doff[i], b - 1) == 32 || dc(doff[i], b - 1) == 9)) b--;
- if (b > 0) { c = dc(doff[i], b - 1); if (c == '*' || c == '/' || c == '%' || c == '.' || c == '-' && b > 1 && dc(doff[i], b - 2) == '-') return 0 - 1; }
+ if (b > 0) { c = dc(doff[i], b - 1); if (c == '*' || c == '/' || c == '%' || c == '.' || (c == '-' && b > 1 && dc(doff[i], b - 2) == '-')) return 0 - 1; }
return o;
}
// is the text at o the word w, ending there
@@ -3294,6 +3305,7 @@ int hf_title(word p) {
if (p[1] == HF_RENAME) { hov_text("rename `"); while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } hov_text("` to _"); }
else if (p[1] == HF_CONST) { hov_text("make `"); k = p[3]; while (k < p[4]) { hov_put(dc(doff[ca_i], k)); k++; } hov_text("` a const"); }
else if (p[1] == HF_SHIFT) { hov_text("write << "); hov_number(p[4]); }
+ else if (p[1] == HF_TYPO) { hov_text("change to `"); k = p[4]; while (k < p[5]) { hov_put(dgb[k]); k++; } hov_put('`'); }
else { hov_text("use a stack buffer"); }
return 0;
}
@@ -3302,9 +3314,9 @@ int hf_text(word p) {
hn = 0;
if (p[1] == HF_RENAME) hov_put('_');
else if (p[1] == HF_CONST) {
+ // let becomes a bare const, which takes the initializer's type
hov_text("const ");
- if (hf_word(ca_i, p[2], "let")) hov_text("int ");
- else { k = p[2]; while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } }
+ if (!hf_word(ca_i, p[2], "let")) { k = p[2]; while (k < p[3]) { hov_put(dc(doff[ca_i], k)); k++; } }
}
else if (p[1] == HF_SHIFT) {
// (operand << k): the operand's text runs from the record's start to the *, without trailing blanks
@@ -3313,6 +3325,7 @@ int hf_text(word p) {
while (k < c) { hov_put(dc(doff[ca_i], k)); k++; }
hov_text(" << "); hov_number(p[4]); hov_put(')');
}
+ else if (p[1] == HF_TYPO) { k = p[4]; while (k < p[5]) { hov_put(dgb[k]); k++; } }
else { hov_put('['); k = p[4]; while (k < p[5]) { hov_put(dc(doff[ca_i], k)); k++; } hov_put(']'); }
return 0;
}
@@ -3335,7 +3348,7 @@ int gen_code_actions() {
if (!first) wc(','); first = 0; imported = run.module_imported(p[1]);
hn = 0;
if (!imported) { hov_text("include "); hov_text(p[1]); }
- if (p[2]) { if (!imported) hov_text(" and "); hov_text("use "); hov_text(p[1]); hov_put('.'); hov_text(hname); }
+ if (p[2]) { if (!imported) hov_text(" and "); hov_text("use "); hov_text(p[1]); hov_put('.'); hov_text(p[2]); }
ws("{\"title\":"); wjs(hov, hn); ws(",\"kind\":\"quickfix\",");
if (single) ws("\"isPreferred\":true,");
ws("\"edit\":{\"documentChanges\":[{\"textDocument\":{\"uri\":");
@@ -3348,13 +3361,33 @@ int gen_code_actions() {
}
if (p[2]) {
if (!imported) wc(',');
- ws("{\"range\":{\"start\":"); wpos(ca_start); ws(",\"end\":"); wpos(ca_end); ws("},\"newText\":");
- hn = 0; hov_text(p[1]); hov_put('.'); hov_text(hname); wjs(hov, hn); wc('}');
+ ws("{\"range\":{\"start\":"); wpos(p[3]); ws(",\"end\":"); wpos(ca_end); ws("},\"newText\":");
+ hn = 0; hov_text(p[1]); hov_put('.'); hov_text(p[2]); wjs(hov, hn); wc('}');
}
ws("]}]}}"); p = p[0];
}
ws("]}"); return 0;
}
+// "did you mean 'name'?" under the cursor: the name replaces the word. a module's export
+// (rt.put_c) also gets its include when the document lacks it.
+char[64] ca_owner; char[64] ca_path;
+int ca_typo(int at) {
+ int n = dgb[at + 4], e = n - 2, b, dot, k;
+ if (!dg_ends(at, "'?")) return 0;
+ b = e; while (b > 0 && dgb[at + 5 + b - 1] != 39) b--;
+ if (b == 0 || e - b >= 63) return 0;
+ dot = b; while (dot < e && dgb[at + 5 + dot] != '.') dot++;
+ if (dot < e) {
+ k = 0; while (b + k < dot) { ca_owner[k] = dgb[at + 5 + b + k]; k++; } ca_owner[k] = 0;
+ if (run.module_known(ca_owner)) {
+ k = 0; while (dot + 1 + k < e) { ca_path[k] = dgb[at + 6 + dot + k]; k++; } ca_path[k] = 0;
+ ca_add(ca_owner, ca_path, ca_start);
+ return 0;
+ }
+ }
+ hf_add(HF_TYPO, ca_start, ca_end, at + 5 + b, at + 5 + e);
+ return 0;
+}
int do_code_actions(int i) {
int at = 0, error = -1, s, dot, k, n, base = doff[i], start, end, cursor, limit, sl = hline, sc = hchar;
ca_list = 0; hf_list = 0; ca_i = i;
@@ -3375,22 +3408,18 @@ int do_code_actions(int i) {
while (start > 0 && (hid(dc(base, start - 1)) || dc(base, start - 1) == '.')) start--;
dot = start; while (dot < ca_end && dc(base, dot) != '.') dot++;
if (dot < ca_end) {
+ // rt.put_c without include rt needs only the include; Text.itoa without std is std.Text.itoa
char* owner = rt.new(dot - start + 1); k = start; n = 0;
while (k < dot) { owner[n] = dc(base, k); n++; k++; } owner[n] = 0;
- if (run.module_known(owner) && !run.module_imported(owner)) ca_add(d*owner, 0); else rt.free(owner);
- } else {
- s = 0;
- while (s < run.nsym) {
- if (run.skind[s] != run.K_DOC) {
- char* symbol = run.sname[s]; dot = 0; while (symbol[dot] && symbol[dot] != '.') dot++;
- if (symbol[dot] == '.' && rt.eq_w(symbol + dot + 1, hname)) {
- char* owner = rt.new(dot + 1); rt.copy_w(owner, symbol, dot); owner[dot] = 0;
- if (run.module_known(owner)) ca_add(d*owner, 1); else rt.free(owner);
- }
- }
- s++;
+ if (run.module_known(owner) || run.module_loaded(owner)) { if (!run.module_imported(owner)) ca_add(owner, 0, 0); }
+ else {
+ char* dotted = rt.new(ca_end - start + 1); k = start; n = 0;
+ while (k < ca_end) { dotted[n] = dc(base, k); n++; k++; } dotted[n] = 0;
+ ca_owners(dotted, start); rt.free(dotted);
}
- }
+ rt.free(owner);
+ } else ca_owners(hname, ca_start);
+ if (!ca_list) ca_typo(error);
send(gen_code_actions); return 0;
}