Skip to content

Instantly share code, notes, and snippets.

@olexale
Created May 27, 2023 13:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olexale/dbaba212c81584dcce7e95e09e4e2e52 to your computer and use it in GitHub Desktop.
Save olexale/dbaba212c81584dcce7e95e09e4e2e52 to your computer and use it in GitHub Desktop.
scheme dart 2
import 'dart:math' as math;
Future<void> main() async {
final program = '(begin (define circle-area (lambda (r) (* pi (* r r)))) (circle-area 10))';
print(eval(parse(program), standardEnv));
}
dynamic parse(String program) => parseTokens(tokenize(program));
List<String> tokenize(String program) => program
.replaceAll('(', ' ( ')
.replaceAll(')', ' ) ')
.split(' ')
.where((token) => token.isNotEmpty)
.toList();
dynamic parseTokens(List<String> tokens) {
if (tokens.isEmpty) {
throw Exception('Unexpected EOF');
}
final token = tokens.removeAt(0);
if (token == '(') {
final elements = <dynamic>[];
while (tokens.first != ')') {
elements.add(parseTokens(tokens));
if (tokens.isEmpty) {
throw Exception('Unexpected EOF');
}
}
tokens.removeAt(0); // remove ')'
return elements;
} else if (token == ')') {
throw Exception('Unexpected ")"');
} else {
return _atom(token);
}
}
dynamic _atom(String token) {
// parse doubles
final d = double.tryParse(token);
if (d != null) {
return d;
}
// parse booleans
if (token == '#t') {
return true;
}
if (token == '#f') {
return false;
}
// parse strings
if (token.startsWith('"') && token.endsWith('"')) {
return token.substring(1, token.length - 1);
}
return Symbol(token);
}
class Symbol {
const Symbol(this.name);
final String name;
@override
String toString() => name;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Symbol && runtimeType == other.runtimeType && name == other.name;
@override
int get hashCode => name.hashCode;
}
final Map<Symbol, dynamic> standardEnv = {
..._strictParamsFuncs,
..._multipleParamsFuncs
};
final Map<Symbol, dynamic> _strictParamsFuncs = {
Symbol('pi'): math.pi,
Symbol('sin'): math.sin,
Symbol('cos'): math.cos,
Symbol('tan'): math.tan,
Symbol('asin'): math.asin,
Symbol('acos'): math.acos,
Symbol('atan'): math.atan,
Symbol('sqrt'): math.sqrt,
Symbol('random'): (num x) => math.Random().nextDouble() * x,
Symbol('>'): (a, b) => a > b,
Symbol('<'): (a, b) => a < b,
Symbol('>='): (a, b) => a >= b,
Symbol('<='): (a, b) => a <= b,
Symbol('='): (a, b) => a == b,
Symbol('abs'): (num x) => x.abs(),
Symbol('append'): (a, b) => a + b,
Symbol('apply'): (Function proc, List args) => Function.apply(proc, args),
Symbol('car'): (List x) => x.first,
Symbol('cdr'): (List x) => x.sublist(1),
Symbol('cons'): (x, List y) => [x, ...y],
Symbol('eq?'): (a, b) => a == b,
Symbol('expt'): math.pow,
Symbol('equal?'): (a, b) => a == b,
Symbol('length'): (List x) => x.length,
Symbol('not'): (bool x) => !x,
Symbol('null?'): (x) => x == null,
Symbol('number?'): (dynamic x) => x is num,
Symbol('print'): print,
Symbol('procedure?'): (x) => x is Function,
Symbol('round'): (num x) => x.round(),
Symbol('symbol?'): (x) => x is Symbol,
};
final Map<Symbol, dynamic> _multipleParamsFuncs = {
Symbol('begin'): (List x) => x.last,
Symbol('list'): (List<dynamic> x) => List.from(x),
Symbol('+'): (List x) => x.reduce((a, b) => a + b),
Symbol('-'): (List x) => x.reduce((a, b) => a - b),
Symbol('*'): (List x) => x.reduce((a, b) => a * b),
Symbol('/'): (List x) => x.reduce((a, b) => a / b),
Symbol('max'): (List x) => x.cast<num>().reduce((a, b) => math.max(a, b)),
Symbol('min'): (List x) => x.cast<num>().reduce((a, b) => math.min(a, b)),
};
dynamic eval(dynamic x, Map<Symbol, dynamic> env) {
if (x is Symbol) {
return env[x];
}
if (x is num || x is bool || x is String) {
return x;
}
if (x is List) {
return switch (x[0].toString()) {
'if' => _handleIf(x, env),
'define' => _handleDefine(x, env),
'quote' => x[1],
'lambda' => _handleLambda(x, env),
'map' => _handleMap(x, env),
_ => _handleProcedure(x, env),
};
}
throw Exception('Unknown expression type: $x');
}
dynamic _handleIf(List x, Map<Symbol, dynamic> env) {
final test = x[1];
final conseq = x[2];
final alt = x[3];
final exp = eval(test, env) ? conseq : alt;
return eval(exp, env);
}
dynamic _handleDefine(List x, Map<Symbol, dynamic> env) {
final symbol = x[1].toString();
final exp = x[2];
env[Symbol(symbol)] = eval(exp, env);
return null;
}
dynamic _handleLambda(List x, Map<Symbol, dynamic> env) {
return (List arguments) {
final localScope = <Symbol, dynamic>{
...Map.fromIterables(x[1].cast<Symbol>(), arguments),
...env,
};
return eval(x[2], localScope);
};
}
dynamic _handleMap(List x, Map<Symbol, dynamic> env) {
final args = eval(x[2], env);
return args.map((p) => eval([x[1], p], env)).toList();
}
dynamic _handleProcedure(List x, Map<Symbol, dynamic> env) {
var proc = eval(x[0], env);
var args = x.sublist(1).map((arg) => eval(arg, env)).toList();
return Function.apply(
proc,
// if function is either an exceptional or a custom lambda - wrap args
_multipleParamsFuncs.containsKey(x[0]) ||
(proc is Function && !_strictParamsFuncs.containsKey(x[0]))
? [args]
: args,
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment