aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/compiler.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 09:39:22 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 09:39:22 +0000
commit6bb3d79cbd853601623095b497a1807489c3ef6e (patch)
tree188bc9ceb6d6db1670427f4355529eb8d46d1494 /src/compiler/compiler.zc
parenttodo: record the reverted unchecked trial and what it showed (diff)
add c-style typedef and a built-in string alias
typedef T name; binds name to the type record of T, at the top level or in a function (scoped to its block). string is a built-in alias for char*. A type name straight before , or ) in a parameter list is the parameter's name, so fn f(string) still works. The language server shows typedef T name; on hover and highlights typedef as a keyword.
Diffstat (limited to 'src/compiler/compiler.zc')
-rw-r--r--src/compiler/compiler.zc107
1 files changed, 100 insertions, 7 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("--");