1
1
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:
Dov Murik 2019-09-18 08:49:33 +03:00
parent 953e0d74d1
commit a04e7a78d8
24 changed files with 2217 additions and 1 deletions

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
string readline(string prompt) {
write(prompt);
return Stdio.stdin->gets();
}

460
pike/Types.pmod Normal file
View 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
View File

@ -0,0 +1,2 @@
#!/bin/bash
exec pike $(dirname $0)/${STEP:-stepA_mal}.pike "${@}"

34
pike/step0_repl.pike Normal file
View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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