← 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
5 files changed+1331−0
front/src/jtypes/C/classes.cadded
+703−0Show 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_DIR11
+#define ASH_STDLIB_DIR "stdlib"12
+#endif13
+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 be628
+ * 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_H2
+#define CHECK_H3
+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
+#endiffront/src/jtypes/H/jtypes.hadded
+163−0@@ -0,0 +1,163 @@1
+#ifndef JTYPES_H2
+#define JTYPES_H3
+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_ERROR20
+} 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_THIS111
+};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
+#endifmain.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 status13
+// [x] Phase 0 specification locked (MIGRATION.md decisions D1-D15)14
+// [x] Phase 1 lexer: new keywords, // /* */ /// comments15
+// [x] Phase 2 parser + AST: everything in this file parses16
+// [ ] Phase 3 type checker (in progress: types, class registry done)17
+// [ ] Phase 4 lowering to ashvm / kiln18
+// [ ] Phase 5 standard library19
+//20
+// Old New21
+// local x = 5; int x = 5; / var x = 5;22
+// say x; System.out.println(x);23
+// given / otherwise if / else24
+// during while25
+// 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 / catch29
+// raise v; throw new SomeException(...);30
+// yes / no / none true / false / null31
+// @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
+}