Skip to content

Instantly share code, notes, and snippets.

@sma
Last active August 4, 2023 02:07
Show Gist options
  • Save sma/1a46f7f36d8d5d791230d036459e8a02 to your computer and use it in GitHub Desktop.
Save sma/1a46f7f36d8d5d791230d036459e8a02 to your computer and use it in GitHub Desktop.
a tiny forth interpreter
import 'package:flutter/material.dart';
void main() {
Forth().run('V: count 5 ! : inc dup @ 1 + ! ; '
'[ [ text ] count builder [ count inc ] " Increment text button ] '
'list column app run');
}
typedef Impl = void Function(Forth f);
class Forth {
final stack = <dynamic>[];
void push(dynamic value) => stack.add(value);
dynamic pop() => stack.removeLast();
List<String> words = [];
String next() => words.removeAt(0);
List<Impl>? code;
final Map<String, Impl> vocab = {
'+': (f) => f.push((f.pop() as num) + (f.pop() as num)),
'.': (f) => print(f.pop()),
'dup': (f) => f.push(f.stack.last),
':': (f) {
final name = f.next();
final code = <Impl>[];
f.vocab[name] = (f) => f.exec(code);
f.code = code;
},
';': (f) => f.code = null,
'"': (f) => f.add(f.next()),
'V:': (f) {
final name = f.next();
final value = ValueNotifier<dynamic>(null);
f.vocab[name] = f.lit(value);
f.push(value);
},
'@': (f) => f.push((f.pop() as ValueNotifier<dynamic>).value),
'!': (f) => ((dynamic v) => (f.pop() as ValueNotifier<dynamic>).value = v)(f.pop()),
'[': (f) {
f.push(f.code);
f.code = <Impl>[];
},
']': (f) {
final quot = f.code;
if (quot == null) throw Exception('] without [');
f.code = f.pop() as List<Impl>?;
f.add(quot);
},
'list': (f) {
final i = f.stack.length - 1;
f.exec(f.pop() as List<Impl>);
final l = f.stack.sublist(i);
f.stack.length = i;
f.push(l);
},
'builder': (f) {
final vl = f.pop() as ValueNotifier<dynamic>;
final quot = f.pop() as List<Impl>;
f.push(ValueListenableBuilder(
valueListenable: vl,
builder: (context, value, _) {
f.push(value);
f.exec(quot);
return f.pop() as Widget;
},
));
},
'text': (f) => f.push(Text('${f.pop()}')),
'button': (f) {
final child = f.pop() as Widget;
final quot = f.pop() as List<Impl>;
f.push(ElevatedButton(onPressed: () => f.exec(quot), child: child));
},
'column': (f) => f.push(Column(children: (f.pop() as List).cast<Widget>())),
'app': (f) => f.push(MaterialApp(home: Scaffold(body: f.pop() as Widget))),
'run': (f) => runApp(f.pop() as Widget),
};
final immediate = {';', '"', '[', ']'};
void run(String input) {
words = input.split(' ');
while (words.isNotEmpty) {
final word = next();
final impl = vocab[word] ?? lit(num.parse(word));
if (immediate.contains(word) || code == null) {
impl(this);
} else {
code!.add(impl);
}
}
}
Impl lit(dynamic value) => (f) => f.push(value);
void add(dynamic value) => code != null ? code!.add(lit(value)) : push(value);
void exec(List<Impl> quot) {
for (final impl in quot) {
impl(this);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment