aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/compiler.zc107
-rw-r--r--src/lsp/server.zc50
2 files changed, 145 insertions, 12 deletions
diff --git a/src/compiler/compiler.zc b/src/compiler/compiler.zc
index 86e00c5..7619b64 100644
--- a/src/compiler/compiler.zc
+++ b/src/compiler/compiler.zc
@@ -285,8 +285,12 @@ int fldst, fldi; // the one struct a bare address's field name resolved to
char*[8] tpn; int ntp; // type parameters in scope
// reading a return type: unknown names are type parameters declared after the function's name
int tvauto;
+// the typedefs of the blocks being compiled: three words each (the symbol, the block depth and the
+// compile context it was made in), newest last. a block's end unlinks them from their names.
+word tdstk; int ntd, tdcap;
word readonly_types;
int dty, dtag; // result of dtype()
+int s_typedef;
int s_lt, s_gt, s_shr, s_le, s_ge, s_eqeq, s_ne, s_plus, s_struct, s_type, s_typecheck, s_docs, s_small, s_std, s_icon, s_on, s_off, s_new, s_new_t;
int s_o3, s_lfa, s_outside, s_new_raw, s_new_oz, s_oob;
int s_ld8, s_st8, s_ld16, s_st16, s_ld32, s_st32;
@@ -1102,6 +1106,7 @@ int source_lookup(char* name) {
// drop the symbols above index to, which ends a scope.
int popsyms(int to) {
while (nsym > to) { nsym--; sname[nsym][-1] = snext[nsym]; }
+ while (ntd && tdstk[ntd * 3 - 3] >= to) ntd--; // local typedefs made above to went with them
return 0;
}
// look a name up from a plain string.
@@ -2952,6 +2957,7 @@ int block_close(int save) {
while (i < save) { if (lhome[i] >= fdepth) { lstate[i] = S_DEAD; lhome[i] = fdepth - 1; } i++; }
local_drop(save);
fdrop(save);
+ td_end(fdepth);
return 0;
}
@@ -4348,6 +4354,89 @@ int kw_type() {
expect(s_semi);
return 0;
}
+// does a parameter start with a type? a type name straight before , or ) is the parameter's
+// own name (fn f(string)), not a type, since a typed parameter always has a name after it.
+int paramstart() {
+ if (tk == T_OP) return tv == s_lk;
+ if (tk != T_ID) return 0;
+ if (tv == s_fn) return 1;
+ if (!istypename(tv)) return 0;
+ peek();
+ return !(pk_tk == T_OP && (pk_tv == s_comma || pk_tv == s_rp));
+}
+// does a typedef start here? typedef followed by a name is one, so typedef can still be
+// a variable or a function's name.
+int typedef_ahead() {
+ if (tk != T_ID || tv != s_typedef) return 0;
+ peekat(0);
+ return pk_tk == T_ID;
+}
+// unlink the symbol s from its name, leaving the rest of the name's chain as it was.
+int td_unlink(int s) {
+ char* nm = sname[s]; int i = nm[-1] - 1;
+ if (i == s) { nm[-1] = snext[s]; return 0; }
+ while (i >= 0 && snext[i] - 1 != s) i = snext[i] - 1;
+ if (i >= 0) snext[i] = snext[s];
+ return 0;
+}
+// end the scope of the local typedefs made at block depth depth or deeper in this
+// compile context (and of any made in a context nested inside it).
+int td_end(int depth) {
+ int k;
+ while (ntd) {
+ k = ntd * 3 - 3;
+ if (tdstk[k + 2] < ctxsp || (tdstk[k + 2] == ctxsp && tdstk[k + 1] < depth)) break;
+ td_unlink(tdstk[k]);
+ ntd--;
+ }
+ return 0;
+}
+// is the name q (as a symbol is named) already a typedef of the block being compiled?
+int td_here(char* q) {
+ int k = ntd;
+ while (k) {
+ k--;
+ if (tdstk[k * 3 + 2] != ctxsp || tdstk[k * 3 + 1] != fdepth) break;
+ if (sname[tdstk[k * 3]] == q) return 1;
+ }
+ return 0;
+}
+// typedef T name;
+// the C spelling of type name = T;. name becomes another way to write T: the very same
+// type record, so the two are interchangeable everywhere. at the top level the name is
+// global; in a function it lasts to the end of its block, like a local. a name that is
+// already in use is an error, except the same type under the same name again. a tag
+// (.u, .m, .d) belongs to a declaration, not to a type, so a typedef has none.
+int kw_typedef() {
+ char* n; char* q; word t; int s, at, clash = 0, local = infn;
+ next();
+ t = ptype();
+ if (panic) return 0;
+ if (is_op(s_dot)) { die("a typedef names a type, which has no tag: .u, .m and .d go on a declaration"); return 0; }
+ at = toff; n = name();
+ if (panic) return 0;
+ q = module_symbol(n);
+ if (local) {
+ s = findloc(n);
+ if ((s >= 0 && lhome[s] >= fdepth) || td_here(q)) clash = 1;
+ } else {
+ s = lookup_raw(q);
+ if (s < 0 && module_name) { s = lookup_raw(n); if (s >= 0 && skind[s] != K_TYPE && skind[s] != K_KW) s = 0 - 1; }
+ if (s >= 0 && skind[s] == K_TYPE && sval[s] == t) return expect(s_semi); // the same typedef again
+ if (s >= 0) clash = 1;
+ }
+ if (clash) serrn(n, "already defined");
+ else {
+ s = addsym(n, K_TYPE, t);
+ rs_put(at, RS_SYM, rs_sym(s), s);
+ if (local) {
+ if (ntd == tdcap) { if (tdcap) tdcap = tdcap * 2; else tdcap = 16; tdstk = chk_resize(tdstk, ntd * 3, tdcap * 3); }
+ tdstk[ntd * 3] = s; tdstk[ntd * 3 + 1] = fdepth; tdstk[ntd * 3 + 2] = ctxsp; ntd++;
+ }
+ }
+ expect(s_semi);
+ return 0;
+}
// (T) or (T*) ahead of the current '(', within the lookahead we have
// the parser sees ( and has to decide between a parenthesized expression and a
// cast. a cast is ( type stars ) so peek ahead through the stars to the ).
@@ -5516,6 +5605,7 @@ int types_init() {
addsym(tword[2], K_TYPE, tword); addsym(tint[2], K_TYPE, tint); addsym(tchar[2], K_TYPE, tchar);
addsym(tbool[2], K_TYPE, tbool); addsym(tvoid[2], K_TYPE, tvoid); addsym(tfloat[2], K_TYPE, tfloat);
addsym(intern("f64"), K_TYPE, tfloat);
+ addsym(intern("string"), K_TYPE, tptr(tchar)); // string is char*, as if by typedef char* string;
// f32: single precision. a variable holds the rounded value as a double; an element
// of an array or pointer of f32 is packed two to a word, like the sized integers
f32p = tnew(TY_FLOAT, 1, intern("f32"), 0);
@@ -5530,7 +5620,7 @@ int types_init() {
s_le = intern("<=");
s_ge = intern(">=");
s_eqeq = intern("=="); s_ne = intern("!="); s_plus = intern("+");
- s_struct = intern("struct"); s_type = intern("type"); s_typecheck = intern("typecheck"); s_docs = intern("docs"); s_std = intern("std"); s_icon = intern("icon"); s_small = intern("small");
+ s_struct = intern("struct"); s_type = intern("type"); s_typedef = intern("typedef"); s_typecheck = intern("typecheck"); s_docs = intern("docs"); s_std = intern("std"); s_icon = intern("icon"); s_small = intern("small");
s_on = intern("on");
s_off = intern("off");
s_new = intern("rt.new");
@@ -6445,7 +6535,7 @@ int closure() {
while (1) {
pty[np] = tword;
if (expected && expected[0] == TY_FN && np < expected[3]) pty[np] = expected[7 + np];
- if (!is_op(s_dot) && ((tk == T_OP && tv == s_lk) || (tk == T_ID && (tv == s_fn || istypename(tv))))) {
+ if (!is_op(s_dot) && paramstart()) {
pty[np] = ptype(); if (pty[np][0] == TY_ARR) pty[np] = decay(pty[np]);
}
tag = 0;
@@ -7611,7 +7701,7 @@ word stmtstart() {
int s;
if (tk == T_OP) return tv == s_semi || tv == s_lb || tstart();
if (tk != T_ID || findloc(tv) >= 0) return 0;
- if (tstart()) return 1;
+ if (tstart() || typedef_ahead()) return 1;
s = lookup(tv);
return s >= 0 && skind[s] == K_KW && !(sflag[s] & F_EXPR);
}
@@ -7712,6 +7802,7 @@ int stmt_inner() {
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 (typedef_ahead() && findloc(tv) < 0) return kw_typedef();
if (tstart()) { localdecl(1); return 0; }
if (tk == T_OP) {
if (tv == s_semi) { next(); return 0; }
@@ -8596,7 +8687,7 @@ int prescan(int kind, int obj) {
if (is_op(s_lb)) depth++;
else if (is_op(s_rb)) { depth--; pend = 0; po3 = 0; }
else if (depth <= 0) {
- if (is_id(s_type)) { while (tk != T_EOF && !is_op(s_semi)) next(); if (is_op(s_semi)) next(); pend = 0; po3 = 0; continue; }
+ if (is_id(s_type) || typedef_ahead()) { while (tk != T_EOF && !is_op(s_semi)) next(); if (is_op(s_semi)) next(); pend = 0; po3 = 0; continue; }
if (tk == T_ID && rt.eq_w(tv, "namespace")) { kw_namespace(); continue; }
if (is_op(s_semi)) { pend = 0; po3 = 0; }
else if (is_op(s_dot)) { peek(); if (pk_tk == T_ID && pk_tv == s_u) pend = 1; }
@@ -8745,6 +8836,7 @@ int epilogue() {
infn = 0;
local_drop(lbase);
fdrop(lbase);
+ td_end(0);
return 0;
}
// the statements of a function between its braces, then the end-of-function
@@ -8800,7 +8892,7 @@ int fndef(int chk, int rty, int ret, int n) {
t = 0;
pty[np] = tword;
pk = 0;
- if (!is_op(s_dot) && ((tk == T_OP && tv == s_lk) || (tk == T_ID && (tv == s_fn || istypename(tv))))) {
+ if (!is_op(s_dot) && paramstart()) {
pty[np] = ptype(); typed = 1; pk = 1;
if (pty[np][0] == TY_ARR) pty[np] = decay(pty[np]);
if (isagg(pty[np])) {
@@ -9065,7 +9157,7 @@ int topsync() {
while (tk != T_EOF) {
if (is_op(s_lb)) depth++;
else if (is_op(s_rb)) { depth--; if (depth < 0) depth = 0; }
- else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type
+ else if (!depth && tk == T_ID && (tv == s_fn || tv == s_var || tv == s_const || tv == s_struct || tv == s_type || tv == s_typedef
|| tv == s_enhance || tv == s_typecheck || tv == s_docs || tv == s_unchecked || tv == s_immediate || tv == s_o3
|| tv == s_memdebug || tv == s_enum || tv == s_include)) break;
next();
@@ -9175,6 +9267,7 @@ int topitem1() {
if (is_id(s_var)) return gdecl(0);
if (is_id(s_struct)) { kw_struct(); return 0; }
if (is_id(s_type)) return kw_type();
+ if (typedef_ahead()) return kw_typedef();
if (is_id(s_typecheck)) return kw_typecheck();
if (is_id(s_docs)) return kw_docs();
if (is_id(s_memdebug)) return kw_memdebug();
@@ -9978,7 +10071,7 @@ int zc_init() {
poolend = R_POOLEND; poolblocks = 0; namecount = 0;
rt.fill_w(namehash, 0, hashcap);
nsym = 0; nfixu = 0; fixfree = 0; nbin = 0; nun = 0; nasg = 0; nkop = 0; seqtop = 0;
- lsp = 0; ctxsp = 0; snaptop = 0; nloc = 0; lbase = 0; npm = 0; npe = 0; rt.fill_w(lkeys, 0, lkeycap); lkeyn = 0;
+ lsp = 0; ctxsp = 0; ntd = 0; snaptop = 0; nloc = 0; lbase = 0; npm = 0; npe = 0; rt.fill_w(lkeys, 0, lkeycap); lkeyn = 0;
s_lp = intern("("); s_rp = intern(")"); s_lb = intern("{"); s_rb = intern("}");
s_lk = intern("["); s_rk = intern("]"); s_comma = intern(","); s_semi = intern(";");
s_eq = intern("="); s_arrow = intern("->"); s_inc = intern("++"); s_dec = intern("--");
diff --git a/src/lsp/server.zc b/src/lsp/server.zc
index 1608c4c..3262e39 100644
--- a/src/lsp/server.zc
+++ b/src/lsp/server.zc
@@ -390,7 +390,7 @@ int isopc(int c) {
int iskeyword(int n) {
return n == run.s_fn || n == run.s_var || n == run.s_const || n == run.s_else || n == run.s_as || n == run.s_unchecked || n == run.s_immediate
|| n == run.s_case || n == run.s_default || n == run.s_do || n == run.s_while || n == run.s_enum || n == run.s_include || n == run.s_memdebug
- || n == run.s_enhance || n == run.s_micro || n == run.s_vm || n == run.s_struct || n == run.s_type || n == run.s_typecheck || n == run.s_static;
+ || n == run.s_enhance || n == run.s_micro || n == run.s_vm || n == run.s_struct || n == run.s_type || n == run.s_typedef || n == run.s_typecheck || n == run.s_static;
}
// the record of the name at byte offset o (see rs_note in the compiler), or -1.
// the records are sorted by offset, so this is a binary search.
@@ -934,7 +934,7 @@ int reply_error(int code) {
return 0;
}
-int hs_name = -1, hs_first = -1, hs_const;
+int hs_name = -1, hs_first = -1, hs_const, hs_typedef, hs_tname;
int bs_k, bs_off; // the embedded source and name offset found by bs_find
// ---- hover ----
@@ -999,8 +999,8 @@ word hover_decl(int base, int ls, int le) {
// it (hcs is -1 when none) which runs to the line at hdl. a declaration needs
// no comment, so the signature still shows without one.
int hover_scan(int i) {
- hs_name = -1; hs_first = -1; hs_const = 0;
- if (hover_source_decl(i) || hover_source_function(i)) return 1;
+ hs_name = -1; hs_first = -1; hs_const = 0; hs_typedef = 0;
+ if (hover_typedef(i) || hover_source_decl(i) || hover_source_function(i)) return 1;
int base = doff[i], size = dsize[i], ls = 0, le, cs = 0 - 1, c0;
while (ls < size) {
le = ls;
@@ -1182,6 +1182,12 @@ int hover_docs(int i, int head, int name, int length, int tail) {
int hover_fill(int i) {
int base = doff[i], o = hdl, c, cs, ls, typed = 0, k, run.start, end;
hn = 0;
+ if (hs_typedef) { // typedef T name; as written, from its first word to its ;
+ hov_text("```zc\n"); c = hdl;
+ while (c < hdle) { hov_put(dc(base, c)); c++; }
+ hov_text("\n```"); hover_docs(i, hdl, hdl, 0 - 1, hdle);
+ return hn;
+ }
if (hs_name >= 0) {
hov_text("```zc\n"); c = hdl;
while (c < hs_first) { hov_put(dc(base, c)); c++; }
@@ -1624,9 +1630,11 @@ int hover_type_span(int i) {
}
if (best < 0) return 0;
t = run.ht_type[best];
+ // a typedef name is shown as its typedef, then the type it stands for
+ if (width == rt.len_w(hname) && run.ht_start[best] == hoff && hover_typedef(i)) { hover_fill(i); hover_type_info(t); return 1; }
hov_text("```zc\n"); hov_type(t); hov_text("\n```"); hover_type_info(t);
k = kwtext();
- if (k && (hname_is("word") || hname_is("int") || hname_is("char") || hname_is("bool") || hname_is("void") || hname_is("float") || hname_is("f32") || hname_is("f64") || hname_is("byte") || hname_is("u8") || hname_is("i8") || hname_is("u16") || hname_is("i16") || hname_is("u32") || hname_is("i32"))) {
+ if (k && (hname_is("word") || hname_is("int") || hname_is("char") || hname_is("bool") || hname_is("void") || hname_is("float") || hname_is("f32") || hname_is("f64") || hname_is("byte") || hname_is("string") || hname_is("u8") || hname_is("i8") || hname_is("u16") || hname_is("i16") || hname_is("u32") || hname_is("i32"))) {
while (*k && *k != '~') k++;
if (*k) { hov_text("\n\n"); hov_text(k + 1); }
}
@@ -1686,6 +1694,8 @@ char* kwtext() {
if (hname_is("return")) return "return e; or return;~leaves the function with a value, or leaves a void function. returning an owned local transfers it to the caller automatically.";
if (hname_is("enum")) return "enum name { A, B = 5 };~global constants. under typecheck on they are a type of their own, and a member can carry fields to make a constructor.";
if (hname_is("struct")) return "struct name { fields };~a record type. struct name<T> { } makes it generic, one copy for each set of type arguments.";
+ if (hname_is("typedef")) return "typedef T name;~an alias for a type, the same as type name = T; but in C order. the name is another way to write T, not a new type. inside a function it lasts to the end of its block.";
+ if (hname_is("string")) return "string~a built-in alias for char*, a word string, as if typedef char* string; were written.";
if (hname_is("type")) return "type name = T;~an alias for a type, such as type row = int[3]; for a pointer to an array.";
if (hname_is("include")) return "include \"path\"; / include std;~compiles a file or the embedded standard library at that point, its signatures first.";
if (hname_is("embed")) return "embed \"path\" as [type] name;~declares name with the bytes of the file at path, read when the unit is compiled. with no type, or bstr, name is a byte string; char* holds one character per word; a number type reads the bytes little-endian; T[] and T[n] lay them out as array elements.";
@@ -2160,6 +2170,7 @@ int bs_name(char* path) {
}
// the offset of the name a declaration line declares: the word before its first delimiter
int decl_off(int base, int ls, int le) {
+ if (hs_typedef) return hs_tname;
if (hs_name >= 0) return hs_name;
int dl = ls, e, b;
while (dl < le && !hdelim(dc(base, dl))) dl++;
@@ -2441,6 +2452,35 @@ int hover_source_function(int i) {
}
return 0;
}
+// the statement typedef T name; that declares hname, found by tokens, at the top level or in a block
+// (the last such one before the cursor, else the first). hdl and hdle are the typedef word and the end
+// of the ;, and hs_tname is where the name is.
+int hover_typedef(int i) {
+ int t = 0, w, depth, last, found = -1, first = -1, fend, fname, hend, hname_at;
+ fm_lex(i);
+ while (t < fm_nt) {
+ if (fm_k[t] == 7) break;
+ if (!fm_is(i, t, "typedef")) { t++; continue; }
+ w = t + 1; depth = 0; last = -1;
+ while (w < fm_nt && fm_k[w] != 7) {
+ if (fm_k[w] == 6 && fm_o[w] == 2 && !depth) break;
+ if (fm_k[w] == 6 && (fm_o[w] == 5 || fm_o[w] == 6)) break;
+ if (fm_k[w] == 6 && fm_o[w] == 3) depth++;
+ else if (fm_k[w] == 6 && fm_o[w] == 4) depth--;
+ else if (fm_k[w] == 1 && !depth) last = w;
+ w++;
+ }
+ if (w < fm_nt && fm_k[w] == 6 && fm_o[w] == 2 && last > t && hs_match(i, last)) {
+ if (first < 0) { first = t; fend = fm_e[w]; fname = fm_s[last]; }
+ if (fm_s[t] <= hcursor) { found = t; hend = fm_e[w]; hname_at = fm_s[last]; }
+ }
+ t = w + 1;
+ }
+ if (first < 0) return 0;
+ if (found < 0) { found = first; hend = fend; hname_at = fname; }
+ hdl = fm_s[found]; hdle = hend; hs_tname = hname_at; hs_typedef = 1; hs_name = -1; hs_first = -1; hs_const = 0; hcs = -1;
+ return 1;
+}
int hover_source_decl(int i) {
int t = 0, head = -1, first = -1, candidate = -1, depth = 0, brackets = 0, init = 0, constant = 0, matched = 0, tail, prev = -1;
fm_lex(i);