← Commit history

types: add type model, class registry and lazy stdlib loading; docs: add main.ash showcase

lennoxrose committed on Oct 17, 2026 at 16:51 UTC

83fcbf6parent5164baf

5 files changed+1331−0

front/src/jtypes/C/classes.cadded

+703−0
Show diff (703 changed lines)
@@ -0,0 +1,703 @@
1+#include <stdarg.h>
2+#include <stdio.h>
3+#include <stdlib.h>
4+#include <string.h>
5+#include <unistd.h>
6+
7+#include "jparse/H/jparse.h"
8+#include "jtypes/H/jtypes.h"
9+
10+#ifndef ASH_STDLIB_DIR
11+#define ASH_STDLIB_DIR "stdlib"
12+#endif
13+
14+static JList roots; /* char* */
15+static JList all_classes; /* JClass* */
16+static int method_ids;
17+
18+void cerr(Node *n, const char *fmt, ...) {
19+ va_list ap;
20+ va_start(ap, fmt);
21+ int len = vsnprintf(NULL, 0, fmt, ap);
22+ va_end(ap);
23+ char *msg = j_alloc((size_t)len + 1);
24+ va_start(ap, fmt);
25+ vsnprintf(msg, (size_t)len + 1, fmt, ap);
26+ va_end(ap);
27+ j_error_soft(n ? n->path : NULL, n ? n->line : 0, n ? n->col : 0, "%s", msg);
28+}
29+
30+void jt_add_root(const char *dir) { j_list_push(&roots, j_strdup(dir)); }
31+
32+static int dir_exists(const char *d) { return access(d, F_OK) == 0; }
33+
34+void jt_init_default_roots(const char *entry_path, const char *entry_pkg) {
35+ /* source root = entry file path minus its package directories */
36+ char *dir = j_strdup(entry_path);
37+ char *slash = strrchr(dir, '/');
38+ if (slash) *slash = 0; else strcpy(dir, ".");
39+ char *root = j_strdup(dir);
40+ if (entry_pkg && *entry_pkg) {
41+ char *pk = j_strdup(entry_pkg);
42+ for (char *c = pk; *c; c++) if (*c == '.') *c = '/';
43+ size_t dl = strlen(dir), pl = strlen(pk);
44+ if (dl >= pl && strcmp(dir + dl - pl, pk) == 0 && (dl == pl || dir[dl - pl - 1] == '/')) {
45+ root = j_strndup(dir, dl > pl ? dl - pl - 1 : 0);
46+ if (!*root) root = j_strdup(".");
47+ }
48+ }
49+ jt_add_root(root);
50+ /* dependency roots (forgepack): <root>/ash.libs/<lib>, <root>/@ash-modules/<lib>/src/main/ash */
51+ const char *env = getenv("ASH_PATH");
52+ if (env) {
53+ char *e = j_strdup(env);
54+ for (char *tok = strtok(e, ":"); tok; tok = strtok(NULL, ":")) jt_add_root(tok);
55+ }
56+ const char *std = getenv("ASH_STDLIB");
57+ jt_add_root(std ? std : ASH_STDLIB_DIR);
58+ (void)dir_exists;
59+}
60+
61+/* ---------------------------------------------------------------- registry */
62+
63+int jt_all_classes(JClass ***out) {
64+ *out = (JClass **)all_classes.items;
65+ return all_classes.count;
66+}
67+
68+static JClass *registry_find(const char *qname) {
69+ for (int i = 0; i < all_classes.count; i++) {
70+ if (strcmp(((JClass *)all_classes.items[i])->qname, qname) == 0) return all_classes.items[i];
71+ }
72+ return NULL;
73+}
74+
75+
76+static JClass *declare(Node *unit, Node *d, JClass *outer) {
77+ JClass *c = j_alloc(sizeof(JClass));
78+ c->name = d->name;
79+ c->pkg = unit->name;
80+ if (outer) c->qname = j_fmt("%s.%s", outer->qname, d->name);
81+ else c->qname = *unit->name ? j_fmt("%s.%s", unit->name, d->name) : d->name;
82+ char *m = j_strdup(c->qname);
83+ for (char *p = m; *p; p++) if (*p == '.') *p = '_';
84+ c->mangled = m;
85+ c->kind = d->kind;
86+ c->flags = d->flags;
87+ c->decl = d;
88+ c->path = d->path;
89+ c->unit = unit;
90+ c->outer = outer;
91+ c->order = all_classes.count;
92+ if (registry_find(c->qname)) {
93+ cerr(d, "duplicate definition of type '%s'", c->qname);
94+ return c;
95+ }
96+ d->sym = c;
97+ j_list_push(&all_classes, c);
98+ if (outer) {
99+ j_list_push(&outer->nested, c);
100+ c->flags |= M_STATIC;
101+ if (outer->kind == N_INTERFACE) c->flags |= M_PUBLIC;
102+ }
103+ for (int i = 0; i < d->l1.count; i++) {
104+ Node *mem = d->l1.items[i];
105+ if (mem->kind == N_CLASS || mem->kind == N_INTERFACE || mem->kind == N_ENUM || mem->kind == N_RECORD) {
106+ declare(unit, mem, c);
107+ }
108+ }
109+ return c;
110+}
111+
112+JClass *jt_load_unit(const char *path, const char *src) {
113+ Node *u = jparse_unit(path, src);
114+ JClass *first = NULL;
115+ for (int i = 0; i < u->l2.count; i++) {
116+ JClass *c = declare(u, u->l2.items[i], NULL);
117+ if (!first) first = c;
118+ }
119+ return first;
120+}
121+
122+static int path_has_decl(const char *file, const char *simple) { (void)file; (void)simple; return 1; }
123+
124+JClass *jt_find_class(const char *qname) {
125+ JClass *c = registry_find(qname);
126+ if (c) return c;
127+ /* try to load <root>/<a/b/C>.ash; for nested names load the outer file */
128+ char *copy = j_strdup(qname);
129+ for (;;) {
130+ char *file = j_strdup(copy);
131+ for (char *p = file; *p; p++) if (*p == '.') *p = '/';
132+ for (int i = 0; i < roots.count; i++) {
133+ char *full = j_fmt("%s/%s.ash", (char *)roots.items[i], file);
134+ char *src = j_read_file(full);
135+ if (src && path_has_decl(full, copy)) {
136+ jt_load_unit(full, src);
137+ c = registry_find(qname);
138+ if (c) return c;
139+ }
140+ }
141+ char *dot = strrchr(copy, '.');
142+ if (!dot) return NULL;
143+ *dot = 0;
144+ }
145+}
146+
147+JClass *jt_well_known(const char *qname) {
148+ JClass *c = jt_find_class(qname);
149+ if (!c) {
150+ fprintf(stderr, "error: standard library type '%s' not found (looked in the Ash stdlib; set ASH_STDLIB)\n", qname);
151+ exit(1);
152+ }
153+ return c;
154+}
155+
156+static JClass *object_cls, *string_cls;
157+JClass *jt_object(void) {
158+ if (!object_cls) object_cls = jt_well_known("ash.lang.Object");
159+ return object_cls;
160+}
161+JClass *jt_string(void) {
162+ if (!string_cls) string_cls = jt_well_known("ash.lang.String");
163+ return string_cls;
164+}
165+JType *jt_string_type(void) { jt_complete(jt_string()); return jt_string()->self_type; }
166+JType *jt_object_type(void) { jt_complete(jt_object()); return jt_object()->self_type; }
167+
168+/* ---------------------------------------------------------------- type resolution */
169+
170+static JClass *find_nested(JClass *c, const char *name, int depth) {
171+ if (!c || depth > 8) return NULL;
172+ for (int i = 0; i < c->nested.count; i++) {
173+ JClass *n = c->nested.items[i];
174+ if (strcmp(n->name, name) == 0) return n;
175+ }
176+ jt_complete(c);
177+ if (c->super) {
178+ JClass *r = find_nested(c->super->cls, name, depth + 1);
179+ if (r) return r;
180+ }
181+ for (int i = 0; i < c->ifaces.count; i++) {
182+ JClass *r = find_nested(((JType *)c->ifaces.items[i])->cls, name, depth + 1);
183+ if (r) return r;
184+ }
185+ return NULL;
186+}
187+
188+static JClass *resolve_class_simple(const char *name, JClass *ctx) {
189+ for (JClass *c = ctx; c; c = c->outer) {
190+ if (strcmp(c->name, name) == 0) return c;
191+ JClass *n = find_nested(c, name, 0);
192+ if (n) return n;
193+ }
194+ Node *unit = ctx ? ctx->unit : NULL;
195+ if (unit) {
196+ const char *pkg = unit->name;
197+ JClass *c = jt_find_class(*pkg ? j_fmt("%s.%s", pkg, name) : name);
198+ if (c) return c;
199+ for (int i = 0; i < unit->l1.count; i++) {
200+ Node *im = unit->l1.items[i];
201+ if (!im->flags) {
202+ const char *last = strrchr(im->name, '.');
203+ last = last ? last + 1 : im->name;
204+ if (strcmp(last, name) == 0) {
205+ c = jt_find_class(im->name);
206+ if (c) return c;
207+ }
208+ }
209+ }
210+ for (int i = 0; i < unit->l1.count; i++) {
211+ Node *im = unit->l1.items[i];
212+ if (im->flags) {
213+ c = jt_find_class(j_fmt("%s.%s", im->name, name));
214+ if (c) return c;
215+ }
216+ }
217+ }
218+ JClass *c = jt_find_class(j_fmt("ash.lang.%s", name));
219+ if (c) return c;
220+ return jt_find_class(j_fmt("ash.lang.function.%s", name));
221+}
222+
223+static JTVar *find_tvar(const char *name, JClass *ctx, JMethod *mctx) {
224+ if (mctx) {
225+ for (int i = 0; i < mctx->tparams.count; i++) {
226+ JTVar *tv = mctx->tparams.items[i];
227+ if (strcmp(tv->name, name) == 0) return tv;
228+ }
229+ }
230+ for (JClass *c = ctx; c; c = c->outer) {
231+ for (int i = 0; i < c->tparams.count; i++) {
232+ JTVar *tv = c->tparams.items[i];
233+ if (strcmp(tv->name, name) == 0) return tv;
234+ }
235+ }
236+ return NULL;
237+}
238+
239+static JType *prim_by_name(const char *n) {
240+ static const struct { const char *n; TyKind k; } tbl[] = {
241+ {"void", TY_VOID}, {"bool", TY_BOOL}, {"byte", TY_BYTE}, {"short", TY_SHORT},
242+ {"char", TY_CHAR}, {"int", TY_INT}, {"long", TY_LONG}, {"float", TY_FLOAT}, {"double", TY_DOUBLE},
243+ };
244+ for (size_t i = 0; i < sizeof(tbl) / sizeof(tbl[0]); i++) {
245+ if (strcmp(tbl[i].n, n) == 0) return ty_prim(tbl[i].k);
246+ }
247+ return NULL;
248+}
249+
250+JType *jt_resolve_type(Node *tn, JClass *ctx, JMethod *mctx, int report) {
251+ JType *base = prim_by_name(tn->name);
252+ if (base) {
253+ for (int i = 0; i < tn->flags; i++) base = ty_array(base);
254+ if (tn->nullable) {
255+ if (ty_is_prim(base)) {
256+ if (report) cerr(tn, "primitive type '%s' cannot be nullable; use the boxed type", ty_str(base));
257+ } else {
258+ base = ty_with_null(base, 1);
259+ }
260+ }
261+ return base;
262+ }
263+ int dims = tn->flags & 0xFFFF;
264+ JType *t = NULL;
265+ if (!strchr(tn->name, '.')) {
266+ JTVar *tv = find_tvar(tn->name, ctx, mctx);
267+ if (tv) t = ty_tvar(tv);
268+ }
269+ if (!t) {
270+ JClass *c = NULL;
271+ char *copy = j_strdup(tn->name);
272+ char *dot = strchr(copy, '.');
273+ if (!dot) {
274+ c = resolve_class_simple(copy, ctx);
275+ } else {
276+ *dot = 0;
277+ JClass *outer = resolve_class_simple(copy, ctx);
278+ if (outer) {
279+ c = outer;
280+ char *rest = dot + 1;
281+ for (char *seg = strtok(rest, "."); seg && c; seg = strtok(NULL, ".")) c = find_nested(c, seg, 0);
282+ }
283+ if (!c) c = jt_find_class(tn->name);
284+ }
285+ if (!c) {
286+ if (report) cerr(tn, "cannot find type '%s'", tn->name);
287+ return ty_error();
288+ }
289+ JType **args = NULL;
290+ int nargs = tn->l1.count;
291+ if (nargs) {
292+ args = j_alloc(sizeof(JType *) * (size_t)nargs);
293+ for (int i = 0; i < nargs; i++) {
294+ Node *an = tn->l1.items[i];
295+ if (an->kind == N_WILDCARD) {
296+ args[i] = an->a ? jt_resolve_type(an->a, ctx, mctx, report) : jt_object_type();
297+ } else {
298+ args[i] = jt_resolve_type(an, ctx, mctx, report);
299+ if (ty_is_prim(args[i]) && report) {
300+ cerr(an, "type arguments must be reference types; use '%s' instead of '%s'",
301+ ty_str(ty_box(args[i])), ty_str(args[i]));
302+ }
303+ }
304+ }
305+ }
306+ t = ty_class(c, args, nargs);
307+ }
308+ for (int i = 0; i < dims; i++) t = ty_array(t);
309+ if (tn->nullable) t = ty_with_null(t, 1);
310+ return t;
311+}
312+
313+/* ---------------------------------------------------------------- headers and members */
314+
315+static const char *anno_str(JList *annos, const char *name, int *present) {
316+ for (int i = 0; i < annos->count; i++) {
317+ Node *a = annos->items[i];
318+ if (strcmp(a->name, name) == 0) {
319+ if (present) *present = 1;
320+ return a->str;
321+ }
322+ }
323+ return NULL;
324+}
325+
326+static int has_anno(JList *annos, const char *name) {
327+ int p = 0;
328+ anno_str(annos, name, &p);
329+ return p;
330+}
331+
332+static JMethod *new_method(JClass *c, const char *name, int flags, JType *ret) {
333+ JMethod *m = j_alloc(sizeof(JMethod));
334+ m->name = name;
335+ m->flags = flags;
336+ m->cls = c;
337+ m->ret = ret;
338+ m->id = method_ids++;
339+ return m;
340+}
341+
342+static void add_param(JMethod *m, const char *name, JType *t) {
343+ JParam *p = j_alloc(sizeof(JParam));
344+ p->name = name;
345+ p->type = t;
346+ j_list_push(&m->params, p);
347+}
348+
349+static JField *add_field(JClass *c, const char *name, JType *t, int flags, Node *decl) {
350+ JField *f = j_alloc(sizeof(JField));
351+ f->name = name;
352+ f->type = t;
353+ f->flags = flags;
354+ f->cls = c;
355+ f->decl = decl;
356+ f->ordinal = c->fields.count;
357+ j_list_push(&c->fields, f);
358+ return f;
359+}
360+
361+static int has_method_sig(JClass *c, const char *name, int nparams) {
362+ for (int i = 0; i < c->methods.count; i++) {
363+ JMethod *m = c->methods.items[i];
364+ if (strcmp(m->name, name) == 0 && m->params.count == nparams) return 1;
365+ }
366+ return 0;
367+}
368+
369+static void member_field(JClass *c, Node *f) {
370+ JType *t = jt_resolve_type(f->a, c, NULL, 1);
371+ int flags = f->flags;
372+ if (c->kind == N_INTERFACE) flags |= M_PUBLIC | M_STATIC | M_FINAL;
373+ if (!(flags & (M_PUBLIC | M_PROTECTED | M_PRIVATE))) flags |= M_PRIVATE; /* D7 */
374+ if ((flags & M_PUBLIC) && !(flags & M_FINAL) && !has_anno(&f->l1, "PublicField") && c->kind != N_INTERFACE &&
375+ !(c->flags & 0x40000)) {
376+ cerr(f, "public mutable field '%s' is not allowed; make it private, final, or annotate it with @PublicField", f->name);
377+ }
378+ JField *jf = add_field(c, f->name, t, flags, f);
379+ jf->tmpl = anno_str(&f->l1, "Lower", NULL);
380+ for (int i = 0; i < c->fields.count - 1; i++) {
381+ if (strcmp(((JField *)c->fields.items[i])->name, f->name) == 0) cerr(f, "duplicate field '%s'", f->name);
382+ }
383+}
384+
385+static void member_method(JClass *c, Node *m) {
386+ int flags = m->flags;
387+ JMethod *jm = new_method(c, m->name, flags, NULL);
388+ jm->decl = m;
389+ jm->is_ctor = m->kind == N_CTOR;
390+ for (int i = 0; i < m->l2.count; i++) {
391+ Node *tp = m->l2.items[i];
392+ JTVar *tv = j_alloc(sizeof(JTVar));
393+ tv->name = tp->name;
394+ tv->owner_m = jm;
395+ j_list_push(&jm->tparams, tv);
396+ }
397+ for (int i = 0; i < m->l2.count; i++) {
398+ Node *tp = m->l2.items[i];
399+ if (tp->a) ((JTVar *)jm->tparams.items[i])->bound = jt_resolve_type(tp->a, c, jm, 1);
400+ }
401+ if (c->kind == N_INTERFACE) {
402+ if (!(flags & (M_PRIVATE))) jm->flags |= M_PUBLIC;
403+ if (!m->b && !(flags & (M_STATIC | M_NATIVE))) jm->flags |= M_ABSTRACT;
404+ }
405+ jm->ret = m->a ? jt_resolve_type(m->a, c, jm, 1) : ty_prim(TY_VOID);
406+ if (m->flags & 0x20000) { /* compact canonical record constructor: params = components */
407+ for (int i = 0; i < c->decl->l4.count; i++) {
408+ Node *rc = c->decl->l4.items[i];
409+ add_param(jm, rc->name, jt_resolve_type(rc->a, c, jm, 1));
410+ }
411+ }
412+ for (int i = 0; i < m->l1.count; i++) {
413+ Node *p = m->l1.items[i];
414+ add_param(jm, p->name, jt_resolve_type(p->a, c, jm, 1));
415+ if (p->flags & M_VARARGS) jm->varargs = 1;
416+ }
417+ for (int i = 0; i < m->l3.count; i++) {
418+ j_list_push(&jm->throws, jt_resolve_type(m->l3.items[i], c, jm, 1));
419+ }
420+ jm->tmpl = anno_str(&m->l4, "Lower", NULL);
421+ jm->has_override_anno = has_anno(&m->l4, "Override");
422+ if ((jm->flags & M_ABSTRACT) && m->b) cerr(m, "abstract method '%s' cannot have a body", m->name);
423+ if (!(jm->flags & (M_ABSTRACT | M_NATIVE)) && !m->b && !jm->tmpl) {
424+ cerr(m, "method '%s' needs a body (or declare it abstract/native)", m->name);
425+ }
426+ if (jm->is_ctor) j_list_push(&c->ctors, jm);
427+ else j_list_push(&c->methods, jm);
428+ m->sym = jm;
429+}
430+
431+static void synth_record(JClass *c) {
432+ Node *d = c->decl;
433+ JMethod *canon = NULL;
434+ for (int i = 0; i < d->l4.count; i++) {
435+ Node *rc = d->l4.items[i];
436+ JType *t = jt_resolve_type(rc->a, c, NULL, 1);
437+ add_field(c, rc->name, t, M_PRIVATE | M_FINAL, rc);
438+ }
439+ for (int i = 0; i < c->ctors.count; i++) {
440+ JMethod *k = c->ctors.items[i];
441+ if (k->params.count == d->l4.count) {
442+ int same = 1;
443+ for (int j = 0; j < k->params.count; j++) {
444+ JField *f = c->fields.items[j];
445+ if (!ty_same(((JParam *)k->params.items[j])->type, f->type)) same = 0;
446+ }
447+ if (same) canon = k;
448+ }
449+ }
450+ if (!canon) {
451+ canon = new_method(c, c->name, M_PUBLIC, ty_prim(TY_VOID));
452+ canon->is_ctor = 1;
453+ canon->synthetic = 1;
454+ for (int i = 0; i < d->l4.count; i++) {
455+ JField *f = c->fields.items[i];
456+ add_param(canon, f->name, f->type);
457+ }
458+ j_list_push(&c->ctors, canon);
459+ }
460+ for (int i = 0; i < d->l4.count; i++) {
461+ JField *f = c->fields.items[i];
462+ if (!has_method_sig(c, f->name, 0)) {
463+ JMethod *acc = new_method(c, f->name, M_PUBLIC, f->type);
464+ acc->synthetic = 1;
465+ acc->is_record_accessor = 1;
466+ j_list_push(&c->methods, acc);
467+ }
468+ }
469+ JType *str = jt_string_type();
470+ if (!has_method_sig(c, "toString", 0)) {
471+ JMethod *m = new_method(c, "toString", M_PUBLIC, str);
472+ m->synthetic = 1;
473+ j_list_push(&c->methods, m);
474+ }
475+ if (!has_method_sig(c, "hashCode", 0)) {
476+ JMethod *m = new_method(c, "hashCode", M_PUBLIC, ty_prim(TY_INT));
477+ m->synthetic = 1;
478+ j_list_push(&c->methods, m);
479+ }
480+ if (!has_method_sig(c, "equals", 1)) {
481+ JMethod *m = new_method(c, "equals", M_PUBLIC, ty_prim(TY_BOOL));
482+ m->synthetic = 1;
483+ add_param(m, "o", jt_object_type());
484+ j_list_push(&c->methods, m);
485+ }
486+}
487+
488+static void synth_enum(JClass *c) {
489+ Node *d = c->decl;
490+ JType *self = ty_class(c, NULL, 0);
491+ for (int i = 0; i < d->l4.count; i++) {
492+ Node *ec = d->l4.items[i];
493+ JField *f = add_field(c, ec->name, self, M_PUBLIC | M_STATIC | M_FINAL, ec);
494+ f->is_enum_const = 1;
495+ j_list_push(&c->enum_names, (void *)ec->name);
496+ }
497+ JMethod *values = new_method(c, "values", M_PUBLIC | M_STATIC, ty_array(self));
498+ values->synthetic = 1;
499+ j_list_push(&c->methods, values);
500+ JMethod *vo = new_method(c, "valueOf", M_PUBLIC | M_STATIC, self);
501+ vo->synthetic = 1;
502+ add_param(vo, "name", jt_string_type());
503+ j_list_push(&c->methods, vo);
504+}
505+
506+void jt_complete(JClass *c) {
507+ if (c->header_done) return;
508+ c->header_done = 1;
509+ Node *d = c->decl;
510+ /* type parameters */
511+ JType **targs = NULL;
512+ if (d->l2.count) targs = j_alloc(sizeof(JType *) * (size_t)d->l2.count);
513+ for (int i = 0; i < d->l2.count; i++) {
514+ Node *tp = d->l2.items[i];
515+ JTVar *tv = j_alloc(sizeof(JTVar));
516+ tv->name = tp->name;
517+ tv->owner_cls = c;
518+ j_list_push(&c->tparams, tv);
519+ targs[i] = ty_tvar(tv);
520+ }
521+ c->self_type = ty_class(c, targs, d->l2.count);
522+ for (int i = 0; i < d->l2.count; i++) {
523+ Node *tp = d->l2.items[i];
524+ if (tp->a) ((JTVar *)c->tparams.items[i])->bound = jt_resolve_type(tp->a, c, NULL, 1);
525+ }
526+ /* supertypes */
527+ int is_object = strcmp(c->qname, "ash.lang.Object") == 0;
528+ if (c->kind == N_INTERFACE) {
529+ for (int i = 0; i < d->l3.count; i++) j_list_push(&c->ifaces, jt_resolve_type(d->l3.items[i], c, NULL, 1));
530+ } else {
531+ if (d->a) {
532+ c->super = jt_resolve_type(d->a, c, NULL, 1);
533+ if (c->super->kind == TY_CLASS) {
534+ JClass *sc = c->super->cls;
535+ jt_complete(sc);
536+ if (sc->kind == N_INTERFACE) cerr(d->a, "class '%s' cannot extend interface '%s'; use implements", c->name, sc->name);
537+ else if (sc->kind == N_ENUM || sc->kind == N_RECORD) cerr(d->a, "cannot extend %s '%s'", sc->kind == N_ENUM ? "enum" : "record", sc->name);
538+ else if (sc->flags & M_FINAL) cerr(d->a, "cannot extend final class '%s'", sc->name);
539+ } else {
540+ c->super = NULL;
541+ }
542+ } else if (c->kind == N_ENUM) {
543+ JClass *e = jt_well_known("ash.lang.Enum");
544+ JType **a = j_alloc(sizeof(JType *));
545+ a[0] = ty_class(c, NULL, 0);
546+ c->super = ty_class(e, a, 1);
547+ } else if (c->kind == N_RECORD) {
548+ c->super = jt_well_known("ash.lang.Record")->self_type;
549+ } else if (!is_object) {
550+ c->super = jt_object_type();
551+ }
552+ for (int i = 0; i < d->l3.count; i++) {
553+ JType *it = jt_resolve_type(d->l3.items[i], c, NULL, 1);
554+ if (it->kind == TY_CLASS) {
555+ jt_complete(it->cls);
556+ if (it->cls->kind != N_INTERFACE) {
557+ cerr(d->l3.items[i], "'%s' is not an interface; classes use extends for superclasses", it->cls->name);
558+ continue;
559+ }
560+ }
561+ j_list_push(&c->ifaces, it);
562+ }
563+ }
564+ if (c->super && c->super->kind == TY_CLASS) j_list_push(&c->super->cls->subclasses, c);
565+ for (int i = 0; i < c->ifaces.count; i++) {
566+ JType *it = c->ifaces.items[i];
567+ if (it->kind == TY_CLASS) j_list_push(&it->cls->subclasses, c);
568+ }
569+ for (int i = 0; i < d->l5.count; i++) {
570+ JType *pt = jt_resolve_type(d->l5.items[i], c, NULL, 1);
571+ if (pt->kind == TY_CLASS) j_list_push(&c->permits, pt->cls);
572+ }
573+ /* members */
574+ if (c->kind == N_ENUM) synth_enum(c);
575+ if (c->kind == N_RECORD) {
576+ for (int i = 0; i < d->l1.count; i++) {
577+ Node *mem = d->l1.items[i];
578+ if (mem->kind == N_CTOR) member_method(c, mem);
579+ }
580+ }
581+ for (int i = 0; i < d->l1.count; i++) {
582+ Node *mem = d->l1.items[i];
583+ if (c->kind == N_RECORD && mem->kind == N_CTOR) continue;
584+ if (mem->kind == N_FIELD) member_field(c, mem);
585+ else if (mem->kind == N_SEQ) {
586+ for (int j = 0; j < mem->l1.count; j++) member_field(c, mem->l1.items[j]);
587+ } else if (mem->kind == N_METHOD || mem->kind == N_CTOR) member_method(c, mem);
588+ }
589+ if (c->kind == N_RECORD) synth_record(c);
590+ if (c->kind != N_INTERFACE && c->ctors.count == 0) {
591+ JMethod *k = new_method(c, c->name, c->kind == N_ENUM ? M_PRIVATE : (c->flags & (M_PUBLIC | M_PROTECTED | M_PRIVATE)), ty_prim(TY_VOID));
592+ k->is_ctor = 1;
593+ k->synthetic = 1;
594+ j_list_push(&c->ctors, k);
595+ }
596+ c->members_done = 1;
597+}
598+
599+/* ---------------------------------------------------------------- member lookup */
600+
601+JField *jt_find_field(JClass *c, const char *name) {
602+ jt_complete(c);
603+ for (int i = 0; i < c->fields.count; i++) {
604+ JField *f = c->fields.items[i];
605+ if (strcmp(f->name, name) == 0) return f;
606+ }
607+ if (c->super) {
608+ JField *f = jt_find_field(c->super->cls, name);
609+ if (f) return f;
610+ }
611+ for (int i = 0; i < c->ifaces.count; i++) {
612+ JField *f = jt_find_field(((JType *)c->ifaces.items[i])->cls, name);
613+ if (f) return f;
614+ }
615+ return NULL;
616+}
617+
618+static int same_erased_params(JMethod *a, JMethod *b) {
619+ if (a->params.count != b->params.count) return 0;
620+ for (int i = 0; i < a->params.count; i++) {
621+ JType *x = ty_erase_bound(((JParam *)a->params.items[i])->type);
622+ JType *y = ty_erase_bound(((JParam *)b->params.items[i])->type);
623+ if (x->kind != y->kind) {
624+ if (!(x->kind == TY_CLASS && y->kind == TY_CLASS)) return 0;
625+ }
626+ if (x->kind == TY_CLASS && y->kind == TY_CLASS) {
627+ /* a type variable on one side is treated as matching any reference type: it may be
628+ * the implementation of a generic parameter (e.g. compareTo(T) vs compareTo(Point)) */
629+ JType *xp = ((JParam *)a->params.items[i])->type, *yp = ((JParam *)b->params.items[i])->type;
630+ if (xp->kind == TY_TVAR || yp->kind == TY_TVAR) continue;
631+ if (x->cls != y->cls) return 0;
632+ }
633+ if (x->kind == TY_ARRAY && !ty_same(x, y)) return 0;
634+ if (ty_is_prim(x) && x->kind != y->kind) return 0;
635+ }
636+ return 1;
637+}
638+
639+static void collect(JClass *c, const char *name, JList *out, int depth) {
640+ if (depth > 16) return;
641+ jt_complete(c);
642+ for (int i = 0; i < c->methods.count; i++) {
643+ JMethod *m = c->methods.items[i];
644+ if (strcmp(m->name, name) != 0) continue;
645+ int dup = 0;
646+ for (int j = 0; j < out->count; j++) {
647+ if (same_erased_params(out->items[j], m)) { dup = 1; break; }
648+ }
649+ if (!dup) j_list_push(out, m);
650+ }
651+ if (c->super) collect(c->super->cls, name, out, depth + 1);
652+ for (int i = 0; i < c->ifaces.count; i++) collect(((JType *)c->ifaces.items[i])->cls, name, out, depth + 1);
653+}
654+
655+void jt_collect_methods(JClass *c, const char *name, JList *out) {
656+ collect(c, name, out, 0);
657+ if (c->kind == N_INTERFACE) collect(jt_object(), name, out, 0);
658+}
659+
660+static void collect_abstract(JClass *c, JList *out, int depth) {
661+ if (depth > 16) return;
662+ jt_complete(c);
663+ for (int i = 0; i < c->methods.count; i++) {
664+ JMethod *m = c->methods.items[i];
665+ if (!(m->flags & M_ABSTRACT) || (m->flags & M_STATIC)) continue;
666+ if (!strcmp(m->name, "equals") || !strcmp(m->name, "hashCode") || !strcmp(m->name, "toString")) continue;
667+ int dup = 0;
668+ for (int j = 0; j < out->count; j++) {
669+ JMethod *o = out->items[j];
670+ if (!strcmp(o->name, m->name) && o->params.count == m->params.count) dup = 1;
671+ }
672+ if (!dup) j_list_push(out, m);
673+ }
674+ for (int i = 0; i < c->ifaces.count; i++) collect_abstract(((JType *)c->ifaces.items[i])->cls, out, depth + 1);
675+}
676+
677+JMethod *jt_find_sam(JClass *c) {
678+ if (c->sam_done) return c->sam;
679+ c->sam_done = 1;
680+ if (c->kind != N_INTERFACE) return NULL;
681+ JList abs = {0};
682+ collect_abstract(c, &abs, 0);
683+ /* an abstract method re-declared with a default body in a sub-interface is not abstract */
684+ JList real = {0};
685+ for (int i = 0; i < abs.count; i++) {
686+ JMethod *m = abs.items[i];
687+ JList all = {0};
688+ jt_collect_methods(c, m->name, &all);
689+ int implemented = 0;
690+ for (int j = 0; j < all.count; j++) {
691+ JMethod *o = all.items[j];
692+ if (!(o->flags & M_ABSTRACT) && !(o->flags & M_STATIC) && o->params.count == m->params.count &&
693+ o->cls != jt_object()) implemented = 1;
694+ }
695+ if (!implemented) j_list_push(&real, m);
696+ }
697+ if (real.count == 1) c->sam = real.items[0];
698+ return c->sam;
699+}
700+
701+int jt_is_functional(JType *t) {
702+ return t->kind == TY_CLASS && t->cls->kind == N_INTERFACE && jt_find_sam(t->cls) != NULL;
703+}

