mirror of
https://github.com/kanaka/mal.git
synced 2024-09-21 02:27:10 +03:00
.. | ||
core.adb | ||
core.ads | ||
Dockerfile | ||
envs.adb | ||
envs.ads | ||
err.adb | ||
err.ads | ||
eval_cb.ads | ||
Makefile | ||
printer.adb | ||
printer.ads | ||
reader.adb | ||
reader.ads | ||
readline.adb | ||
readline.ads | ||
README | ||
run | ||
step0_repl.adb | ||
step1_read_print.adb | ||
step2_eval.adb | ||
step3_env.adb | ||
step4_if_fn_do.adb | ||
step5_tco.adb | ||
step6_file.adb | ||
step7_quote.adb | ||
step8_macros.adb | ||
step9_try.adb | ||
stepa_mal.adb | ||
types-atoms.adb | ||
types-atoms.ads | ||
types-builtins.adb | ||
types-builtins.ads | ||
types-fns.adb | ||
types-fns.ads | ||
types-mal.adb | ||
types-mal.ads | ||
types-maps.adb | ||
types-maps.ads | ||
types-sequences.adb | ||
types-sequences.ads | ||
types-symbols-names.ads | ||
types-symbols.adb | ||
types-symbols.ads | ||
types.ads |
Comparison with the first Ada implementation. -- The first implementation was deliberately compatible with all Ada compilers, while this one illustrates various Ada 2020 features: assertions, preconditions, invariants, initial assignment for limited types, limited imports, indexing aspects... The variant MAL type is implemented with a discriminant instead of object-style dispatching. This allows more static and dynamic checks, but also two crucial performance improvements: * Nil, boolean, integers and pointers to built-in functions are passed by value without dynamic allocation. * Lists are implemented as C-style arrays, and most of them can be allocated on the stack. Once each component has an explicit interface, various optimizations have been added: unique allocation of symbols, stack-style allocation of environments in the current execution path, reuse of allocated memory when the reference count reaches 1... The eventual performances compete with C-style languages, allthough all user input is checked (implicit language-defined checks like array bounds and discriminant consistency are only enabled during tests). There are also similarities with the first implementation. For example, both rely on user-defined finalization to count references in recursive structures instead of a posteriori garbage collection. Notes for contributors that do not fit in a specific package. -- * All packages can call Eval back via a reference in the Eval_Cb package, set during startup. I am interested in a prettier solution ensuring a valid value during elaboration. Note that generic packages cannot export access values. * All wrapped pointers are non null, new variables must be assigned immediately. This is usually enforced by a hidden discriminant, but here we want the type to become a field inside Types.Mal.T. So the check happens at run time with a private invariant. * The finalize procedure may be called twice, so it does nothing when the reference count is zero, meaning that we are reaching Finalize recursively. * In implementations, a consistent object (that will be deallocated automatically) must be built before any exception is raised by user code (for example the 'map' built-in function may run user code). * Each module encapsulating dynamic allocation counts allocations and deallocations. With debugging options, a failure is reported if - too many deallocation happen (via a numeric range check) - all storage is not freed (via a dedicated call from the step file) Known bugs -- The third step of the perf^ada2 target fails during the final storage deallocation when the executable is built with -gnatp. I have failed to understand why so far. Debugging -- Uncaught exceptions are reported with an execution trace (excluding TCO cycles). This has become possible in step9, but has been backported to former steps as this is really handy for debugging. Some environment variables increase verbosity. # dbg_reader= ./stepAmal trace reader recursion # dbgeval= ./stepAmal eval recursion (or TCO) # dbgenv0= ./stepAmal eval recursion and environments contents # dbgenv1= ./stepAmal eval recursion and environment internals