mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 13:55:55 +03:00
Add Pike implementation
This commit is contained in:
parent
953e0d74d1
commit
a04e7a78d8
3
Makefile
3
Makefile
@ -92,7 +92,7 @@ DOCKERIZE =
|
||||
IMPLS = ada ada.2 awk bash basic bbc-basic c chuck clojure coffee common-lisp cpp crystal cs d dart \
|
||||
elisp elixir elm erlang es6 factor fantom forth fsharp go groovy gnu-smalltalk \
|
||||
guile haskell haxe hy io java js julia kotlin livescript logo lua make mal \
|
||||
matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp plpgsql \
|
||||
matlab miniMAL nasm nim objc objpascal ocaml perl perl6 php picolisp pike plpgsql \
|
||||
plsql powershell ps python r racket rexx rpython ruby rust scala scheme skew \
|
||||
swift swift3 swift4 tcl ts vala vb vhdl vimscript wasm yorick
|
||||
|
||||
@ -236,6 +236,7 @@ perl_STEP_TO_PROG = perl/$($(1)).pl
|
||||
perl6_STEP_TO_PROG = perl6/$($(1)).pl
|
||||
php_STEP_TO_PROG = php/$($(1)).php
|
||||
picolisp_STEP_TO_PROG = picolisp/$($(1)).l
|
||||
pike_STEP_TO_PROG = pike/$($(1)).pike
|
||||
plpgsql_STEP_TO_PROG = plpgsql/$($(1)).sql
|
||||
plsql_STEP_TO_PROG = plsql/$($(1)).sql
|
||||
powershell_STEP_TO_PROG = powershell/$($(1)).ps1
|
||||
|
97
pike/Core.pmod
Normal file
97
pike/Core.pmod
Normal file
@ -0,0 +1,97 @@
|
||||
import .Interop;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
private Val apply(mixed f, Val ... args)
|
||||
{
|
||||
if(sizeof(args) == 1) return f(@args[0].data);
|
||||
array(Val) mid_args = args[0..(sizeof(args) - 2)];
|
||||
return f(@(mid_args + args[-1].data));
|
||||
}
|
||||
|
||||
private Val swap_bang(Val atom, mixed f, Val ... args)
|
||||
{
|
||||
atom.data = f(@(({ atom.data }) + args));
|
||||
return atom.data;
|
||||
}
|
||||
|
||||
private mapping(string:function) builtins = ([
|
||||
"=": lambda(Val a, Val b) { return to_bool(a == b); },
|
||||
"throw": lambda(Val a) { throw(a); },
|
||||
|
||||
"nil?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_NIL); },
|
||||
"true?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_TRUE); },
|
||||
"false?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_FALSE); },
|
||||
"string?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_STRING); },
|
||||
"symbol": lambda(Val a) { return a.mal_type == MALTYPE_SYMBOL ? a : Symbol(a.value); },
|
||||
"symbol?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_SYMBOL); },
|
||||
"keyword": lambda(Val a) { return a.mal_type == MALTYPE_KEYWORD ? a : Keyword(a.value); },
|
||||
"keyword?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_KEYWORD); },
|
||||
"number?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_NUMBER); },
|
||||
"fn?": lambda(Val a) { return to_bool(a.is_fn && !a.macro); },
|
||||
"macro?": lambda(Val a) { return to_bool(a.macro); },
|
||||
|
||||
"pr-str": lambda(Val ... a) { return String(map(a, lambda(Val e) { return pr_str(e, true); }) * " "); },
|
||||
"str": lambda(Val ... a) { return String(map(a, lambda(Val e) { return pr_str(e, false); }) * ""); },
|
||||
"prn": lambda(Val ... a) { write(({ map(a, lambda(Val e) { return pr_str(e, true); }) * " ", "\n" })); return MAL_NIL; },
|
||||
"println": lambda(Val ... a) { write(({ map(a, lambda(Val e) { return pr_str(e, false); }) * " ", "\n" })); return MAL_NIL; },
|
||||
"read-string": lambda(Val a) { return read_str(a.value); },
|
||||
"readline": lambda(Val a) { string line = readline(a.value); return line ? String(line) : MAL_NIL; },
|
||||
"slurp": lambda(Val a) { return String(Stdio.read_file(a.value)); },
|
||||
|
||||
"<": lambda(Val a, Val b) { return to_bool(a.value < b.value); },
|
||||
"<=": lambda(Val a, Val b) { return to_bool(a.value <= b.value); },
|
||||
">": lambda(Val a, Val b) { return to_bool(a.value > b.value); },
|
||||
">=": lambda(Val a, Val b) { return to_bool(a.value >= b.value); },
|
||||
"+": lambda(Val a, Val b) { return Number(a.value + b.value); },
|
||||
"-": lambda(Val a, Val b) { return Number(a.value - b.value); },
|
||||
"*": lambda(Val a, Val b) { return Number(a.value * b.value); },
|
||||
"/": lambda(Val a, Val b) { return Number(a.value / b.value); },
|
||||
"time-ms": lambda() { array(int) t = System.gettimeofday(); return Number(t[0] * 1000 + t[1] / 1000); },
|
||||
|
||||
"list": lambda(Val ... a) { return List(a); },
|
||||
"list?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_LIST); },
|
||||
"vector": lambda(Val ... a) { return Vector(a); },
|
||||
"vector?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_VECTOR); },
|
||||
"hash-map": lambda(Val ... a) { return Map(a); },
|
||||
"map?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_MAP); },
|
||||
"assoc": lambda(Val a, Val ... b) { return a.assoc(b); },
|
||||
"dissoc": lambda(Val a, Val ... b) { return a.dissoc(b); },
|
||||
"get": lambda(Val a, Val b) { return a.mal_type != MALTYPE_NIL ? (a.data[b] || MAL_NIL) : MAL_NIL; },
|
||||
"contains?": lambda(Val a, Val b) { return to_bool(a.data[b]); },
|
||||
"keys": lambda(Val a) { return List(indices(a.data)); },
|
||||
"vals": lambda(Val a) { return List(values(a.data)); },
|
||||
|
||||
"sequential?": lambda(Val a) { return to_bool(a.is_sequence); },
|
||||
"cons": lambda(Val a, Val b) { return List(({ a }) + b.data); },
|
||||
"concat": lambda(Val ... a) { return List(`+(({ }), @map(a, lambda(Val e) { return e.data; }))); },
|
||||
"nth": lambda(Val a, Val b) { return a.nth(b.value); },
|
||||
"first": lambda(Val a) { return a.first(); },
|
||||
"rest": lambda(Val a) { return a.rest(); },
|
||||
"empty?": lambda(Val a) { return to_bool(a.emptyp()); },
|
||||
"count": lambda(Val a) { return Number(a.count()); },
|
||||
"apply": apply,
|
||||
"map": lambda(mixed f, Val a) { return List(map(a.data, f)); },
|
||||
|
||||
"conj": lambda(Val a, Val ... b) { return a.conj(b); },
|
||||
"seq": lambda(Val a) { return a.seq(); },
|
||||
|
||||
"meta": lambda(Val a) { return a.meta || MAL_NIL; },
|
||||
"with-meta": lambda(Val a, Val b) { Val new_a = a.clone(); new_a.meta = b; return new_a; },
|
||||
"atom": lambda(Val a) { return Atom(a); },
|
||||
"atom?": lambda(Val a) { return to_bool(a.mal_type == MALTYPE_ATOM); },
|
||||
"deref": lambda(Val a) { return a.data; },
|
||||
"reset!": lambda(Val a, Val b) { a.data = b; return a.data; },
|
||||
"swap!": swap_bang,
|
||||
|
||||
"pike-eval": lambda(Val a) { return pike_eval(a.value); },
|
||||
]);
|
||||
|
||||
mapping(Val:Val) NS()
|
||||
{
|
||||
mapping(Val:Val) ns = ([ ]);
|
||||
foreach(builtins; string name; function f) { ns[Symbol(name)] = BuiltinFn(name, f); }
|
||||
return ns;
|
||||
}
|
24
pike/Dockerfile
Normal file
24
pike/Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER Joel Martin <github@martintribe.org>
|
||||
|
||||
##########################################################
|
||||
# General requirements for testing or common across many
|
||||
# implementations
|
||||
##########################################################
|
||||
|
||||
RUN apt-get -y update
|
||||
|
||||
# Required for running tests
|
||||
RUN apt-get -y install make python
|
||||
|
||||
# Some typical implementation and test requirements
|
||||
RUN apt-get -y install curl libreadline-dev libedit-dev
|
||||
|
||||
RUN mkdir -p /mal
|
||||
WORKDIR /mal
|
||||
|
||||
##########################################################
|
||||
# Specific implementation requirements
|
||||
##########################################################
|
||||
|
||||
RUN apt-get -y install pike8.0 pike8.0-pcre
|
45
pike/Env.pmod
Normal file
45
pike/Env.pmod
Normal file
@ -0,0 +1,45 @@
|
||||
import .Types;
|
||||
|
||||
class Env
|
||||
{
|
||||
Env outer;
|
||||
mapping(string:Val) data;
|
||||
|
||||
void create(Env the_outer, List|void binds, List|void exprs)
|
||||
{
|
||||
outer = the_outer;
|
||||
data = ([ ]);
|
||||
if(binds)
|
||||
{
|
||||
for(int i = 0; i < binds.count(); i++)
|
||||
{
|
||||
if(binds.data[i].value == "&")
|
||||
{
|
||||
set(binds.data[i + 1], List(exprs.data[i..]));
|
||||
break;
|
||||
}
|
||||
set(binds.data[i], exprs.data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Val set(Val key, Val val)
|
||||
{
|
||||
data[key.value] = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
Env find(Val key)
|
||||
{
|
||||
if(data[key.value]) return this_object();
|
||||
if(outer) return outer.find(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Val get(Val key)
|
||||
{
|
||||
Env found_env = find(key);
|
||||
if(!found_env) throw("'" + key.value + "' not found");
|
||||
return found_env.data[key.value];
|
||||
}
|
||||
}
|
33
pike/Interop.pmod
Normal file
33
pike/Interop.pmod
Normal file
@ -0,0 +1,33 @@
|
||||
import .Types;
|
||||
|
||||
Val pike_eval(string expr_str)
|
||||
{
|
||||
program prog = compile_string("mixed tmp_func() { return (" + expr_str + "); }", "pike-eval");
|
||||
mixed v = prog()->tmp_func();
|
||||
return pike2mal(v);
|
||||
}
|
||||
|
||||
private Val pike2mal(mixed v)
|
||||
{
|
||||
if(stringp(v)) return String(v);
|
||||
if(intp(v)) return Number(v);
|
||||
if(arrayp(v))
|
||||
{
|
||||
array(Val) res = ({ });
|
||||
foreach(v, mixed e)
|
||||
{
|
||||
res += ({ pike2mal(e) });
|
||||
}
|
||||
return List(res);
|
||||
}
|
||||
if(mappingp(v))
|
||||
{
|
||||
array(Val) res = ({ });
|
||||
foreach(v; mixed k; mixed v)
|
||||
{
|
||||
res += ({ pike2mal(k), pike2mal(v) });
|
||||
}
|
||||
return Map(res);
|
||||
}
|
||||
return MAL_NIL;
|
||||
}
|
19
pike/Makefile
Normal file
19
pike/Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
SOURCES_BASE = readline.pike types.pike reader.pike printer.pike
|
||||
SOURCES_LISP = env.pike core.pike stepA_mal.pike
|
||||
SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
|
||||
|
||||
all:
|
||||
true
|
||||
|
||||
dist: mal.pike mal
|
||||
|
||||
mal.pike: $(SOURCES)
|
||||
cat $+ | grep -v "^#include" > $@
|
||||
|
||||
mal: mal.pike
|
||||
echo "#!/usr/bin/env pike" > $@
|
||||
cat $< >> $@
|
||||
chmod +x $@
|
||||
|
||||
clean:
|
||||
rm -f mal.pike mal
|
7
pike/Printer.pmod
Normal file
7
pike/Printer.pmod
Normal file
@ -0,0 +1,7 @@
|
||||
import .Types;
|
||||
|
||||
string pr_str(Val ast, bool print_readably)
|
||||
{
|
||||
if(functionp(ast)) return "#<Fn>";
|
||||
return ast->to_string(print_readably);
|
||||
}
|
122
pike/Reader.pmod
Normal file
122
pike/Reader.pmod
Normal file
@ -0,0 +1,122 @@
|
||||
import .Types;
|
||||
|
||||
Regexp.PCRE tokenizer_regexp = Regexp.PCRE.Studied("[\\s ,]*(~@|[\\[\\]{}()'`~@]|\"([\\\\].|[^\\\\\"])*\"?|;.*|[^\\s \\[\\]{}()'\"`~@,;]*)");
|
||||
Regexp.PCRE string_regexp = Regexp.PCRE.Studied("^\"(?:[\\\\].|[^\\\\\"])*\"$");
|
||||
Regexp.PCRE number_regexp = Regexp.PCRE.Studied("^-?[0-9]+$");
|
||||
|
||||
private class Reader(private array(string) tokens, private void|int position)
|
||||
{
|
||||
string next()
|
||||
{
|
||||
if(position >= sizeof(tokens)) return 0;
|
||||
string token = tokens[position];
|
||||
position++;
|
||||
return token;
|
||||
}
|
||||
|
||||
string peek()
|
||||
{
|
||||
if(position >= sizeof(tokens)) return 0;
|
||||
return tokens[position];
|
||||
}
|
||||
}
|
||||
|
||||
private array(string) tokenize(string str)
|
||||
{
|
||||
array(string) tokens = ({ });
|
||||
tokenizer_regexp.matchall(str, lambda(mixed m) {
|
||||
if(sizeof(m[1]) > 0 && m[1][0] != ';') tokens += ({ m[1] });
|
||||
});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private string unescape_string(string token)
|
||||
{
|
||||
if(!string_regexp.match(token)) throw("expected '\"', got EOF");
|
||||
string s = token[1..(sizeof(token) - 2)];
|
||||
s = replace(s, "\\\\", "\u029e");
|
||||
s = replace(s, "\\\"", "\"");
|
||||
s = replace(s, "\\n", "\n");
|
||||
s = replace(s, "\u029e", "\\");
|
||||
return s;
|
||||
}
|
||||
|
||||
private Val read_atom(Reader reader)
|
||||
{
|
||||
string token = reader->next();
|
||||
if(number_regexp.match(token)) return Number((int)token);
|
||||
if(token[0] == '"') return String(unescape_string(token));
|
||||
if(token[0] == ':') return Keyword(token[1..]);
|
||||
switch(token)
|
||||
{
|
||||
case "nil": return MAL_NIL;
|
||||
case "true": return MAL_TRUE;
|
||||
case "false": return MAL_FALSE;
|
||||
}
|
||||
return Symbol(token);
|
||||
}
|
||||
|
||||
private array(Val) read_seq(Reader reader, string start, string end)
|
||||
{
|
||||
string token = reader->next();
|
||||
if(token != start) throw("expected '" + start + "'");
|
||||
token = reader->peek();
|
||||
array(Val) elements = ({ });
|
||||
while(token != end)
|
||||
{
|
||||
if(!token) throw("expected '" + end + "', got EOF");
|
||||
elements += ({ read_form(reader) });
|
||||
token = reader->peek();
|
||||
}
|
||||
reader->next();
|
||||
return elements;
|
||||
}
|
||||
|
||||
private Val reader_macro(Reader reader, string symbol)
|
||||
{
|
||||
reader->next();
|
||||
return List(({ Symbol(symbol), read_form(reader) }));
|
||||
}
|
||||
|
||||
private Val read_form(Reader reader)
|
||||
{
|
||||
string token = reader->peek();
|
||||
switch(token)
|
||||
{
|
||||
case "'":
|
||||
return reader_macro(reader, "quote");
|
||||
case "`":
|
||||
return reader_macro(reader, "quasiquote");
|
||||
case "~":
|
||||
return reader_macro(reader, "unquote");
|
||||
case "~@":
|
||||
return reader_macro(reader, "splice-unquote");
|
||||
case "@":
|
||||
return reader_macro(reader, "deref");
|
||||
case "^":
|
||||
reader->next();
|
||||
Val meta = read_form(reader);
|
||||
return List(({ Symbol("with-meta"), read_form(reader), meta }));
|
||||
case "(":
|
||||
return List(read_seq(reader, "(", ")"));
|
||||
case ")":
|
||||
throw("unexpected ')'");
|
||||
case "[":
|
||||
return Vector(read_seq(reader, "[", "]"));
|
||||
case "]":
|
||||
throw("unexpected ']'");
|
||||
case "{":
|
||||
return Map(read_seq(reader, "{", "}"));
|
||||
case "}":
|
||||
throw("unexpected '}'");
|
||||
default:
|
||||
return read_atom(reader);
|
||||
}
|
||||
}
|
||||
|
||||
Val read_str(string str)
|
||||
{
|
||||
array(string) tokens = tokenize(str);
|
||||
if(sizeof(tokens) == 0) return MAL_NIL;
|
||||
return read_form(Reader(tokens));
|
||||
}
|
4
pike/Readline.pmod
Normal file
4
pike/Readline.pmod
Normal file
@ -0,0 +1,4 @@
|
||||
string readline(string prompt) {
|
||||
write(prompt);
|
||||
return Stdio.stdin->gets();
|
||||
}
|
460
pike/Types.pmod
Normal file
460
pike/Types.pmod
Normal file
@ -0,0 +1,460 @@
|
||||
enum MalType {
|
||||
MALTYPE_UNDEFINED,
|
||||
MALTYPE_NIL,
|
||||
MALTYPE_TRUE,
|
||||
MALTYPE_FALSE,
|
||||
MALTYPE_NUMBER,
|
||||
MALTYPE_SYMBOL,
|
||||
MALTYPE_STRING,
|
||||
MALTYPE_KEYWORD,
|
||||
MALTYPE_LIST,
|
||||
MALTYPE_VECTOR,
|
||||
MALTYPE_MAP,
|
||||
MALTYPE_FN,
|
||||
MALTYPE_BUILTINFN,
|
||||
MALTYPE_ATOM,
|
||||
};
|
||||
|
||||
class Val
|
||||
{
|
||||
constant mal_type = MALTYPE_UNDEFINED;
|
||||
Val meta;
|
||||
string to_string(bool print_readably);
|
||||
Val clone();
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
return objectp(other) && other.mal_type == mal_type;
|
||||
}
|
||||
}
|
||||
|
||||
class Nil
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_NIL;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "nil";
|
||||
}
|
||||
|
||||
int count()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Val first()
|
||||
{
|
||||
return MAL_NIL;
|
||||
}
|
||||
|
||||
Val rest()
|
||||
{
|
||||
return List(({ }));
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return this_object();
|
||||
}
|
||||
|
||||
Val seq()
|
||||
{
|
||||
return MAL_NIL;
|
||||
}
|
||||
}
|
||||
|
||||
Nil MAL_NIL = Nil();
|
||||
|
||||
class True
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_TRUE;
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "true";
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return this_object();
|
||||
}
|
||||
}
|
||||
|
||||
True MAL_TRUE = True();
|
||||
|
||||
class False
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_FALSE;
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "false";
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return this_object();
|
||||
}
|
||||
}
|
||||
|
||||
False MAL_FALSE = False();
|
||||
|
||||
Val to_bool(bool b)
|
||||
{
|
||||
if(b) return MAL_TRUE;
|
||||
return MAL_FALSE;
|
||||
}
|
||||
|
||||
class Number(int value)
|
||||
{
|
||||
constant mal_type = MALTYPE_NUMBER;
|
||||
inherit Val;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return (string)value;
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
return ::`==(other) && other.value == value;
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return this_object();
|
||||
}
|
||||
}
|
||||
|
||||
class Symbol(string value)
|
||||
{
|
||||
constant mal_type = MALTYPE_SYMBOL;
|
||||
inherit Val;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
return ::`==(other) && other.value == value;
|
||||
}
|
||||
|
||||
int __hash()
|
||||
{
|
||||
return hash((string)mal_type) ^ hash(value);
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return Symbol(value);
|
||||
}
|
||||
}
|
||||
|
||||
class String(string value)
|
||||
{
|
||||
constant mal_type = MALTYPE_STRING;
|
||||
inherit Val;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
if(print_readably) {
|
||||
string s = replace(value, "\\", "\\\\");
|
||||
s = replace(s, "\"", "\\\"");
|
||||
s = replace(s, "\n", "\\n");
|
||||
return "\"" + s + "\"";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
return ::`==(other) && other.value == value;
|
||||
}
|
||||
|
||||
int __hash()
|
||||
{
|
||||
return hash((string)mal_type) ^ hash(value);
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return String(value);
|
||||
}
|
||||
|
||||
Val seq()
|
||||
{
|
||||
if(sizeof(value) == 0) return MAL_NIL;
|
||||
array(Val) parts = ({ });
|
||||
for(int i = 0; i < sizeof(value); i++)
|
||||
{
|
||||
parts += ({ String(value[i..i]) });
|
||||
}
|
||||
return List(parts);
|
||||
}
|
||||
}
|
||||
|
||||
class Keyword(string value)
|
||||
{
|
||||
constant mal_type = MALTYPE_KEYWORD;
|
||||
inherit Val;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return ":" + value;
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
return ::`==(other) && other.value == value;
|
||||
}
|
||||
|
||||
int __hash()
|
||||
{
|
||||
return hash((string)mal_type) ^ hash(value);
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return Keyword(value);
|
||||
}
|
||||
}
|
||||
|
||||
class Sequence(array(Val) data)
|
||||
{
|
||||
inherit Val;
|
||||
constant is_sequence = true;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return map(data, lambda(Val e) { return e.to_string(print_readably); }) * " ";
|
||||
}
|
||||
|
||||
bool emptyp()
|
||||
{
|
||||
return sizeof(data) == 0;
|
||||
}
|
||||
|
||||
int count()
|
||||
{
|
||||
return sizeof(data);
|
||||
}
|
||||
|
||||
Val nth(int index)
|
||||
{
|
||||
if(index >= count()) throw("nth: index out of range");
|
||||
return data[index];
|
||||
}
|
||||
|
||||
Val first()
|
||||
{
|
||||
if(emptyp()) return MAL_NIL;
|
||||
return data[0];
|
||||
}
|
||||
|
||||
Val rest()
|
||||
{
|
||||
return List(data[1..]);
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
if(!objectp(other)) return 0;
|
||||
if(!other.is_sequence) return 0;
|
||||
if(other.count() != count()) return 0;
|
||||
for(int i = 0; i < count(); i++)
|
||||
{
|
||||
if(other.data[i] != data[i]) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Val seq()
|
||||
{
|
||||
if(emptyp()) return MAL_NIL;
|
||||
return List(data);
|
||||
}
|
||||
}
|
||||
|
||||
class List
|
||||
{
|
||||
inherit Sequence;
|
||||
constant mal_type = MALTYPE_LIST;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "(" + ::to_string(print_readably) + ")";
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return List(data);
|
||||
}
|
||||
|
||||
Val conj(array(Val) other)
|
||||
{
|
||||
return List(reverse(other) + data);
|
||||
}
|
||||
}
|
||||
|
||||
class Vector
|
||||
{
|
||||
inherit Sequence;
|
||||
constant mal_type = MALTYPE_VECTOR;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "[" + ::to_string(print_readably) + "]";
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return Vector(data);
|
||||
}
|
||||
|
||||
Val conj(array(Val) other)
|
||||
{
|
||||
return Vector(data + other);
|
||||
}
|
||||
}
|
||||
|
||||
class Map
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_MAP;
|
||||
mapping(Val:Val) data;
|
||||
|
||||
void create(array(Val) list)
|
||||
{
|
||||
array(Val) keys = Array.everynth(list, 2, 0);
|
||||
array(Val) vals = Array.everynth(list, 2, 1);
|
||||
data = mkmapping(keys, vals);
|
||||
}
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
array(string) strs = ({ });
|
||||
foreach(data; Val k; Val v)
|
||||
{
|
||||
strs += ({ k.to_string(print_readably), v.to_string(print_readably) });
|
||||
}
|
||||
return "{" + (strs * " ") + "}";
|
||||
}
|
||||
|
||||
int count()
|
||||
{
|
||||
return sizeof(data);
|
||||
}
|
||||
|
||||
bool `==(mixed other)
|
||||
{
|
||||
if(!::`==(other)) return 0;
|
||||
if(other.count() != count()) return 0;
|
||||
foreach(data; Val k; Val v)
|
||||
{
|
||||
if(other.data[k] != v) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Val assoc(array(Val) list)
|
||||
{
|
||||
array(Val) keys = Array.everynth(list, 2, 0);
|
||||
array(Val) vals = Array.everynth(list, 2, 1);
|
||||
Map result = Map(({ }));
|
||||
result.data = copy_value(data);
|
||||
for(int i = 0; i < sizeof(keys); i++)
|
||||
{
|
||||
result.data[keys[i]] = vals[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Val dissoc(array(Val) list)
|
||||
{
|
||||
Map result = Map(({ }));
|
||||
result.data = copy_value(data);
|
||||
foreach(list, Val key) m_delete(result.data, key);
|
||||
return result;
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
Map m = Map(({ }));
|
||||
m.data = data;
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
class Fn(Val ast, Val params, .Env.Env env, function func, void|bool macro)
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_FN;
|
||||
constant is_fn = true;
|
||||
|
||||
void set_macro()
|
||||
{
|
||||
macro = true;
|
||||
}
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
string tag = macro ? "Macro" : "Fn";
|
||||
return "#<" + tag + " params=" + params.to_string(true) + ">";
|
||||
}
|
||||
|
||||
mixed `()(mixed ... args)
|
||||
{
|
||||
return func(@args);
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return Fn(ast, params, env, func);
|
||||
}
|
||||
|
||||
Val clone_as_macro()
|
||||
{
|
||||
return Fn(ast, params, env, func, true);
|
||||
}
|
||||
}
|
||||
|
||||
class BuiltinFn(string name, function func)
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_BUILTINFN;
|
||||
constant is_fn = true;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "#<BuiltinFn " + name + ">";
|
||||
}
|
||||
|
||||
mixed `()(mixed ... args)
|
||||
{
|
||||
return func(@args);
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return BuiltinFn(name, func);
|
||||
}
|
||||
}
|
||||
|
||||
class Atom(Val data)
|
||||
{
|
||||
inherit Val;
|
||||
constant mal_type = MALTYPE_ATOM;
|
||||
|
||||
string to_string(bool print_readably)
|
||||
{
|
||||
return "(atom " + data.to_string(print_readably) + ")";
|
||||
}
|
||||
|
||||
Val clone()
|
||||
{
|
||||
return Atom(data);
|
||||
}
|
||||
}
|
2
pike/run
Executable file
2
pike/run
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exec pike $(dirname $0)/${STEP:-stepA_mal}.pike "${@}"
|
34
pike/step0_repl.pike
Normal file
34
pike/step0_repl.pike
Normal file
@ -0,0 +1,34 @@
|
||||
import .Readline;
|
||||
|
||||
string READ(string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
string EVAL(string ast, string env)
|
||||
{
|
||||
return ast;
|
||||
}
|
||||
|
||||
string PRINT(string exp)
|
||||
{
|
||||
return exp;
|
||||
}
|
||||
|
||||
string rep(string str)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), ""));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
write(({ rep(line), "\n" }));
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
41
pike/step1_read_print.pike
Normal file
41
pike/step1_read_print.pike
Normal file
@ -0,0 +1,41 @@
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, string env)
|
||||
{
|
||||
return ast;
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), ""));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
75
pike/step2_eval.pike
Normal file
75
pike/step2_eval.pike
Normal file
@ -0,0 +1,75 @@
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, mapping(string:function) env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
function f = env[ast.value];
|
||||
if(!f) throw("'" + ast.value + "' not found");
|
||||
return f;
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, mapping(string:function) env)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
function f = evaled_ast.data[0];
|
||||
return f(@evaled_ast.data[1..]);
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, mapping(string:function) env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
mapping(string:function) repl_env = ([
|
||||
"+": lambda(Val a, Val b) { return Number(a.value + b.value); },
|
||||
"-": lambda(Val a, Val b) { return Number(a.value - b.value); },
|
||||
"*": lambda(Val a, Val b) { return Number(a.value * b.value); },
|
||||
"/": lambda(Val a, Val b) { return Number(a.value / b.value); }
|
||||
]);
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
88
pike/step3_env.pike
Normal file
88
pike/step3_env.pike
Normal file
@ -0,0 +1,88 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
return EVAL(ast.data[2], let_env);
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
function f = evaled_ast.data[0];
|
||||
return f(@evaled_ast.data[1..]);
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
repl_env.set(Symbol("+"), lambda(Val a, Val b) { return Number(a.value + b.value); });
|
||||
repl_env.set(Symbol("-"), lambda(Val a, Val b) { return Number(a.value - b.value); });
|
||||
repl_env.set(Symbol("*"), lambda(Val a, Val b) { return Number(a.value * b.value); });
|
||||
repl_env.set(Symbol("/"), lambda(Val a, Val b) { return Number(a.value / b.value); });
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
106
pike/step4_if_fn_do.pike
Normal file
106
pike/step4_if_fn_do.pike
Normal file
@ -0,0 +1,106 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
return EVAL(ast.data[2], let_env);
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
return result;
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
return EVAL(ast.data[3], env);
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
return EVAL(ast.data[2], env);
|
||||
case "fn*":
|
||||
return lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); };
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
return f(@evaled_ast.data[1..]);
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
124
pike/step5_tco.pike
Normal file
124
pike/step5_tco.pike
Normal file
@ -0,0 +1,124 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
132
pike/step6_file.pike
Normal file
132
pike/step6_file.pike
Normal file
@ -0,0 +1,132 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main(int argc, array argv)
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
repl_env.set(Symbol("eval"), BuiltinFn("eval", lambda(Val a) { return EVAL(a, repl_env); }));
|
||||
repl_env.set(Symbol("*ARGV*"), List(map(argv[2..], String)));
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
||||
if(argc >= 2)
|
||||
{
|
||||
rep("(load-file \"" + argv[1] + "\")", repl_env);
|
||||
return 0;
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
154
pike/step7_quote.pike
Normal file
154
pike/step7_quote.pike
Normal file
@ -0,0 +1,154 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
bool is_pair(Val e)
|
||||
{
|
||||
return e.is_sequence && !e.emptyp();
|
||||
}
|
||||
|
||||
Val quasiquote(Val ast)
|
||||
{
|
||||
if(!is_pair(ast)) return List(({ Symbol("quote"), ast }));
|
||||
Val ast0 = ast.data[0];
|
||||
if(ast0.mal_type == MALTYPE_SYMBOL && ast0.value == "unquote") return ast.data[1];
|
||||
if(is_pair(ast0) && ast0.data[0].mal_type == MALTYPE_SYMBOL && ast0.data[0].value == "splice-unquote")
|
||||
{
|
||||
return List(({ Symbol("concat"), ast0.data[1], quasiquote(ast.rest()) }));
|
||||
}
|
||||
return List(({ Symbol("cons"), quasiquote(ast0), quasiquote(ast.rest()) }));
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "quote":
|
||||
return ast.data[1];
|
||||
case "quasiquote":
|
||||
ast = quasiquote(ast.data[1]);
|
||||
continue; // TCO
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main(int argc, array argv)
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
repl_env.set(Symbol("eval"), BuiltinFn("eval", lambda(Val a) { return EVAL(a, repl_env); }));
|
||||
repl_env.set(Symbol("*ARGV*"), List(map(argv[2..], String)));
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
||||
if(argc >= 2)
|
||||
{
|
||||
rep("(load-file \"" + argv[1] + "\")", repl_env);
|
||||
return 0;
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
185
pike/step8_macros.pike
Normal file
185
pike/step8_macros.pike
Normal file
@ -0,0 +1,185 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
bool is_pair(Val e)
|
||||
{
|
||||
return e.is_sequence && !e.emptyp();
|
||||
}
|
||||
|
||||
Val quasiquote(Val ast)
|
||||
{
|
||||
if(!is_pair(ast)) return List(({ Symbol("quote"), ast }));
|
||||
Val ast0 = ast.data[0];
|
||||
if(ast0.mal_type == MALTYPE_SYMBOL && ast0.value == "unquote") return ast.data[1];
|
||||
if(is_pair(ast0) && ast0.data[0].mal_type == MALTYPE_SYMBOL && ast0.data[0].value == "splice-unquote")
|
||||
{
|
||||
return List(({ Symbol("concat"), ast0.data[1], quasiquote(ast.rest()) }));
|
||||
}
|
||||
return List(({ Symbol("cons"), quasiquote(ast0), quasiquote(ast.rest()) }));
|
||||
}
|
||||
|
||||
bool is_macro_call(Val ast, Env env)
|
||||
{
|
||||
if(ast.mal_type == MALTYPE_LIST &&
|
||||
!ast.emptyp() &&
|
||||
ast.data[0].mal_type == MALTYPE_SYMBOL &&
|
||||
env.find(ast.data[0]))
|
||||
{
|
||||
Val v = env.get(ast.data[0]);
|
||||
if(objectp(v) && v.macro) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Val macroexpand(Val ast, Env env)
|
||||
{
|
||||
while(is_macro_call(ast, env))
|
||||
{
|
||||
Val macro = env.get(ast.data[0]);
|
||||
ast = macro(@ast.data[1..]);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
ast = macroexpand(ast, env);
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "quote":
|
||||
return ast.data[1];
|
||||
case "quasiquote":
|
||||
ast = quasiquote(ast.data[1]);
|
||||
continue; // TCO
|
||||
case "defmacro!":
|
||||
Val macro = EVAL(ast.data[2], env).clone_as_macro();
|
||||
return env.set(ast.data[1], macro);
|
||||
case "macroexpand":
|
||||
return macroexpand(ast.data[1], env);
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main(int argc, array argv)
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
repl_env.set(Symbol("eval"), BuiltinFn("eval", lambda(Val a) { return EVAL(a, repl_env); }));
|
||||
repl_env.set(Symbol("*ARGV*"), List(map(argv[2..], String)));
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
if(argc >= 2)
|
||||
{
|
||||
rep("(load-file \"" + argv[1] + "\")", repl_env);
|
||||
return 0;
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err)) err = err[0];
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
205
pike/step9_try.pike
Normal file
205
pike/step9_try.pike
Normal file
@ -0,0 +1,205 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
bool is_pair(Val e)
|
||||
{
|
||||
return e.is_sequence && !e.emptyp();
|
||||
}
|
||||
|
||||
Val quasiquote(Val ast)
|
||||
{
|
||||
if(!is_pair(ast)) return List(({ Symbol("quote"), ast }));
|
||||
Val ast0 = ast.data[0];
|
||||
if(ast0.mal_type == MALTYPE_SYMBOL && ast0.value == "unquote") return ast.data[1];
|
||||
if(is_pair(ast0) && ast0.data[0].mal_type == MALTYPE_SYMBOL && ast0.data[0].value == "splice-unquote")
|
||||
{
|
||||
return List(({ Symbol("concat"), ast0.data[1], quasiquote(ast.rest()) }));
|
||||
}
|
||||
return List(({ Symbol("cons"), quasiquote(ast0), quasiquote(ast.rest()) }));
|
||||
}
|
||||
|
||||
bool is_macro_call(Val ast, Env env)
|
||||
{
|
||||
if(ast.mal_type == MALTYPE_LIST &&
|
||||
!ast.emptyp() &&
|
||||
ast.data[0].mal_type == MALTYPE_SYMBOL &&
|
||||
env.find(ast.data[0]))
|
||||
{
|
||||
Val v = env.get(ast.data[0]);
|
||||
if(objectp(v) && v.macro) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Val macroexpand(Val ast, Env env)
|
||||
{
|
||||
while(is_macro_call(ast, env))
|
||||
{
|
||||
Val macro = env.get(ast.data[0]);
|
||||
ast = macro(@ast.data[1..]);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
ast = macroexpand(ast, env);
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "quote":
|
||||
return ast.data[1];
|
||||
case "quasiquote":
|
||||
ast = quasiquote(ast.data[1]);
|
||||
continue; // TCO
|
||||
case "defmacro!":
|
||||
Val macro = EVAL(ast.data[2], env).clone_as_macro();
|
||||
return env.set(ast.data[1], macro);
|
||||
case "macroexpand":
|
||||
return macroexpand(ast.data[1], env);
|
||||
case "try*":
|
||||
if(ast.count() < 3) return EVAL(ast.data[1], env);
|
||||
if(mixed err = catch { return EVAL(ast.data[1], env); } )
|
||||
{
|
||||
Val err_val;
|
||||
if(objectp(err)) err_val = err;
|
||||
else if(stringp(err)) err_val = String(err);
|
||||
else if(arrayp(err)) err_val = String(err[0]);
|
||||
Val catch_clause = ast.data[2];
|
||||
Env catch_env = Env(env);
|
||||
catch_env.set(catch_clause.data[1], err_val);
|
||||
return EVAL(catch_clause.data[2], catch_env);
|
||||
}
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main(int argc, array argv)
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
repl_env.set(Symbol("eval"), BuiltinFn("eval", lambda(Val a) { return EVAL(a, repl_env); }));
|
||||
repl_env.set(Symbol("*ARGV*"), List(map(argv[2..], String)));
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
if(argc >= 2)
|
||||
{
|
||||
rep("(load-file \"" + argv[1] + "\")", repl_env);
|
||||
return 0;
|
||||
}
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(objectp(err))
|
||||
{
|
||||
err = err.to_string(true);
|
||||
}
|
||||
else if(arrayp(err))
|
||||
{
|
||||
err = err[0];
|
||||
}
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
207
pike/stepA_mal.pike
Normal file
207
pike/stepA_mal.pike
Normal file
@ -0,0 +1,207 @@
|
||||
import .Env;
|
||||
import .Printer;
|
||||
import .Reader;
|
||||
import .Readline;
|
||||
import .Types;
|
||||
|
||||
Val READ(string str)
|
||||
{
|
||||
return read_str(str);
|
||||
}
|
||||
|
||||
bool is_pair(Val e)
|
||||
{
|
||||
return e.is_sequence && !e.emptyp();
|
||||
}
|
||||
|
||||
Val quasiquote(Val ast)
|
||||
{
|
||||
if(!is_pair(ast)) return List(({ Symbol("quote"), ast }));
|
||||
Val ast0 = ast.data[0];
|
||||
if(ast0.mal_type == MALTYPE_SYMBOL && ast0.value == "unquote") return ast.data[1];
|
||||
if(is_pair(ast0) && ast0.data[0].mal_type == MALTYPE_SYMBOL && ast0.data[0].value == "splice-unquote")
|
||||
{
|
||||
return List(({ Symbol("concat"), ast0.data[1], quasiquote(ast.rest()) }));
|
||||
}
|
||||
return List(({ Symbol("cons"), quasiquote(ast0), quasiquote(ast.rest()) }));
|
||||
}
|
||||
|
||||
bool is_macro_call(Val ast, Env env)
|
||||
{
|
||||
if(ast.mal_type == MALTYPE_LIST &&
|
||||
!ast.emptyp() &&
|
||||
ast.data[0].mal_type == MALTYPE_SYMBOL &&
|
||||
env.find(ast.data[0]))
|
||||
{
|
||||
Val v = env.get(ast.data[0]);
|
||||
if(objectp(v) && v.macro) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Val macroexpand(Val ast, Env env)
|
||||
{
|
||||
while(is_macro_call(ast, env))
|
||||
{
|
||||
Val macro = env.get(ast.data[0]);
|
||||
ast = macro(@ast.data[1..]);
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
Val eval_ast(Val ast, Env env)
|
||||
{
|
||||
switch(ast.mal_type)
|
||||
{
|
||||
case MALTYPE_SYMBOL:
|
||||
return env.get(ast);
|
||||
case MALTYPE_LIST:
|
||||
return List(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_VECTOR:
|
||||
return Vector(map(ast.data, lambda(Val e) { return EVAL(e, env); }));
|
||||
case MALTYPE_MAP:
|
||||
array(Val) elements = ({ });
|
||||
foreach(ast.data; Val k; Val v)
|
||||
{
|
||||
elements += ({ k, EVAL(v, env) });
|
||||
}
|
||||
return Map(elements);
|
||||
default:
|
||||
return ast;
|
||||
}
|
||||
}
|
||||
|
||||
Val EVAL(Val ast, Env env)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
ast = macroexpand(ast, env);
|
||||
if(ast.mal_type != MALTYPE_LIST) return eval_ast(ast, env);
|
||||
if(ast.emptyp()) return ast;
|
||||
if(ast.data[0].mal_type == MALTYPE_SYMBOL) {
|
||||
switch(ast.data[0].value)
|
||||
{
|
||||
case "def!":
|
||||
return env.set(ast.data[1], EVAL(ast.data[2], env));
|
||||
case "let*":
|
||||
Env let_env = Env(env);
|
||||
Val ast1 = ast.data[1];
|
||||
for(int i = 0; i < sizeof(ast1.data); i += 2)
|
||||
{
|
||||
let_env.set(ast1.data[i], EVAL(ast1.data[i + 1], let_env));
|
||||
}
|
||||
env = let_env;
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "quote":
|
||||
return ast.data[1];
|
||||
case "quasiquote":
|
||||
ast = quasiquote(ast.data[1]);
|
||||
continue; // TCO
|
||||
case "defmacro!":
|
||||
Val macro = EVAL(ast.data[2], env).clone_as_macro();
|
||||
return env.set(ast.data[1], macro);
|
||||
case "macroexpand":
|
||||
return macroexpand(ast.data[1], env);
|
||||
case "try*":
|
||||
if(ast.count() < 3) return EVAL(ast.data[1], env);
|
||||
if(mixed err = catch { return EVAL(ast.data[1], env); } )
|
||||
{
|
||||
Val err_val;
|
||||
if(objectp(err)) err_val = err;
|
||||
else if(stringp(err)) err_val = String(err);
|
||||
else if(arrayp(err)) err_val = String(err[0]);
|
||||
Val catch_clause = ast.data[2];
|
||||
Env catch_env = Env(env);
|
||||
catch_env.set(catch_clause.data[1], err_val);
|
||||
return EVAL(catch_clause.data[2], catch_env);
|
||||
}
|
||||
case "do":
|
||||
Val result;
|
||||
foreach(ast.data[1..(sizeof(ast.data) - 2)], Val element)
|
||||
{
|
||||
result = EVAL(element, env);
|
||||
}
|
||||
ast = ast.data[-1];
|
||||
continue; // TCO
|
||||
case "if":
|
||||
Val cond = EVAL(ast.data[1], env);
|
||||
if(cond.mal_type == MALTYPE_FALSE || cond.mal_type == MALTYPE_NIL)
|
||||
{
|
||||
if(sizeof(ast.data) > 3)
|
||||
ast = ast.data[3];
|
||||
else
|
||||
return MAL_NIL;
|
||||
}
|
||||
else
|
||||
ast = ast.data[2];
|
||||
continue; // TCO
|
||||
case "fn*":
|
||||
return Fn(ast.data[2], ast.data[1], env,
|
||||
lambda(Val ... a) { return EVAL(ast.data[2], Env(env, ast.data[1], List(a))); });
|
||||
}
|
||||
}
|
||||
Val evaled_ast = eval_ast(ast, env);
|
||||
Val f = evaled_ast.data[0];
|
||||
switch(f.mal_type)
|
||||
{
|
||||
case MALTYPE_BUILTINFN:
|
||||
return f(@evaled_ast.data[1..]);
|
||||
case MALTYPE_FN:
|
||||
ast = f.ast;
|
||||
env = Env(f.env, f.params, List(evaled_ast.data[1..]));
|
||||
continue; // TCO
|
||||
default:
|
||||
throw("Unknown function type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string PRINT(Val exp)
|
||||
{
|
||||
return pr_str(exp, true);
|
||||
}
|
||||
|
||||
string rep(string str, Env env)
|
||||
{
|
||||
return PRINT(EVAL(READ(str), env));
|
||||
}
|
||||
|
||||
int main(int argc, array argv)
|
||||
{
|
||||
Env repl_env = Env(0);
|
||||
foreach(.Core.NS(); Val k; Val v) repl_env.set(k, v);
|
||||
repl_env.set(Symbol("eval"), BuiltinFn("eval", lambda(Val a) { return EVAL(a, repl_env); }));
|
||||
repl_env.set(Symbol("*ARGV*"), List(map(argv[2..], String)));
|
||||
rep("(def! *host-language* \"pike\")", repl_env);
|
||||
rep("(def! not (fn* (a) (if a false true)))", repl_env);
|
||||
rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))", repl_env);
|
||||
rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))", repl_env);
|
||||
if(argc >= 2)
|
||||
{
|
||||
rep("(load-file \"" + argv[1] + "\")", repl_env);
|
||||
return 0;
|
||||
}
|
||||
rep("(println (str \"Mal [\" \*host-language\* \"]\"))", repl_env);
|
||||
while(1)
|
||||
{
|
||||
string line = readline("user> ");
|
||||
if(!line) break;
|
||||
if(strlen(line) == 0) continue;
|
||||
if(mixed err = catch { write(({ rep(line, repl_env), "\n" })); } )
|
||||
{
|
||||
if(arrayp(err))
|
||||
{
|
||||
err = err[0];
|
||||
}
|
||||
else if(objectp(err))
|
||||
{
|
||||
err = err.to_string(true);
|
||||
}
|
||||
write(({ "Error: ", err, "\n" }));
|
||||
}
|
||||
}
|
||||
write("\n");
|
||||
return 0;
|
||||
}
|
15
pike/tests/step5_tco.mal
Normal file
15
pike/tests/step5_tco.mal
Normal file
@ -0,0 +1,15 @@
|
||||
;; Test recursive non-tail call function
|
||||
|
||||
(def! sum-to (fn* (n) (if (= n 0) 0 (+ n (sum-to (- n 1))))))
|
||||
|
||||
(sum-to 10)
|
||||
;=>55
|
||||
|
||||
;;; no try* yet, so test completion of side-effects
|
||||
(def! res1 nil)
|
||||
;=>nil
|
||||
;;; For implementations without their own TCO this should fail and
|
||||
;;; leave res1 unchanged
|
||||
(def! res1 (sum-to 10000))
|
||||
res1
|
||||
;=>nil
|
36
pike/tests/stepA_mal.mal
Normal file
36
pike/tests/stepA_mal.mal
Normal file
@ -0,0 +1,36 @@
|
||||
;; Testing basic Pike interop
|
||||
|
||||
;;; pike-eval compiles the given string inside a temporary function after a
|
||||
;;; "return " keyword. To evaluate complex statements, you may use an anonymous
|
||||
;;; lambda and call it immediately (see the last example).
|
||||
|
||||
(pike-eval "7")
|
||||
;=>7
|
||||
|
||||
(pike-eval "'A'")
|
||||
;=>65
|
||||
|
||||
(pike-eval "\"7\"")
|
||||
;=>"7"
|
||||
|
||||
(pike-eval "({ 7,8,9 })")
|
||||
;=>(7 8 9)
|
||||
|
||||
(pike-eval "([ \"abc\": 789 ])")
|
||||
;=>{"abc" 789}
|
||||
|
||||
(pike-eval "write(\"hello\\n\")")
|
||||
;/hello
|
||||
;=>6
|
||||
|
||||
(pike-eval "map(({ \"a\", \"b\", \"c\" }), lambda(string x) { return \"X\" + x + \"Y\"; }) * \" \"")
|
||||
;=>"XaY XbY XcY"
|
||||
|
||||
(pike-eval "map(({ 1,2,3 }), lambda(int x) { return 1 + x; })")
|
||||
;=>(2 3 4)
|
||||
|
||||
(pike-eval "throw(upper_case(\"aaa\" + \"bbb\"))")
|
||||
;/Error: AAABBB
|
||||
|
||||
(pike-eval "(lambda() { int a = 5; int b = a * 3; return a + b; })()")
|
||||
;=>20
|
Loading…
Reference in New Issue
Block a user