front/src/jtypes/C/types.cadded

+270−0
@@ -0,0 +1,270 @@
1+#include <stdlib.h>
2+#include <string.h>
3+
4+#include "jtypes/H/jtypes.h"
5+
6+static JType prims[TY_ERROR + 1];
7+static int prims_ready;
8+
9+static void init_prims(void) {
10+ for (int i = 0; i <= TY_ERROR; i++) prims[i].kind = (TyKind)i;
11+ prims_ready = 1;
12+}
13+
14+JType *ty_prim(TyKind k) {
15+ if (!prims_ready) init_prims();
16+ return &prims[k];
17+}
18+
19+JType *ty_null(void) { return ty_prim(TY_NULL); }
20+JType *ty_error(void) { return ty_prim(TY_ERROR); }
21+
22+JType *ty_class(JClass *c, JType **args, int nargs) {
23+ JType *t = j_alloc(sizeof(JType));
24+ t->kind = TY_CLASS;
25+ t->cls = c;
26+ t->args = args;
27+ t->nargs = nargs;
28+ return t;
29+}
30+
31+JType *ty_array(JType *elem) {
32+ JType *t = j_alloc(sizeof(JType));
33+ t->kind = TY_ARRAY;
34+ t->elem = elem;
35+ return t;
36+}
37+
38+JType *ty_tvar(JTVar *tv) {
39+ JType *t = j_alloc(sizeof(JType));
40+ t->kind = TY_TVAR;
41+ t->tv = tv;
42+ return t;
43+}
44+
45+JType *ty_with_null(JType *t, int nullable) {
46+ if (t->kind != TY_CLASS && t->kind != TY_ARRAY && t->kind != TY_TVAR) return t;
47+ if (t->nullable == nullable) return t;
48+ JType *c = j_alloc(sizeof(JType));
49+ *c = *t;
50+ c->nullable = nullable;
51+ return c;
52+}
53+
54+int ty_is_prim(JType *t) { return t->kind >= TY_BOOL && t->kind <= TY_DOUBLE; }
55+int ty_is_numeric(JType *t) { return t->kind >= TY_BYTE && t->kind <= TY_DOUBLE; }
56+int ty_is_integral(JType *t) { return t->kind >= TY_BYTE && t->kind <= TY_LONG; }
57+int ty_is_ref(JType *t) {
58+ return t->kind == TY_CLASS || t->kind == TY_ARRAY || t->kind == TY_TVAR || t->kind == TY_NULL;
59+}
60+
61+int ty_is_string(JType *t) { return t->kind == TY_CLASS && t->cls == jt_string(); }
62+int ty_is_object(JType *t) { return t->kind == TY_CLASS && t->cls == jt_object(); }
63+
64+int ty_same(JType *a, JType *b) {
65+ if (a == b) return 1;
66+ if (a->kind != b->kind) return 0;
67+ switch (a->kind) {
68+ case TY_CLASS:
69+ if (a->cls != b->cls) return 0;
70+ if (a->nargs && b->nargs) {
71+ if (a->nargs != b->nargs) return 0;
72+ for (int i = 0; i < a->nargs; i++) {
73+ if (!ty_same(a->args[i], b->args[i])) return 0;
74+ }
75+ }
76+ return 1;
77+ case TY_ARRAY: return ty_same(a->elem, b->elem);
78+ case TY_TVAR: return a->tv == b->tv;
79+ default: return 1;
80+ }
81+}
82+
83+const char *ty_str(JType *t) {
84+ JBuf b = {0};
85+ switch (t->kind) {
86+ case TY_VOID: return "void";
87+ case TY_BOOL: return "bool";
88+ case TY_BYTE: return "byte";
89+ case TY_SHORT: return "short";
90+ case TY_CHAR: return "char";
91+ case TY_INT: return "int";
92+ case TY_LONG: return "long";
93+ case TY_FLOAT: return "float";
94+ case TY_DOUBLE: return "double";
95+ case TY_NULL: return "null";
96+ case TY_ERROR: return "<error>";
97+ case TY_TVAR: j_buf_add(&b, t->tv->name); break;
98+ case TY_ARRAY: j_buf_add(&b, ty_str(t->elem)); j_buf_add(&b, "[]"); break;
99+ case TY_CLASS:
100+ j_buf_add(&b, t->cls->name);
101+ if (t->nargs) {
102+ j_buf_addc(&b, '<');
103+ for (int i = 0; i < t->nargs; i++) {
104+ if (i) j_buf_add(&b, ", ");
105+ j_buf_add(&b, ty_str(t->args[i]));
106+ }
107+ j_buf_addc(&b, '>');
108+ }
109+ break;
110+ }
111+ if (t->nullable) j_buf_addc(&b, '?');
112+ return b.data;
113+}
114+
115+static const char *box_names[] = {
116+ [TY_BOOL] = "ash.lang.Boolean", [TY_BYTE] = "ash.lang.Byte", [TY_SHORT] = "ash.lang.Short",
117+ [TY_CHAR] = "ash.lang.Character", [TY_INT] = "ash.lang.Integer", [TY_LONG] = "ash.lang.Long",
118+ [TY_FLOAT] = "ash.lang.Float", [TY_DOUBLE] = "ash.lang.Double",
119+};
120+
121+JType *ty_box(JType *t) {
122+ if (!ty_is_prim(t)) return t;
123+ return jt_well_known(box_names[t->kind])->self_type;
124+}
125+
126+JType *ty_unbox(JType *t) {
127+ if (t->kind != TY_CLASS || !t->cls->qname) return t;
128+ for (int k = TY_BOOL; k <= TY_DOUBLE; k++) {
129+ if (strcmp(t->cls->qname, box_names[k]) == 0) return ty_prim((TyKind)k);
130+ }
131+ return t;
132+}
133+
134+JType *ty_subst(JType *t, JList *tvars, JType **args, int nargs) {
135+ switch (t->kind) {
136+ case TY_TVAR:
137+ for (int i = 0; i < tvars->count && i < nargs; i++) {
138+ if (tvars->items[i] == t->tv) return ty_with_null(args[i], args[i]->nullable || t->nullable);
139+ }
140+ return t;
141+ case TY_ARRAY: {
142+ JType *e = ty_subst(t->elem, tvars, args, nargs);
143+ if (e == t->elem) return t;
144+ return ty_with_null(ty_array(e), t->nullable);
145+ }
146+ case TY_CLASS: {
147+ if (!t->nargs) return t;
148+ JType **na = j_alloc(sizeof(JType *) * (size_t)t->nargs);
149+ int changed = 0;
150+ for (int i = 0; i < t->nargs; i++) {
151+ na[i] = ty_subst(t->args[i], tvars, args, nargs);
152+ if (na[i] != t->args[i]) changed = 1;
153+ }
154+ if (!changed) return t;
155+ return ty_with_null(ty_class(t->cls, na, t->nargs), t->nullable);
156+ }
157+ default:
158+ return t;
159+ }
160+}
161+
162+JType *ty_erase_bound(JType *t) {
163+ if (t->kind == TY_TVAR) return t->tv->bound ? ty_erase_bound(t->tv->bound) : jt_object_type();
164+ return t;
165+}
166+
167+int ty_is_subclass(JClass *c, JClass *target) {
168+ if (c == target) return 1;
169+ if (target == jt_object()) return 1;
170+ jt_complete(c);
171+ if (c->super && ty_is_subclass(c->super->cls, target)) return 1;
172+ for (int i = 0; i < c->ifaces.count; i++) {
173+ if (ty_is_subclass(((JType *)c->ifaces.items[i])->cls, target)) return 1;
174+ }
175+ return 0;
176+}
177+
178+JType *ty_as_super(JType *t, JClass *target) {
179+ if (t->kind == TY_TVAR) return ty_as_super(ty_erase_bound(t), target);
180+ if (t->kind == TY_ARRAY) return target == jt_object() ? jt_object_type() : NULL;
181+ if (t->kind != TY_CLASS) return NULL;
182+ if (t->cls == target) return t;
183+ JClass *c = t->cls;
184+ jt_complete(c);
185+ if (c->super) {
186+ JType *s = ty_subst(c->super, &c->tparams, t->args, t->nargs);
187+ JType *r = ty_as_super(s, target);
188+ if (r) return r;
189+ }
190+ for (int i = 0; i < c->ifaces.count; i++) {
191+ JType *s = ty_subst((JType *)c->ifaces.items[i], &c->tparams, t->args, t->nargs);
192+ JType *r = ty_as_super(s, target);
193+ if (r) return r;
194+ }
195+ if (target == jt_object()) return jt_object_type();
196+ return NULL;
197+}
198+
199+static int prim_widens(TyKind from, TyKind to) {
200+ if (from == to) return 1;
201+ switch (from) {
202+ case TY_BYTE: return to == TY_SHORT || to == TY_INT || to == TY_LONG || to == TY_FLOAT || to == TY_DOUBLE;
203+ case TY_SHORT: return to == TY_INT || to == TY_LONG || to == TY_FLOAT || to == TY_DOUBLE;
204+ case TY_CHAR: return to == TY_INT || to == TY_LONG || to == TY_FLOAT || to == TY_DOUBLE;
205+ case TY_INT: return to == TY_LONG || to == TY_FLOAT || to == TY_DOUBLE;
206+ case TY_LONG: return to == TY_FLOAT || to == TY_DOUBLE;
207+ case TY_FLOAT: return to == TY_DOUBLE;
208+ default: return 0;
209+ }
210+}
211+
212+static int args_compatible(JType *from, JType *to) {
213+ if (!from->nargs || !to->nargs || from->nargs != to->nargs) return 1;
214+ for (int i = 0; i < from->nargs; i++) {
215+ JType *a = from->args[i], *b = to->args[i];
216+ if (a->kind == TY_TVAR || b->kind == TY_TVAR) continue;
217+ if (ty_is_object(b)) continue;
218+ if (!ty_assignable(a, b, 1)) return 0;
219+ }
220+ return 1;
221+}
222+
223+int ty_assignable(JType *from, JType *to, int lenient_null) {
224+ if (from->kind == TY_ERROR || to->kind == TY_ERROR) return 1;
225+ if (from->kind == TY_VOID || to->kind == TY_VOID) return 0;
226+ if (from->kind == TY_NULL) return ty_is_ref(to) && (to->nullable || lenient_null);
227+ if (ty_is_prim(from) && ty_is_prim(to)) return prim_widens(from->kind, to->kind) || (from->kind == TY_BOOL && to->kind == TY_BOOL);
228+ if (ty_is_prim(from)) { /* boxing */
229+ if (!ty_is_ref(to)) return 0;
230+ return ty_assignable(ty_box(from), ty_with_null(to, 1), 1);
231+ }
232+ if (ty_is_prim(to)) { /* unboxing */
233+ JType *u = ty_unbox(from);
234+ if (!ty_is_prim(u)) return 0;
235+ if (from->nullable && !lenient_null) return 0;
236+ return prim_widens(u->kind, to->kind);
237+ }
238+ if (from->nullable && !to->nullable && !lenient_null) return 0;
239+ if (to->kind == TY_TVAR) return from->kind == TY_TVAR && from->tv == to->tv;
240+ if (to->kind == TY_ARRAY) {
241+ if (from->kind != TY_ARRAY) return 0;
242+ if (ty_is_prim(from->elem) || ty_is_prim(to->elem)) return from->elem->kind == to->elem->kind;
243+ return ty_assignable(from->elem, to->elem, 1);
244+ }
245+ if (to->kind == TY_CLASS) {
246+ if (to->cls == jt_object()) return 1;
247+ if (from->kind == TY_ARRAY) return 0;
248+ JType *s = ty_as_super(from, to->cls);
249+ if (!s) return 0;
250+ return args_compatible(s, to);
251+ }
252+ return 0;
253+}
254+
255+JType *ty_binary_numeric(JType *a, JType *b) {
256+ a = ty_unbox(a);
257+ b = ty_unbox(b);
258+ if (!ty_is_numeric(a) || !ty_is_numeric(b)) return NULL;
259+ if (a->kind == TY_DOUBLE || b->kind == TY_DOUBLE) return ty_prim(TY_DOUBLE);
260+ if (a->kind == TY_FLOAT || b->kind == TY_FLOAT) return ty_prim(TY_FLOAT);
261+ if (a->kind == TY_LONG || b->kind == TY_LONG) return ty_prim(TY_LONG);
262+ return ty_prim(TY_INT);
263+}
264+
265+int ty_is_checked_exception(JType *t) {
266+ if (t->kind != TY_CLASS) return 0;
267+ JClass *ex = jt_well_known("ash.lang.Exception");
268+ JClass *rt = jt_well_known("ash.lang.RuntimeException");
269+ return ty_is_subclass(t->cls, ex) && !ty_is_subclass(t->cls, rt);
270+}

