Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bcfe80e
dart3: init a empty dart3 project
Krysl Apr 30, 2026
7a92b50
dart3: step0
Krysl Apr 30, 2026
7c11d55
dart3: (wip) step 1.1
Krysl May 1, 2026
559bd8f
dart3: step1 passed
Krysl May 1, 2026
945f254
dart3: add nil/bool/:keyword support
Krysl May 1, 2026
4e0bfef
dart3: step2 passed
Krysl May 1, 2026
5652fdb
dart3: step3 passed
Krysl May 1, 2026
599d797
dart3: all test [0~4] passed
Krysl May 3, 2026
dc6b30b
dart3: refactor
Krysl May 3, 2026
420132d
dart3: step5 stash before refactor
Krysl May 4, 2026
40f1d46
dart3: step5 passed
Krysl May 4, 2026
2d73bab
dart3: all step[0~5] passed
Krysl May 5, 2026
932445b
dart3: clean code
Krysl May 5, 2026
192f102
dart3: Define 'not' function using mal itself
Krysl May 5, 2026
ff147d5
dart3: strict closure function type
Krysl May 5, 2026
48a927e
dart3: fix problems when run `make REGRESS=1 "test^dart3^step5"`
Krysl May 6, 2026
e7d4700
dart3: step6 passed
Krysl May 6, 2026
dc3ab4c
dart3: clean code
Krysl May 6, 2026
97bfb55
dart3: step7 passed
Krysl May 6, 2026
c2ed6a5
dart3: step8 passed
Krysl May 6, 2026
97ec4f9
dart3: all step[0~9] passed
Krysl May 7, 2026
2fbb4d3
dart3: all mal test passed
Krysl May 7, 2026
09f83d7
dart3: fix `(keyword :abc)` test
Krysl May 7, 2026
4d066ee
dart3: all docker test passed
Krysl May 8, 2026
1a5f4d7
dart3: update readme/IMPLS.yml
Krysl May 8, 2026
f84d977
dart3: fix ci
Krysl May 8, 2026
2903e3b
dart3: impls missing hashCode function, and a few other code optimize
Krysl May 8, 2026
3032ccb
dart3: enable stricter type checks & code optimization
Krysl May 9, 2026
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
1 change: 1 addition & 0 deletions IMPLS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ IMPL:
- {IMPL: d, d_MODE: ldc2}
- {IMPL: d, d_MODE: dmd}
- {IMPL: dart}
- {IMPL: dart3}
- {IMPL: elisp}
- {IMPL: elixir}
- {IMPL: elm}
Expand Down
12 changes: 9 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ OPTIONAL=1
# Run target/rule within docker image for the implementation
DOCKERIZE =

# On Windows Git/MSYS shells, disable automatic path conversion for docker args.
MSYS_DOCKER_ENV = $(if $(filter Windows_NT,$(OS)),MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*',)


#
# General settings and utility functions
Expand Down Expand Up @@ -136,6 +139,7 @@ actual_impl = $(if $(filter mal,$(1)),$(patsubst %-mal,%,$(MAL_IMPL)),$(1))
# for this impl
get_build_command = $(strip $(foreach mode,$(1)_MODE, \
$(if $(strip $(DOCKERIZE)),\
$(MSYS_DOCKER_ENV) \
docker run \
-it --rm -u $(shell id -u) \
-v $(dir $(abspath $(lastword $(MAKEFILE_LIST)))):/mal \
Expand All @@ -152,6 +156,7 @@ get_build_command = $(strip $(foreach mode,$(1)_MODE, \
# necessary to launch the given impl and step
get_run_prefix = $(strip $(foreach mode,$(call actual_impl,$(1))_MODE, \
$(if $(strip $(DOCKERIZE) $(4)),\
$(MSYS_DOCKER_ENV) \
docker run -e STEP=$($2) -e MAL_IMPL=$(MAL_IMPL) \
-it --rm -u $(shell id -u) \
-v $(dir $(abspath $(lastword $(MAKEFILE_LIST)))):/mal \
Expand Down Expand Up @@ -215,9 +220,10 @@ ALL_REPL = $(strip $(sort \
.PHONY: $(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),$(call $(i)_STEP_TO_PROG,$(s))))
$(foreach i,$(DO_IMPLS),$(foreach s,$(STEPS),$(call $(i)_STEP_TO_PROG,$(s)))):
$(foreach impl,$(word 2,$(subst /, ,$(@))),\
$(if $(DOCKERIZE), \
$(call get_build_command,$(impl)) $(patsubst impls/$(impl)/%,%,$(@)), \
$(call get_build_command,$(impl)) $(subst impls/$(impl)/,,$(@))))
$(if $(filter-out mal,$(impl)),\
$(if $(DOCKERIZE), \
$(call get_build_command,$(call actual_impl,$(impl))) $(patsubst impls/$(impl)/%,%,$(@)), \
$(call get_build_command,$(impl)) $(subst impls/$(impl)/,,$(@)))))

# Allow IMPL, build^IMPL, IMPL^STEP, and build^IMPL^STEP
$(DO_IMPLS): $$(foreach s,$$(STEPS),$$(call $$(@)_STEP_TO_PROG,$$(s)))
Expand Down
4 changes: 3 additions & 1 deletion Makefile.impls
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ wasm_MODE = wasmtime
# Implementation specific settings
#

IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart \
IMPLS = ada ada.2 awk bash basic bbc-basic c c.2 chuck clojure coffee common-lisp cpp crystal cs d dart dart3 \
elisp elixir elm erlang es6 factor fantom fennel forth fsharp go groovy gnu-smalltalk \
guile hare haskell haxe hy io janet java java-truffle js jq julia kotlin latex3 livescript logo lua make mal \
matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \
Expand Down Expand Up @@ -61,6 +61,7 @@ dist_EXCLUDES += guile io julia matlab swift

# Extra options to pass to runtest.py
bbc-basic_TEST_OPTS = --test-timeout 60
dart3_TEST_OPTS = --no-pty
guile_TEST_OPTS = --test-timeout 120
io_TEST_OPTS = --test-timeout 120
java-truffle_TEST_OPTS = --start-timeout 30
Expand Down Expand Up @@ -123,6 +124,7 @@ crystal_STEP_TO_PROG = impls/crystal/$($(1))
cs_STEP_TO_PROG = impls/cs/$($(1)).exe
d_STEP_TO_PROG = impls/d/$($(1))
dart_STEP_TO_PROG = impls/dart/$($(1)).dart
dart3_STEP_TO_PROG = impls/dart3/bin/$($(1)).dart
elisp_STEP_TO_PROG = impls/elisp/$($(1)).el
elixir_STEP_TO_PROG = impls/elixir/lib/mix/tasks/$($(1)).ex
elm_STEP_TO_PROG = impls/elm/$($(1)).js
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ process guide](process/guide.md) there is also a [mal/make-a-lisp
FAQ](docs/FAQ.md) where I attempt to answer some common questions.


**3. Mal is implemented in 89 languages (95 different implementations and 118 runtime modes)**
**3. Mal is implemented in 89 languages (96 different implementations and 118 runtime modes)**

| Language | Creator |
| -------- | ------- |
Expand All @@ -63,6 +63,7 @@ FAQ](docs/FAQ.md) where I attempt to answer some common questions.
| [Crystal](#crystal) | [Linda_pp](https://github.com/rhysd) |
| [D](#d) | [Dov Murik](https://github.com/dubek) |
| [Dart](#dart) | [Harry Terkelsen](https://github.com/hterkelsen) |
| [Dart 3](#dart3) | [Krysl](https://github.com/krysl) |
| [Elixir](#elixir) | [Martin Ek](https://github.com/ekmartin) |
| [Elm](#elm) | [Jos van Bakel](https://github.com/c0deaddict) |
| [Emacs Lisp](#emacs-lisp) | [Vasilij Schneidermann](https://github.com/wasamasa) |
Expand Down Expand Up @@ -420,6 +421,15 @@ cd impls/dart
dart ./stepX_YYY
```

### Dart3

The Dart 3 implementation has been tested with Dart 3.11.4.

```
cd impls/dart3
dart ./bin/stepX_YYY
```

### Emacs Lisp

The Emacs Lisp implementation of mal has been tested with Emacs 24.3
Expand Down
4 changes: 4 additions & 0 deletions impls/dart3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
test/generated/
3 changes: 3 additions & 0 deletions impls/dart3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 1.0.0

- Initial version.
46 changes: 46 additions & 0 deletions impls/dart3/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
FROM ubuntu:24.04
LABEL maintainer="Krysl <krysl@qq.com>"
LABEL org.opencontainers.image.source=https://github.com/kanaka/mal
LABEL org.opencontainers.image.description="mal test container: dart3"

##########################################################
# General requirements for testing or common across many
# implementations
##########################################################

RUN apt-get -y update

# Required for running tests
RUN apt-get -y install make python3
RUN ln -sf /usr/bin/python3 /usr/bin/python

# Some typical implementation and test requirements
RUN apt-get -y install curl libreadline-dev libedit-dev

##########################################################
# Specific implementation requirements
##########################################################

RUN apt-get -y install apt-transport-https wget gnupg
RUN wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub \
| gpg --dearmor -o /usr/share/keyrings/dart.gpg
RUN echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' \
| tee /etc/apt/sources.list.d/dart_stable.list
RUN apt-get -y update
# Pin to a stable 3.11.x release to satisfy package language version requirements.
RUN apt-get -y install dart=3.11.4-1

# Run dart with writable home/cache for arbitrary uid in docker run (-u <uid>).
ENV HOME=/tmp
ENV PUB_CACHE=/opt/dart-pub-cache
ENV IN_DOCKER=true
RUN mkdir -p /opt/dart-pub-cache /tmp/dart3-deps && chmod -R 777 /opt/dart-pub-cache /tmp/dart3-deps

# Pre-fetch dependencies into image cache so tests do not need to run pub get each time.
COPY pubspec.yaml /tmp/dart3-deps/
COPY pubspec.lock /tmp/dart3-deps/
RUN cd /tmp/dart3-deps && dart pub get
RUN chmod -R 777 /opt/dart-pub-cache

RUN mkdir -p /mal
WORKDIR /mal
5 changes: 5 additions & 0 deletions impls/dart3/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
all:
@true


clean:
2 changes: 2 additions & 0 deletions impls/dart3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
A sample command-line application with an entrypoint in `bin/`, library code
in `lib/`, and example unit test in `test/`.
34 changes: 34 additions & 0 deletions impls/dart3/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.

include: package:lints/recommended.yaml

# Uncomment the following section to specify additional rules.

linter:
rules:
- avoid_print

analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
# exclude:
# - path/to/excluded/files/**

# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints

# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options
13 changes: 13 additions & 0 deletions impls/dart3/bin/pub_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'dart:io';

import 'package:args/args.dart';

void main(List<String> args) {
final parser = ArgParser()..addOption('print', abbr: 'p');
final ret = parser.parse(args);
final p = ret.option('print');
if (p != null) {
stdout.writeln(p);
}
return;
}
21 changes: 21 additions & 0 deletions impls/dart3/bin/step0_repl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'dart:io';

String read(String str) => str;

String eval(String str) => str;

String print(String str) => str;

String rep(String str) => print(eval(read(str)));

void main(List<String> args) {
while (true) {
stdout.write('user> ');
if (!stdin.hasTerminal) stdout.write('\n');

final input = stdin.readLineSync();
if (input == null) break;
final output = rep(input);
stdout.writeln(output);
}
}
23 changes: 23 additions & 0 deletions impls/dart3/bin/step1_read_print.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import 'dart:io';

import 'package:mal/mal.dart';

MalAny read(String str) => readStr(str);
MalAny eval(MalAny val) => val;
String print(MalAny str) => prStr(str, true);
String rep(String str) => print(eval(read(str)));

void main(List<String> args) {
while (true) {
stdout.write('user> '.toBlue);
if (!stdin.hasTerminal) stdout.write('\n');
final input = stdin.readLineSync();
if (input == null) break;
try {
final output = rep(input);
stdout.writeln(output);
} on ParserError catch (e) {
stdout.writeln(e.toString());
}
}
}
52 changes: 52 additions & 0 deletions impls/dart3/bin/step2_eval.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:io';

import 'package:mal/mal.dart';

MalAny read(String str) => readStr(str);
MalAny eval(MalAny ast, Env env) {
if (env.debugEval) {
stdout.writeln('${'EVAL:'.toCyan} ${prStr(ast, true)}');
}

MalAny listCall(MalList list, Env env, MalList ast) {
if (list.isNotEmpty) {
var fn = eval(list.first, env);
if (fn is MalFunction) {
return fn.call(list.args.map((e) => eval(e, env)).toList(), env);
}
throw NotCallableError('${fn.toStr()} is not callable');
} else {
return ast;
}
}

return switch (ast) {
final MalSymbol symbol => env.getSymbolVal(symbol),
MalVector(list: final list) => MalVector(
list.map((e) => eval(e, env)).toList(),
),
final MalMap map => MalMap(map.map((k, v) => MapEntry(k, eval(v, env)))),
final MalList list => listCall(list, env, ast),
_ => ast,
};
}

String print(MalAny str) => prStr(str, true);

final replEnv = Env(data: ns);
String rep(String str) => print(eval(read(str), replEnv));

void main(List<String> args) {
while (true) {
stdout.write('user> '.toBlue);
if (!stdin.hasTerminal) stdout.write('\n');
final input = stdin.readLineSync();
if (input == null) break;
try {
final output = rep(input);
stdout.writeln(output);
} on ParserError catch (e) {
stdout.writeln(e.toString());
}
}
}
77 changes: 77 additions & 0 deletions impls/dart3/bin/step3_env.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'dart:io';

import 'package:collection/collection.dart';
import 'package:mal/mal.dart';

MalAny read(String str) => readStr(str);
MalAny eval(MalAny ast, Env env) {
if (env.debugEval) {
stdout.writeln('${'EVAL:'.toCyan} ${prStr(ast, true)}');
}

MalAny listCall(MalList list, Env env, MalList ast) {
if (list.isNotEmpty) {
var fn = eval(list.first, env);
if (fn is MalFunction) {
return fn.call(list.args.map((e) => eval(e, env)).toList(), env);
} else if (fn is MalMacroFunction) {
return fn.callWithoutTCO(list.args, env);
} else if (fn is MalSymbolNotFound) {
throw fn.makeError();
}
throw NotCallableError('${fn.toStr()} is not callable');
} else {
return ast;
}
}

return switch (ast) {
final MalSymbol symbol => env.getSymbolVal(symbol),
MalVector(list: final list) => MalVector(
list.map((e) => eval(e, env)).toList(),
),
final MalMap map => MalMap(map.map((k, v) => MapEntry(k, eval(v, env)))),
final MalList list => listCall(list, env, ast),
_ => ast,
};
}

String print(MalAny str) => prStr(str, true);

final replEnv = Env(
data: {
'def!': MalMacroFunction.normal(
'def!',
(List<MalAny> args, Env env) =>
env[(args[0] as MalSymbol).name] = eval(args[1], env),
),
'let*': MalMacroFunction.normal('let*', (List<MalAny> args, Env env) {
final newEnv = Env(outer: env);
MalListBase first = args.first.asMalListBase(
errMsg: 'unsupported ${args.first.runtimeType} as Let* \'s first arg',
);

for (final [key, val] in first.slices(2)) {
newEnv[(key as MalSymbol).name] = eval(val, newEnv);
}
return eval(args[1], newEnv);
}),
...ns,
},
);
String rep(String str) => print(eval(read(str), replEnv));

void main(List<String> args) {
while (true) {
stdout.write('user> '.toBlue);
if (!stdin.hasTerminal) stdout.write('\n');
final input = stdin.readLineSync();
if (input == null) break;
try {
final output = rep(input);
stdout.writeln(output);
} on ParserError catch (e) {
stdout.writeln(e.toString());
}
}
}
Loading
Loading