Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,18 @@ intuition — read `doc/ficustut.md` and existing code. Verified traps:
- Border access: mode before the bracket — `a.clip[i]`, `a.wrap[i]`,
`a.zero[i]` (arrays, Vector, strings).
- Comprehensions: 1D `[for i <- 0:n {..}]`; 2D `[for i <- 0:m for j <- 0:n
{..}]`; zip is comma; index binding `for x@i <- a` / `for x@(i,j) <- m`;
fold `fold acc=init for x <- a {..}`. Lists: `[:: 1, 2, 3]`, cons `::`,
list-comp `[:: for x <- l {..}]`.
{..}]`; zip is comma; index binding `for x@i <- a` / `for x@(i,j) <- m`.
Lists: `[:: 1, 2, 3]`, cons `::`, list-comp `[:: for x <- l {..}]`.
- **fold (fold-1 reform)** is now imperative: `fold acc=init for x <- a {acc +=
x}` desugars to `{ var acc=init; for x <- a {..}; acc }`. The accumulator is a
real mutable **var**, ASSIGNED in the body (`acc = f(acc,x)` / `acc += x`), not
yielded as the tail value (that was the OLD form — it now warns "accumulator
never assigned"). Multiple accumulators: `fold a=0, b=1 for ..` (each its own
var; a tuple pattern also works). `break`/`continue`/`return` are legal in the
body (it is a plain `for`). Simultaneous tuple assignment `(a,b)=(b,a+b)` is a
language feature (parse-time desugar via a temp; the Fibonacci/swap idiom).
Named reduction sugars (`all`/`exists`/`count`/`find`/`filter`/`vector`,
spelled `name(for ...)`) are a SEPARATE construct, unchanged by the reform.
- Strings are UTF-32: `s.length()` counts chars, `s[i]` is a `char`,
`s[i:j]` a substring; `string([for c <- cs {c}])` builds from chars.
- Use `String.fx` / `Re.fx` instead of hand-rolling string processing.
Expand All @@ -103,9 +112,9 @@ intuition — read `doc/ficustut.md` and existing code. Verified traps:
- `break`/`continue`/`return` (bare or with a value) are legal as a
`match`-arm / `if`-branch value, exactly like `throw` (pseudo-type `TypErr`
unifies with valued siblings; lowering unchanged, cleanup runs). Legality
is positional-agnostic: they need an enclosing loop; rejected inside `fold`
and `@parallel` bodies; bare `return` in a non-void function is a
return-type mismatch.
is positional-agnostic: they need an enclosing loop; legal inside a `fold`
body (post-fold-1 it is a plain `for`), rejected inside `@parallel` bodies;
bare `return` in a non-void function is a return-type mismatch.
- Shift count must be `int`: write `x >> int(n)`.
- **Overload resolution: the least-generic viable candidate wins**, regardless
of declaration/import order. No unique winner at a fully-determined call =
Expand Down
4 changes: 2 additions & 2 deletions compiler/Ast.fx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fun loclist2loc(llist: loc_t list, default_loc: loc_t) =
val {m_idx=loci_m_idx,
line0=loci_line0, col0=loci_col0,
line1=loci_line1, col1=loci_col1} = loci
if m_idx != loci_m_idx {
loc = if m_idx != loci_m_idx {
if m_idx <= 0 { loci } else { loc }
} else {
loc_t
Expand Down Expand Up @@ -1021,7 +1021,7 @@ fun get_numeric_typ_size(t: typ_t, allow_tuples: bool): int =
else {
fold sz=0 for t<-tl {
val szj = get_numeric_typ_size(t, true)
if szj < 0 || sz < 0 {-1} else {sz + szj}
sz = if szj < 0 || sz < 0 {-1} else {sz + szj}
}
}
| _ => -1
Expand Down
276 changes: 166 additions & 110 deletions compiler/Ast_typecheck.fx

Large diffs are not rendered by default.

434 changes: 268 additions & 166 deletions compiler/C_gen_code.fx

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions compiler/C_gen_fdecls.fx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ fun convert_all_fdecls(cm_idx: int, top_code: kcode_t)
val {kci_arg} = kf_closure
val is_ccode_func = match kf_body { | KExpCCode _ => true | _ => false }
val ctor = kf_flags.fun_flag_ctor
val fold args = [] for arg@idx <- kf_params {
var args = []
for arg@idx <- kf_params {
val {kv_typ=t} = get_kval(arg, kf_loc)
val arg = if arg.m == 0 {dup_idc(cm_idx, arg)} else {arg}
val cname = if is_ccode_func { pp(arg) }
Expand All @@ -47,7 +48,7 @@ fun convert_all_fdecls(cm_idx: int, top_code: kcode_t)
(ctyp, [])
}
add_cf_arg(arg, ctyp, cname, kf_loc)
(arg, ctyp, arg_flags) :: args
args = (arg, ctyp, arg_flags) :: args
}
val {ktp_scalar=rt_scalar} = K_annotate.get_ktprops(rt, kf_loc)
val crt = C_gen_types.ktyp2ctyp(rt, kf_loc)
Expand Down Expand Up @@ -93,8 +94,8 @@ fun convert_all_fdecls(cm_idx: int, top_code: kcode_t)
val ctyp = C_gen_types.ktyp2ctyp(kt, kcv_loc)
val c_id = get_id(f"t{idx}")
val elem_exp = cexp_arrow(dst_exp, c_id, ctyp)
val free_ccode = C_gen_types.gen_free_code(elem_exp, ctyp, true, false, free_ccode, kcv_loc)
((c_id, ctyp) :: relems, free_ccode)
relems = (c_id, ctyp) :: relems
free_ccode = C_gen_types.gen_free_code(elem_exp, ctyp, true, false, free_ccode, kcv_loc)
}
val (free_f, decl_free_f) =
if free_ccode == [] {
Expand Down Expand Up @@ -305,8 +306,8 @@ fun convert_all_fdecls(cm_idx: int, top_code: kcode_t)
val {ct_props={ctp_free=(_, free_f)}} = *ct
val entry_ctyp = CTypName(get_id("fx_iface_entry_t"))
val entries_ctyp = CTypRawArray([::CTypStatic], entry_ctyp)
var fold init_ccode = [], ids = [], pairs = [], all_ids = empty_idset
for (iname, methods) <- kvar_ifaces {
var init_ccode = [], ids = [], pairs = [], all_ids = empty_idset
for (iname, methods) <- kvar_ifaces {
// FB-001: an explicit (void*) cast on each method pointer -- C
// implicitly converts a function pointer to void*, but C++ (the
// -c++ backend) rejects it in the `const void* vtbl[]` initializer.
Expand All @@ -316,16 +317,18 @@ fun convert_all_fdecls(cm_idx: int, top_code: kcode_t)
val vtbl_ctyp = CTypRawArray([::CTypStatic, CTypConst], std_CTypVoidPtr)
val vtbl_init_exp = CExpInit(mptrs, (vtbl_ctyp, kvar_loc))
val vtbl = gen_idc(cm_idx, "vtbl")
val (_, init_ccode) = create_cdefval(vtbl, vtbl_ctyp, default_tempval_flags(),
"", Some(vtbl_init_exp), init_ccode, kvar_loc)
(_, init_ccode) = create_cdefval(vtbl, vtbl_ctyp, default_tempval_flags(),
"", Some(vtbl_init_exp), init_ccode, kvar_loc)
val iface = match get_kinterface_opt(KTypName(iname), kvar_loc) {
| Some(iface) => iface
| _ => throw compile_err(kvar_loc,
"cgen: cannot find information about interface '{idk2str(iname, kvar_loc)}'")
}
val pair = CExpInit([::make_int_exp(0, kvar_loc),
make_id_exp(vtbl, kvar_loc)], (entry_ctyp, kvar_loc))
(init_ccode, iface->ki_id :: ids, pair :: pairs, all_ids.add(iface->ki_id))
ids = iface->ki_id :: ids
pairs = pair :: pairs
all_ids = all_ids.add(iface->ki_id)
}
// extend the set of pairs (interface_id, vtbl) to the parent interfaces
for (iname, _) <- kvar_ifaces, pair <- pairs.rev() {
Expand Down
55 changes: 27 additions & 28 deletions compiler/C_gen_types.fx
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,14 @@ fun convert_all_typs(kmods: kmodule_t list)
val {kt_typ, kt_loc} = *kt
match kt_typ {
| KTypTuple telems =>
val (free_code, copy_code, make_code, relems, make_args) =
fold free_code = [], copy_code = [], make_code = [],
relems = [], make_args = [] for kti@i <- telems {
val fold free_code = [], copy_code = [], make_code = [], relems = [], make_args = []
for kti@i <- telems {
val ni = get_id(f"t{i}")
val cti = ktyp2ctyp(kti, kt_loc)
val selem = CExpArrow(src_exp, ni, (cti, loc))
val delem = CExpArrow(dst_exp, ni, (cti, loc))
val free_code = gen_free_code(delem, cti, false, false, free_code, kt_loc)
val copy_code = gen_copy_code(selem, delem, cti, copy_code, kt_loc)
free_code = gen_free_code(delem, cti, false, false, free_code, kt_loc)
copy_code = gen_copy_code(selem, delem, cti, copy_code, kt_loc)
val {ktp_pass_by_ref} = K_annotate.get_ktprops(kti, kt_loc)
val selem2 = make_id_exp(ni, loc)
val (cti_arg, cti_arg_flags, selem2) =
Expand All @@ -483,9 +482,9 @@ fun convert_all_typs(kmods: kmodule_t list)
(cti, [], selem2)
}
val delem2 = CExpArrow(fx_result_exp, ni, (cti, loc))
val make_code = gen_copy_code(selem2, delem2, cti, make_code, kt_loc)
(free_code, copy_code, make_code, (ni, cti) :: relems,
(ni, cti_arg, cti_arg_flags) :: make_args)
make_code = gen_copy_code(selem2, delem2, cti, make_code, kt_loc)
relems = (ni, cti) :: relems
make_args = (ni, cti_arg, cti_arg_flags) :: make_args
}
val mktupl =
if ktp.ktp_complex {
Expand Down Expand Up @@ -514,14 +513,14 @@ fun convert_all_typs(kmods: kmodule_t list)
ct_typ=CTypStruct(Some(tn), relems.rev()),
ct_props=ct_props.{ctp_make=mktupl}
}
| KTypRecord (_, relems) =>
val fold free_code = [], copy_code = [], make_code = [],
relems = [], make_args = [] for (ni, kti) <- relems {
| KTypRecord (_, relems_) =>
val fold free_code = [], copy_code = [], make_code = [], relems = [], make_args = []
for (ni, kti) <- relems_ {
val cti = ktyp2ctyp(kti, loc)
val selem = CExpArrow(src_exp, ni, (cti, kt_loc))
val delem = CExpArrow(dst_exp, ni, (cti, kt_loc))
val free_code = gen_free_code(delem, cti, false, false, free_code, kt_loc)
val copy_code = gen_copy_code(selem, delem, cti, copy_code, kt_loc)
free_code = gen_free_code(delem, cti, false, false, free_code, kt_loc)
copy_code = gen_copy_code(selem, delem, cti, copy_code, kt_loc)
val {ktp_pass_by_ref} = K_annotate.get_ktprops(kti, kt_loc)
val arg_ni = get_id("r_" + pp(ni))
val selem2 = make_id_exp(arg_ni, loc)
Expand All @@ -532,9 +531,9 @@ fun convert_all_typs(kmods: kmodule_t list)
(cti, [], selem2)
}
val delem2 = CExpArrow(fx_result_exp, ni, (cti, loc))
val make_code = gen_copy_code(selem2, delem2, cti, make_code, kt_loc)
(free_code, copy_code, make_code, (ni, cti) :: relems,
(arg_ni, cti_arg, cti_arg_flags) :: make_args)
make_code = gen_copy_code(selem2, delem2, cti, make_code, kt_loc)
relems = (ni, cti) :: relems
make_args = (arg_ni, cti_arg, cti_arg_flags) :: make_args
}

val mkrecl = if ktp.ktp_complex {
Expand Down Expand Up @@ -706,25 +705,26 @@ fun convert_all_typs(kmods: kmodule_t list)
for (ni, kt)@i <- kvar_cases {
val label_i = i+1
match kt {
| KTypVoid => (free_cases, copy_cases, uelems)
| KTypVoid => {}
| _ =>
val ti = ktyp2ctyp(kt, loc)
val ni_clean = get_orig_id(ni)
val selem_i = CExpMem(src_u_exp, ni_clean, (ti, kvar_loc))
val delem_i = CExpMem(dst_u_exp, ni_clean, (ti, kvar_loc))
val switch_label_i_exps = [:: make_int_exp(label_i, kvar_loc) ]
val free_code_i = gen_free_code(delem_i, ti, false, false, [], kvar_loc)
val free_cases= match free_code_i {
| [] => free_cases
| _ => (switch_label_i_exps, free_code_i) :: free_cases
}
free_cases =
match free_code_i {
| [] => free_cases
| _ => (switch_label_i_exps, free_code_i) :: free_cases
}
val copy_code_i = gen_copy_code(selem_i, delem_i, ti, [], kvar_loc)
val copy_cases =
copy_cases =
match copy_code_i {
| [:: CExp(CExpBinary (COpAssign, _, _, _))] => copy_cases
| _ => (switch_label_i_exps, copy_code_i) :: copy_cases
}
(free_cases, copy_cases, (ni_clean, ti) :: uelems)
uelems = (ni_clean, ti) :: uelems
}
}
val free_code =
Expand Down Expand Up @@ -1002,10 +1002,9 @@ fun elim_unused_ctypes(mname: id_t, all_ctypes_fwd_decl: cstmt_t list,
}

update_used_ids(used_ids)
val fold ctypes_ccode = []
for s <- all_ctypes_fwd_decl + (all_ctypes_decl + all_ctypes_fun_decl) {
if is_used_decl(s, used_ids, true) { s :: ctypes_ccode }
else { ctypes_ccode }
}
var ctypes_ccode = []
for s <- all_ctypes_fwd_decl + (all_ctypes_decl + all_ctypes_fun_decl) {
if is_used_decl(s, used_ids, true) { ctypes_ccode = s :: ctypes_ccode }
}
ctypes_ccode.rev()
}
47 changes: 23 additions & 24 deletions compiler/Compiler.fx
Original file line number Diff line number Diff line change
Expand Up @@ -116,35 +116,33 @@ fun clrmsg(clr: msgcolor_t, msg: string)
val error = clrmsg(MsgRed, "error")

fun get_preamble(mfname: string): Lexer.token_t list {
val preamble =
var preamble: Lexer.token_t list = []
if Options.opt.use_preamble {
val bare_name = Filename.remove_extension(Filename.basename(mfname))
val (preamble, _) = fold (preamble, found) = (([] : Lexer.token_t list), false)
for (mname, from_import) <- [:: ("Builtins", true), ("Math", true),
("Complex", true),
("Array", true), ("List", false),
("Vector", false), ("Char", false),
("String", false),
] {
if found {
(preamble, found)
} else if bare_name == mname {
(preamble, true)
} else if from_import {
(preamble + [:: Lexer.FROM, Lexer.IDENT(true, mname), Lexer.IMPORT(false), Lexer.STAR(true), Lexer.SEMICOLON], false)
} else {
(preamble + [:: Lexer.IMPORT(true), Lexer.IDENT(true, mname), Lexer.SEMICOLON], false)
for (mname, from_import) <- [:: ("Builtins", true), ("Math", true),
("Complex", true),
("Array", true), ("List", false),
("Vector", false), ("Char", false),
("String", false),] {
if bare_name == mname {
break
}
if from_import {
preamble += [:: Lexer.FROM, Lexer.IDENT(true, mname), Lexer.IMPORT(false),
Lexer.STAR(true), Lexer.SEMICOLON]
}
else {
preamble += [:: Lexer.IMPORT(true), Lexer.IDENT(true, mname), Lexer.SEMICOLON]
}
}
preamble
} else { [] }
}
fold p=preamble for (n, v) <- Options.opt.defines {
val v = match v {
| Options.OptBool(b) => Ast.LitBool(b)
| Options.OptInt(i) => Ast.LitInt(int64(i))
| Options.OptString(s) => Ast.LitString(s)
}
Lexer.PP_DEFINE :: Lexer.IDENT(true, n) :: Lexer.LITERAL(v) :: p
p = Lexer.PP_DEFINE :: Lexer.IDENT(true, n) :: Lexer.LITERAL(v) :: p
}
}

Expand Down Expand Up @@ -616,10 +614,11 @@ fun run_cc(cmods: C_form.cmodule_t list, ficus_root: string) {
(is_cpp, recompiled, clibs, ok_j, obj_filename)
}]

val fold (any_cpp, any_recompiled, all_clibs, ok, objs) = (false, false, ([] : string list), ok, [])
for (is_cpp, is_recompiled, clibs_j, ok_j, obj) <- results {
(any_cpp | is_cpp, any_recompiled | is_recompiled, clibs_j + all_clibs, ok & ok_j, obj :: objs)
}
var any_cpp = false, any_recompiled = false, all_clibs = ([] : string list), ok = ok, objs = []
for (is_cpp, is_recompiled, clibs_j, ok_j, obj) <- results {
any_cpp |= is_cpp; any_recompiled |= is_recompiled
all_clibs = clibs_j + all_clibs; ok &= ok_j; objs = obj :: objs
}
if ok && !any_recompiled && Filename.exists(Options.opt.app_filename) {
pr_verbose(f"{Options.opt.app_filename} is up-to-date\n")
ok
Expand Down Expand Up @@ -673,7 +672,7 @@ fun print_all_compile_errs()
| _ => ""
}
val key = match msg.find('\n') { | (-1) => msg | nl => msg[:nl] }
if key != "" && seen.mem(key) { acc }
acc = if key != "" && seen.mem(key) { acc }
else { if key != "" { seen.add(key) }; e :: acc }
}
fun errkey(e: exn): (int, int, int) = match e {
Expand Down
15 changes: 9 additions & 6 deletions compiler/K_annotate.fx
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ fun get_typ_deps(n: id_t, loc: loc_t): idset_t
| KTypCPointer | KTypExn | KTypErr | KTypModule => deps
| KTypRawPointer(et) => get_ktyp_deps_(et, deps)
| KTypFun (args, rt) =>
fold deps = deps for t <- rt :: args { get_ktyp_deps_(t, deps) }
fold deps = deps for t <- rt :: args { deps = get_ktyp_deps_(t, deps) }
| KTypTuple tl =>
fold deps = deps for t <- tl { get_ktyp_deps_(t, deps) }
fold deps = deps for t <- tl { deps = get_ktyp_deps_(t, deps) }
| KTypRecord (rn, relems) =>
fold deps = deps for (_, ti) <- relems { get_ktyp_deps_(ti, deps) }
fold deps = deps for (_, ti) <- relems { deps = get_ktyp_deps_(ti, deps) }
| KTypName i => deps.add(i)
| KTypArray (_, et) => get_ktyp_deps_(et, deps)
| KTypList et => get_ktyp_deps_(et, deps)
Expand All @@ -44,11 +44,14 @@ fun get_typ_deps(n: id_t, loc: loc_t): idset_t

match kinfo_(n, loc) {
| KVariant (ref {kvar_cases, kvar_ifaces}) =>
val fold deps = empty_idset for (_, ti) <- kvar_cases { get_ktyp_deps_(ti, deps) }
fold deps = deps for (iname, _) <- kvar_ifaces { deps.add(iname) }
var deps = empty_idset
for (_, ti) <- kvar_cases { deps = get_ktyp_deps_(ti, deps) }
for (iname, _) <- kvar_ifaces { deps = deps.add(iname) }
deps
| KTyp (ref {kt_typ}) => get_ktyp_deps_(kt_typ, empty_idset)
| KInterface (ref {ki_base, ki_all_methods}) =>
val fold deps = empty_idset for (_, ti) <- ki_all_methods { get_ktyp_deps_(ti, deps) }
var deps = empty_idset
for (_, ti) <- ki_all_methods { deps = get_ktyp_deps_(ti, deps) }
if ki_base == noid {deps} else {deps.add(ki_base)}
| _ => throw compile_err(loc, f"the symbol '{idk2str(n, loc)}' is not a type")
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/K_cfold_dealias.fx
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,17 @@ fun cfold_dealias(kmods: kmodule_t list)
| _ => a :: res_al
}

val fold res_al = [] for a <- al {
var res_al = []
for a <- al {
res_al = match a {
| AtomId n =>
match concat_map.find_opt(n) {
| Some(a :: rest) => rest.rev() + try_cfold_str_concat(a, res_al)
| _ => a :: res_al
}
| _ => try_cfold_str_concat(a, res_al)
}
}

match res_al {
| ([:: a]) when (match get_atom_ktyp(a, loc) { | KTypString => true | _ => false }) =>
Expand Down
Loading
Loading