front/src/jtypes/H/check.hadded

+75−0
@@ -0,0 +1,75 @@
1+#ifndef CHECK_H
2+#define CHECK_H
3+
4+#include "jtypes/H/jtypes.h"
5+
6+/* Internal checker state shared by check_expr.c, check_stmt.c and check_class.c. */
7+
8+struct JLocal {
9+ const char *name;
10+ JType *type;
11+ int flags; /* M_FINAL */
12+ int nonnull; /* flow-narrowed to non-null */
13+ int assigns; /* assignments after declaration */
14+ int has_init;
15+ int is_param;
16+ int index; /* position in Ctx.locals */
17+ Node *decl;
18+ Node *captured_at; /* first lambda capture site */
19+ const char *lname;
20+ Node *alias; /* instanceof binding: the scrutinee expression */
21+ int is_temp;
22+};
23+
24+typedef enum { F_TRY, F_METHOD, F_LAMBDA, F_INIT } FrameKind;
25+
26+typedef struct ExFrame {
27+ FrameKind kind;
28+ JList types; /* caught types (F_TRY) or declared throws (F_METHOD/F_LAMBDA) */
29+ struct ExFrame *up;
30+} ExFrame;
31+
32+typedef struct Ctx {
33+ JClass *cls;
34+ JMethod *method;
35+ int is_static;
36+ int in_ctor;
37+ JType *ret; /* expected return type; NULL = infer (lambda) */
38+ JList *ret_types; /* collects return expression types while inferring */
39+ int ret_seen_void;
40+ JList locals; /* JLocal* stack */
41+ int lambda_base; /* locals below this index belong to an enclosing function */
42+ ExFrame *ex;
43+ int loop_depth;
44+ int switch_depth;
45+ int lambda_depth;
46+ JList captures; /* (JLocal*) captured by lambdas, validated at method end */
47+} Ctx;
48+
49+/* check_expr.c */
50+JType *check_expr(Ctx *cx, Node *n, JType *expected);
51+JType *check_cond(Ctx *cx, Node *n);
52+int check_assignable(Ctx *cx, Node *expr, JType *from, JType *to, const char *what);
53+void check_coerce(Ctx *cx, Node *expr, JType *to, const char *what); /* check_expr + assign check */
54+JLocal *declare_local(Ctx *cx, Node *at, const char *name, JType *t, int flags, int is_param);
55+JLocal *lookup_local(Ctx *cx, const char *name);
56+void report_throw(Ctx *cx, Node *at, JType *exc);
57+int accessible(Ctx *cx, JClass *owner, int flags);
58+int const_int_value(Node *e, long *out);
59+JType *narrowed(JLocal *l);
60+void narrow_apply(Ctx *cx, Node *cond, int when_true, JList *undo);
61+void narrow_undo(JList *undo);
62+JType *check_new_object(Ctx *cx, Node *n, JType *expected);
63+void check_args_for_ctor(Ctx *cx, Node *at, JClass *c, JType *ctype, JList *args, Node *call_site, JMethod **chosen);
64+
65+/* check_stmt.c */
66+void check_stmt(Ctx *cx, Node *n);
67+void check_block_body(Ctx *cx, Node *block);
68+int can_complete(Node *n);
69+void push_scope(Ctx *cx, int *mark);
70+void pop_scope(Ctx *cx, int mark);
71+
72+/* check_class.c */
73+void check_method_body(JClass *c, JMethod *m);
74+
75+#endif

