2016-11-14 01:21:44 +03:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'printer.dart';
|
|
|
|
import 'reader.dart' as reader;
|
|
|
|
import 'types.dart';
|
|
|
|
|
2022-01-10 02:15:40 +03:00
|
|
|
Map<String, MalBuiltin> ns = <String, MalBuiltin>{
|
|
|
|
'+': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalInt(a.value + b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'-': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalInt(a.value - b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'*': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalInt(a.value * b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'/': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalInt(a.value ~/ b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'list':
|
2016-11-14 01:21:44 +03:00
|
|
|
new MalBuiltin((List<MalType> args) => new MalList(args.toList())),
|
2022-01-10 02:15:40 +03:00
|
|
|
'list?': new MalBuiltin(
|
2016-11-14 01:21:44 +03:00
|
|
|
(List<MalType> args) => new MalBool(args.single is MalList)),
|
2022-01-10 02:15:40 +03:00
|
|
|
'empty?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args.single as MalIterable;
|
|
|
|
return new MalBool(a.elements.isEmpty);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'count': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args.first as MalIterable;
|
|
|
|
return new MalInt(a.elements.length);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'=': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0];
|
|
|
|
var b = args[1];
|
|
|
|
return new MalBool(a == b);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'<': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalBool(a.value < b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'<=': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalBool(a.value <= b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'>': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalBool(a.value > b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'>=': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var a = args[0] as MalInt;
|
|
|
|
var b = args[1] as MalInt;
|
|
|
|
return new MalBool(a.value >= b.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'pr-str': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalString(
|
|
|
|
args.map((a) => pr_str(a, print_readably: true)).join(' '));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'str': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalString(
|
|
|
|
args.map((a) => pr_str(a, print_readably: false)).join());
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'prn': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
print(args.map((a) => pr_str(a, print_readably: true)).join(' '));
|
|
|
|
return new MalNil();
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'println': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
print(args.map((a) => pr_str(a, print_readably: false)).join(' '));
|
|
|
|
return new MalNil();
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'read-string': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var code = args.single as MalString;
|
|
|
|
return reader.read_str(code.value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'slurp': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var fileName = args.single as MalString;
|
|
|
|
var file = new File(fileName.value);
|
|
|
|
return new MalString(file.readAsStringSync());
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'atom': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var value = args.single;
|
|
|
|
return new MalAtom(value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'atom?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var value = args.single;
|
|
|
|
return new MalBool(value is MalAtom);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'deref': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var atom = args.single as MalAtom;
|
|
|
|
return atom.value;
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'reset!': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var atom = args[0] as MalAtom;
|
|
|
|
var newValue = args[1];
|
|
|
|
atom.value = newValue;
|
|
|
|
return newValue;
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'swap!': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var atom = args[0] as MalAtom;
|
|
|
|
var func = args[1] as MalCallable;
|
|
|
|
var fnArgs = [atom.value]..addAll(args.sublist(2));
|
|
|
|
var result = func.call(fnArgs);
|
|
|
|
atom.value = result;
|
|
|
|
return result;
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'cons': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var x = args[0];
|
|
|
|
var xs = args[1] as MalIterable;
|
|
|
|
return new MalList([x]..addAll(xs));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'concat': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var results = <MalType>[];
|
|
|
|
for (MalIterable element in args) {
|
|
|
|
results.addAll(element);
|
|
|
|
}
|
|
|
|
return new MalList(results);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'vec': new MalBuiltin((List<MalType> args) {
|
2020-07-21 19:01:48 +03:00
|
|
|
if (args.length == 1) {
|
|
|
|
if (args[0] is MalVector) return args[0];
|
2022-01-10 02:15:40 +03:00
|
|
|
if (args[0] is MalList) return new MalVector((args[0] as MalList).elements);
|
2020-07-21 19:01:48 +03:00
|
|
|
}
|
|
|
|
throw new MalException(new MalString("vec: wrong arguments"));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'nth': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var indexable = args[0] as MalIterable;
|
|
|
|
var index = args[1] as MalInt;
|
|
|
|
try {
|
|
|
|
return indexable[index.value];
|
|
|
|
} on RangeError catch (e) {
|
Test uncaught throw, catchless try* . Fix 46 impls.
Fixes made to: ada, c, chuck, clojure, coffee, common-lisp, cpp,
crystal, d, dart, elm, erlang, es6, factor, fsharp, gnu-smalltalk,
groovy, guile, haxe, hy, js, livescript, matlab, miniMAL, nasm, nim,
objc, objpascal, ocaml, perl, perl6, php, plsql, ps, python, r,
rpython, ruby, scheme, swift3, tcl, ts, vb, vimscript, wasm, yorick.
Catchless try* test is an optional test. Not all implementations
support catchless try* but a number were fixed so they at least don't
crash on catchless try*.
2018-12-03 22:20:44 +03:00
|
|
|
throw new MalException(new MalString(e.toString()));
|
2016-11-14 01:21:44 +03:00
|
|
|
}
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'first': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var list = args.first as MalIterable;
|
|
|
|
if (list.isEmpty) return new MalNil();
|
|
|
|
return list.first;
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'rest': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var list = args.first as MalIterable;
|
|
|
|
if (list.isEmpty) return new MalList(<MalType>[]);
|
|
|
|
return new MalList(list.sublist(1));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'throw': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
throw new MalException(args.first);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'nil?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalNil);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'true?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalBool && (args.first as MalBool).value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'false?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalBool && !(args.first as MalBool).value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'symbol': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalSymbol((args.first as MalString).value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'symbol?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalSymbol);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'keyword': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
if (args.first is MalKeyword) return args.first;
|
|
|
|
return new MalKeyword((args.first as MalString).value);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'keyword?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalKeyword);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'number?': new MalBuiltin((List<MalType> args) {
|
2017-10-26 06:38:09 +03:00
|
|
|
return new MalBool(args.first is MalInt);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'fn?': new MalBuiltin((List<MalType> args) {
|
2017-10-26 06:38:09 +03:00
|
|
|
return new MalBool(args.first is MalCallable && !(args.first.isMacro));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'macro?': new MalBuiltin((List<MalType> args) {
|
2017-10-26 06:38:09 +03:00
|
|
|
return new MalBool(args.first is MalCallable && args.first.isMacro);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'vector': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalVector(args);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'vector?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalVector);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'hash-map': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalHashMap.fromSequence(args);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'map?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalHashMap);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'assoc': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var map = args.first as MalHashMap;
|
|
|
|
var assoc = new MalHashMap.fromSequence(args.skip(1).toList());
|
|
|
|
var newMap = new Map<MalType, MalType>.from(map.value);
|
|
|
|
newMap.addAll(assoc.value);
|
|
|
|
return new MalHashMap(newMap);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'dissoc': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var map = args.first as MalHashMap;
|
|
|
|
var newMap = new Map<MalType, MalType>.from(map.value);
|
|
|
|
for (var key in args.skip(1)) {
|
|
|
|
newMap.remove(key);
|
|
|
|
}
|
|
|
|
return new MalHashMap(newMap);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'get': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
if (args[0] is MalNil) return new MalNil();
|
|
|
|
var map = args[0] as MalHashMap;
|
|
|
|
var key = args[1];
|
|
|
|
return map.value[key] ?? new MalNil();
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'contains?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var map = args[0] as MalHashMap;
|
|
|
|
var key = args[1];
|
|
|
|
return new MalBool(map.value.containsKey(key));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'keys': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalList((args.first as MalHashMap).value.keys.toList());
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'vals': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalList((args.first as MalHashMap).value.values.toList());
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'sequential?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalList || args.first is MalVector);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'readline': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var message = args.first as MalString;
|
|
|
|
stdout.write(message.value);
|
|
|
|
var input = stdin.readLineSync();
|
|
|
|
if (input == null) return new MalNil();
|
|
|
|
return new MalString(input);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'time-ms': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
assert(args.isEmpty);
|
|
|
|
return new MalInt(new DateTime.now().millisecondsSinceEpoch);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'conj': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var collection = args.first;
|
|
|
|
var elements = args.sublist(1);
|
|
|
|
if (collection is MalList) {
|
|
|
|
return new MalList(
|
|
|
|
elements.reversed.toList()..addAll(collection.elements));
|
|
|
|
}
|
|
|
|
if (collection is MalVector) {
|
|
|
|
return new MalVector(collection.elements.toList()..addAll(elements));
|
|
|
|
}
|
|
|
|
throw new MalException(new MalString('"conj" takes a list or vector'));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'string?': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
return new MalBool(args.first is MalString);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'seq': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var arg = args.first;
|
|
|
|
if (arg is MalIterable && arg.isEmpty) return new MalNil();
|
|
|
|
if (arg is MalString && arg.value.isEmpty) return new MalNil();
|
|
|
|
|
|
|
|
if (arg is MalNil || arg is MalList) return arg;
|
|
|
|
if (arg is MalVector) return new MalList(arg.elements.toList());
|
|
|
|
if (arg is MalString) {
|
|
|
|
var chars = <MalString>[];
|
|
|
|
for (var i = 0; i < arg.value.length; i++) {
|
|
|
|
chars.add(new MalString(arg.value[i]));
|
|
|
|
}
|
|
|
|
return new MalList(chars);
|
|
|
|
}
|
|
|
|
throw new MalException(new MalString('bad argument to "seq"'));
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'map': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var fn = args[0] as MalCallable;
|
|
|
|
var list = args[1] as MalIterable;
|
|
|
|
var newList = <MalType>[];
|
|
|
|
for (var element in list) {
|
|
|
|
newList.add(fn.call([element]));
|
|
|
|
}
|
|
|
|
return new MalList(newList);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'apply': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var func = args.first as MalCallable;
|
|
|
|
var argList = args.last as MalIterable;
|
|
|
|
var newArgs = args.sublist(1, args.length - 1);
|
|
|
|
newArgs.addAll(argList);
|
|
|
|
return func.call(newArgs);
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'meta': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var arg = args.first;
|
|
|
|
return arg.meta ?? new MalNil();
|
|
|
|
}),
|
2022-01-10 02:15:40 +03:00
|
|
|
'with-meta': new MalBuiltin((List<MalType> args) {
|
2016-11-14 01:21:44 +03:00
|
|
|
var evaled = args.first;
|
|
|
|
var evaledWithMeta = evaled.clone();
|
|
|
|
evaledWithMeta.meta = args[1];
|
|
|
|
return evaledWithMeta;
|
|
|
|
}),
|
|
|
|
};
|