front/src/jtypes/H/jtypes.hadded

+163−0
@@ -0,0 +1,163 @@
1+#ifndef JTYPES_H
2+#define JTYPES_H
3+
4+#include "jparse/H/jast.h"
5+
6+/* Types, symbols and the checker API for new Ash.
7+ * Generics are erased at runtime but fully tracked here. Wildcards are approximated:
8+ * `? extends X` and `? super X` become X, a bare `?` becomes Object. */
9+
10+typedef struct JType JType;
11+typedef struct JClass JClass;
12+typedef struct JField JField;
13+typedef struct JMethod JMethod;
14+typedef struct JTVar JTVar;
15+typedef struct JLocal JLocal;
16+
17+typedef enum {
18+ TY_VOID, TY_BOOL, TY_BYTE, TY_SHORT, TY_CHAR, TY_INT, TY_LONG, TY_FLOAT, TY_DOUBLE,
19+ TY_NULL, TY_CLASS, TY_ARRAY, TY_TVAR, TY_ERROR
20+} TyKind;
21+
22+struct JTVar {
23+ const char *name;
24+ JType *bound; /* NULL = Object */
25+ JClass *owner_cls;
26+ JMethod *owner_m;
27+};
28+
29+struct JType {
30+ TyKind kind;
31+ JClass *cls;
32+ JType **args;
33+ int nargs;
34+ JType *elem;
35+ JTVar *tv;
36+ int nullable;
37+};
38+
39+typedef struct {
40+ const char *name;
41+ JType *type;
42+} JParam;
43+
44+struct JField {
45+ const char *name;
46+ JType *type;
47+ int flags;
48+ JClass *cls;
49+ Node *decl;
50+ int ordinal; /* index among instance/static fields */
51+ const char *lname; /* lowered name */
52+ const char *tmpl; /* @Lower template for native constants */
53+ int is_enum_const;
54+};
55+
56+struct JMethod {
57+ const char *name;
58+ int flags;
59+ JClass *cls;
60+ JType *ret;
61+ JList params; /* JParam* */
62+ JList tparams; /* JTVar* */
63+ JList throws; /* JType* */
64+ Node *decl;
65+ const char *tmpl; /* @Lower template, NULL for compiled methods */
66+ int is_ctor;
67+ int varargs;
68+ int id;
69+ const char *lname;
70+ int has_override_anno;
71+ int is_record_accessor;
72+ int synthetic; /* generated (record members, enum values()) */
73+};
74+
75+struct JClass {
76+ const char *name;
77+ const char *qname;
78+ const char *pkg;
79+ const char *mangled;
80+ NodeKind kind;
81+ int flags;
82+ Node *decl;
83+ const char *path;
84+ Node *unit;
85+ JClass *outer;
86+ JType *super;
87+ JList ifaces; /* JType* */
88+ JList tparams; /* JTVar* */
89+ JList fields; /* JField* */
90+ JList methods; /* JMethod* (no ctors) */
91+ JList ctors; /* JMethod* */
92+ JList nested; /* JClass* */
93+ JList permits; /* JClass* */
94+ JList subclasses; /* JClass* direct subtypes (for devirtualization / sealed) */
95+ JList enum_names; /* const char* in declaration order */
96+ JType *self_type; /* C<T1..Tn> */
97+ int header_done;
98+ int members_done;
99+ int checked;
100+ int native_value; /* represented by a primitive Ash value: String, boxes, arrays */
101+ int order; /* load order */
102+ JMethod *sam; /* cached single abstract method; sam_done marks the cache as valid */
103+ int sam_done;
104+};
105+
106+/* resolution kinds stored in Node.rk */
107+enum {
108+ R_NONE = 0, R_LOCAL, R_FIELD, R_STATIC_FIELD, R_CLASS, R_PACKAGE, R_ARRAYLEN,
109+ R_METHOD, R_STATIC_METHOD, R_SUPER_METHOD, R_CTOR, R_THIS_FIELD, R_ENUM_CONST,
110+ R_CAPTURED_THIS
111+};
112+
113+/* ---- types (types.c) ---- */
114+JType *ty_prim(TyKind k);
115+JType *ty_null(void);
116+JType *ty_error(void);
117+JType *ty_class(JClass *c, JType **args, int nargs);
118+JType *ty_array(JType *elem);
119+JType *ty_tvar(JTVar *tv);
120+JType *ty_with_null(JType *t, int nullable);
121+int ty_is_numeric(JType *t); /* byte..double (incl. char) */
122+int ty_is_integral(JType *t);
123+int ty_is_prim(JType *t);
124+int ty_is_ref(JType *t);
125+int ty_same(JType *a, JType *b);
126+const char *ty_str(JType *t);
127+JType *ty_unbox(JType *t); /* Integer -> int, else same */
128+JType *ty_box(JType *t); /* int -> Integer, else same */
129+JType *ty_subst(JType *t, JList *tvars, JType **args, int nargs);
130+JType *ty_erase_bound(JType *t);
131+JType *ty_as_super(JType *t, JClass *target); /* parameterization of target seen from t, or NULL */
132+int ty_is_subclass(JClass *c, JClass *target);
133+int ty_assignable(JType *from, JType *to, int allow_null_mismatch);
134+JType *ty_binary_numeric(JType *a, JType *b);
135+int ty_is_string(JType *t);
136+int ty_is_object(JType *t);
137+int ty_is_checked_exception(JType *t);
138+
139+/* ---- registry / loading (classes.c) ---- */
140+void jt_add_root(const char *dir);
141+void jt_init_default_roots(const char *entry_path, const char *entry_pkg);
142+JClass *jt_load_unit(const char *path, const char *src); /* registers every type in the file */
143+JClass *jt_find_class(const char *qname); /* loads lazily from roots; NULL if absent */
144+JClass *jt_well_known(const char *qname); /* aborts when missing (stdlib bug) */
145+JType *jt_resolve_type(Node *tnode, JClass *ctx, JMethod *mctx, int report);
146+void jt_complete(JClass *c); /* resolve header and member signatures */
147+JField *jt_find_field(JClass *c, const char *name);
148+void jt_collect_methods(JClass *c, const char *name, JList *out); /* hierarchy incl. interfaces, Object */
149+JMethod *jt_find_sam(JClass *c);
150+int jt_all_classes(JClass ***out);
151+int jt_is_functional(JType *t);
152+JClass *jt_object(void);
153+JClass *jt_string(void);
154+JType *jt_string_type(void);
155+JType *jt_object_type(void);
156+
157+/* ---- checker (check_*.c) ---- */
158+void jt_check_all(void);
159+void jt_check_class(JClass *c);
160+
161+void cerr(Node *n, const char *fmt, ...);
162+
163+#endif

main.ashadded

+120−0
@@ -0,0 +1,120 @@
1+package demo;
2+
3+import ash.util.List;
4+import ash.util.ArrayList;
5+import ash.util.Map;
6+import ash.util.HashMap;
7+
8+// ---------------------------------------------------------------------------
9+// main.ash - living showcase of the new Java-like Ash syntax.
10+// Updated after each larger migration milestone (see MIGRATION.md).
11+//
12+// Milestone status
13+// [x] Phase 0 specification locked (MIGRATION.md decisions D1-D15)
14+// [x] Phase 1 lexer: new keywords, // /* */ /// comments
15+// [x] Phase 2 parser + AST: everything in this file parses
16+// [ ] Phase 3 type checker (in progress: types, class registry done)
17+// [ ] Phase 4 lowering to ashvm / kiln
18+// [ ] Phase 5 standard library
19+//
20+// Old New
21+// local x = 5; int x = 5; / var x = 5;
22+// say x; System.out.println(x);
23+// given / otherwise if / else
24+// during while
25+// each x in xs for (int x : xs)
26+// stop; next; break; continue;
27+// forge f(a) { yield } int f(int a) { return ...; }
28+// attempt / handle try / catch
29+// raise v; throw new SomeException(...);
30+// yes / no / none true / false / null
31+// @import <f.ash>; import pkg.Type;
32+// and / or / not && / || / !
33+// ---------------------------------------------------------------------------
34+
35+/// A sealed interface with a default method.
36+public sealed interface Shape permits Circle, Square {
37+ double area();
38+
39+ default String describe() {
40+ return "shape with area " + area();
41+ }
42+}
43+
44+/// Records are immutable value types.
45+record Circle(double radius) implements Shape {
46+ public double area() {
47+ return 3.14159 * radius * radius;
48+ }
49+}
50+
51+record Square(double side) implements Shape {
52+ Square {
53+ if (side < 0) {
54+ throw new IllegalArgumentException("negative side");
55+ }
56+ }
57+
58+ public double area() {
59+ return side * side;
60+ }
61+}
62+
63+enum Level {
64+ LOW(1), HIGH(3);
65+
66+ private final int weight;
67+
68+ Level(int weight) {
69+ this.weight = weight;
70+ }
71+
72+ public int getWeight() {
73+ return weight;
74+ }
75+}
76+
77+public class Main {
78+ private static final int LIMIT = 5;
79+
80+ public static int fib(int n) {
81+ return n < 2 ? n : fib(n - 1) + fib(n - 2);
82+ }
83+
84+ public static void main(String[] args) {
85+ List<Shape> shapes = new ArrayList<>();
86+ shapes.add(new Circle(1.5));
87+ shapes.add(new Square(2.0));
88+
89+ for (Shape s : shapes) {
90+ System.out.println(s.describe());
91+ }
92+
93+ Map<String, Integer> counts = new HashMap<>();
94+ counts.put("fib", fib(10));
95+
96+ String? maybe = counts.containsKey("x") ? "present" : null;
97+ if (maybe != null) {
98+ System.out.println(maybe.length());
99+ }
100+
101+ try {
102+ Shape bad = new Square(-1.0);
103+ } catch (IllegalArgumentException e) {
104+ System.out.println("caught: " + e.getMessage());
105+ }
106+
107+ int total = 0;
108+ for (int i = 0; i < 10; i++) {
109+ if (i == 3) { continue; }
110+ if (i == LIMIT + 2) { break; }
111+ total += i;
112+ }
113+
114+ String label = switch (Level.HIGH) {
115+ case LOW -> "low";
116+ case HIGH -> "high";
117+ };
118+ System.out.println(label + " " + total);
119+ }
120+}