Created
December 22, 2014 11:02
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*- | |
* Copyright (c) 2014 Katsuyuki Tateishi <kt@wheel.jp> | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
* SUCH DAMAGE. | |
* | |
*/ | |
#define dbg(...) fprintf(stderr,__VA_ARGS__) | |
#define dpr(x) cerr<<"*DBG: "<<#x<<": "<<x<<endl; | |
#define dprc(c) do{cerr<<#c<<":";for(auto&_i:(c)){cerr<<" "<<_i;}cerr<<endl;}while(0) | |
#include <bits/stdc++.h> | |
using namespace std; | |
typedef pair<int, int> pii; | |
typedef vector<int> vi; | |
typedef vector<vi> vvi; | |
int INF = 1e9+7; | |
#define all(c) begin(c), end(c) | |
#define tr(i,c) for(auto i=begin(c);i!=end(c);i++) | |
#define rtr(i,c) for(auto i=(c).rbegin();i!=(c).rend();i++) | |
#define rep(i,b) for(auto i=0;i<(b);i++) | |
#define pb push_back | |
#define sz(c) int((c).size()) | |
class LObj { | |
public: | |
virtual ~LObj() {} | |
virtual string str() const = 0; | |
template <typename T> T *rawptr() { | |
return dynamic_cast<T *>(this); | |
} | |
template <typename T> bool eqtype() { | |
return typeid(T) == typeid(*this); | |
} | |
template <typename T> bool isa() { | |
return dynamic_cast<T *>(this) != nullptr; | |
} | |
}; | |
typedef shared_ptr<LObj> lptr; | |
typedef function<lptr(const lptr&)> primfunc_t; | |
typedef function<lptr(const lptr&, const lptr&)> specialform_t; | |
lptr eval(const lptr& expr, const lptr& env); | |
bool eq(const lptr& l, const lptr& r) { | |
return l.get() == r.get(); | |
} | |
template <typename T> | |
bool eqtype(const lptr& p) { | |
return typeid(T) == typeid(*p); | |
} | |
template <typename T> | |
T *rawptr(const lptr& p) { | |
return dynamic_cast<T *>(p.get()); | |
} | |
template <typename T> | |
bool isa(const lptr& p) { | |
return rawptr<T>(p) != nullptr; | |
} | |
class True : virtual public LObj { | |
public: | |
virtual string str() const { return "T"; } | |
}; | |
const lptr THE_T(new True()); | |
class Error : public True { | |
private: | |
string name; | |
public: | |
Error(const string& s) : name(s) {} | |
virtual string str() const { | |
return "#<Error: " + name + ">"; | |
} | |
}; | |
lptr makeError(const string& s) { | |
return lptr(new Error(s)); | |
} | |
class List : virtual public LObj { | |
public: | |
}; | |
bool isList(const lptr& p) { return isa<List>(p); } | |
class Nil : public List { | |
public: | |
Nil() {} | |
virtual string str() const { return string("NIL"); } | |
}; | |
const lptr THE_NIL(new Nil()); | |
bool isNIL(const lptr& p) { return eq(p, THE_NIL); } | |
class Cons : public True, public List { | |
private: | |
lptr car; | |
lptr cdr; | |
public: | |
Cons(const lptr& a, const lptr& d) : car(a), cdr(d) {} | |
lptr getcar() const { return car; } | |
lptr getcdr() const { return cdr; } | |
void setcar(const lptr& val) { car = val; } | |
void setcdr(const lptr& val) { cdr = val; } | |
virtual string str() const { | |
return string("(" + car->str() + " . " + cdr->str() + ")"); | |
} | |
}; | |
bool isCons(const lptr& p) { | |
return eqtype<Cons>(p); | |
} | |
lptr makeCons(const lptr& x, const lptr& y) { | |
return lptr(new Cons(x, y)); | |
} | |
lptr prmcar(const lptr& p) { | |
return p->rawptr<Cons>()->getcar(); | |
} | |
lptr prmcdr(const lptr& p) { | |
return p->rawptr<Cons>()->getcdr(); | |
} | |
lptr prmcadr(const lptr& p) { | |
return prmcar(prmcdr(p)); | |
} | |
lptr prmcons(const lptr& args) { | |
return makeCons(prmcar(args), prmcadr(args)); | |
} | |
lptr set_car(const lptr& c, const lptr& val) { | |
c->rawptr<Cons>()->setcar(val); | |
return c; | |
} | |
lptr prmset_car(const lptr& args) { | |
return set_car(prmcar(args), prmcadr(args)); | |
} | |
lptr set_cdr(const lptr& c, const lptr& val) { | |
c->rawptr<Cons>()->setcdr(val); | |
return c; | |
} | |
lptr prmset_cdr(const lptr& args) { | |
return set_cdr(prmcar(args), prmcadr(args)); | |
} | |
lptr prmnreverse(const lptr& p) { | |
lptr acc, lst, head, rest; | |
acc = THE_NIL; | |
lst = p; | |
while (!isNIL(lst)) { | |
rest = prmcdr(lst); | |
acc = set_cdr(lst, acc); | |
lst = rest; | |
} | |
return acc; | |
} | |
bool isAtom(const lptr& p) { | |
return isNIL(p) || !isList(p); | |
} | |
class String : public True { | |
private: | |
string val; | |
public: | |
String(const string& s) : val(s) {} | |
virtual string str() const { | |
return val; | |
} | |
}; | |
lptr makeString(const string& s) { | |
return lptr(new String(s)); | |
} | |
class Fixnum : public True { | |
private: | |
int val; | |
public: | |
Fixnum(const string& s) { | |
val = stoi(s); | |
} | |
Fixnum(int i) { | |
val = i; | |
} | |
int value() { | |
return val; | |
} | |
virtual string str() const { | |
return to_string(val); | |
} | |
}; | |
lptr makeFixnum(const string& s) { | |
return lptr(new Fixnum(s)); | |
} | |
class Symbol : public True { | |
private: | |
string name; | |
public: | |
Symbol(string str) { | |
name = str; | |
} | |
virtual string str() const { | |
return name; | |
} | |
}; | |
bool isSymbol(const lptr& p) { | |
return eqtype<Symbol>(p); | |
} | |
unordered_map<string, const lptr> THE_SYMBOL_TABLE; | |
lptr makeSymbol(const string& s) { | |
return lptr(new Symbol(s)); | |
} | |
lptr getSymbol(const string& s) { | |
auto got = THE_SYMBOL_TABLE.find(s); | |
if (got != end(THE_SYMBOL_TABLE)) { | |
return got->second; | |
} else { | |
lptr res = makeSymbol(s); | |
THE_SYMBOL_TABLE.emplace(s, res); | |
return res; | |
} | |
} | |
class Env : public True { | |
private: | |
unordered_map<string, const lptr> hash; | |
lptr parent; | |
public: | |
Env() : parent(THE_NIL) {} | |
Env(const lptr& p) : parent(p) {} | |
lptr get(const string& s) { | |
auto ret = hash.find(s); | |
if (ret != end(hash)) { | |
return ret->second; | |
} else if (!isNIL(parent)) { | |
return parent->rawptr<Env>()->get(s); | |
} else { | |
return makeError("Undefined symbol: " + s); | |
} | |
} | |
lptr get(const lptr& sym) { | |
return get(sym->rawptr<Symbol>()->str()); | |
} | |
lptr set(const string& s, const lptr& val) { | |
auto ret = hash.find(s); | |
if (ret != end(hash)) { | |
hash.emplace(s, val); | |
return val; | |
} else if (!isNIL(parent)) { | |
return parent->rawptr<Env>()->set(s, val); | |
} else { | |
return makeError("Undefined symbol: " + s); | |
} | |
} | |
lptr set(const lptr& sym, const lptr& val) { | |
return set(sym->rawptr<Symbol>()->str(), val); | |
} | |
lptr define(const string& s, const lptr& val) { | |
hash.emplace(s, val); | |
return val; | |
} | |
lptr define(const lptr& sym, const lptr& val) { | |
return define(sym->rawptr<Symbol>()->str(), val); | |
} | |
virtual string str() const { | |
return string("#<Environment >"); | |
} | |
}; | |
template <typename T> | |
lptr envget(const lptr& env, const T& sym) { | |
return rawptr<Env>(env)->get(sym); | |
} | |
template <typename T> | |
lptr envset(const lptr& env, const T& sym, const lptr& val) { | |
return rawptr<Env>(env)->set(sym, val); | |
} | |
template <typename T> | |
lptr envdefine(const lptr& env, const T& sym, const lptr& val) { | |
return rawptr<Env>(env)->define(sym, val); | |
} | |
lptr makeEnv(const lptr& parent) { | |
return lptr(new Env(parent)); | |
} | |
lptr THE_ENVIRONMENT(new Env()); | |
class Proc : public True { | |
private: | |
string name; | |
public: | |
Proc(const string& s) : name(s) {} | |
string getname() const { return name; } | |
void setname(const string& s) { | |
name = s; | |
} | |
virtual string str() const { | |
return string("#<Procedure " + name + ">"); | |
} | |
virtual lptr apply(const lptr& values) const = 0; | |
}; | |
bool isProc(const lptr& p) { | |
return isa<Proc>(p); | |
} | |
bool isProcForm(const lptr& expr) { | |
return isCons(expr) && isProc(prmcar(expr)); | |
} | |
class PrimitiveProc : public Proc { | |
private: | |
primfunc_t primproc; | |
public: | |
PrimitiveProc(const string& s, primfunc_t f) : Proc(s), primproc(f) {}; | |
lptr apply(const lptr& values) const { | |
return primproc(values); | |
} | |
}; | |
lptr makePrimitiveProc(const string& name, primfunc_t f) { | |
return lptr(new PrimitiveProc(name, f)); | |
} | |
lptr sf_begin(const lptr& args, const lptr& env); | |
class CompoundProc : public Proc { | |
private: | |
lptr args; | |
lptr body; | |
lptr env; | |
public: | |
CompoundProc(const string& s, const lptr& a, const lptr& b, const lptr& e) : | |
Proc(s), args(a), body(b), env(e) {} | |
lptr setupenv(const lptr& env, const lptr& args, const lptr& values) const { | |
if (isNIL(args) && isNIL(values)) return THE_NIL; | |
if (isNIL(args)) return makeError("Applying Procedure: Too much arguments"); | |
if (isNIL(values)) return makeError("Applying Procedure: Too few arguments"); | |
if (args->eqtype<Cons>()) { | |
envdefine(env, prmcar(args), prmcar(values)); | |
return setupenv(env, prmcdr(args), prmcdr(values)); | |
} else { | |
envdefine(env, args, values); | |
return THE_NIL; | |
} | |
} | |
lptr apply(const lptr& values) const { | |
lptr newenv = makeEnv(env); | |
lptr status = setupenv(newenv, args, values); | |
if (status->isa<Error>()) { | |
return status; | |
} else { | |
return sf_begin(body, newenv); | |
return eval(body, newenv); | |
} | |
} | |
}; | |
lptr makeCompoundProc(const string& name, const lptr& args, | |
const lptr& body, const lptr& env) { | |
return lptr(new CompoundProc(name, args, body, env)); | |
} | |
lptr prm_plus(const lptr& args) { | |
lptr rest; | |
int res = 0; | |
for (rest = args; !isNIL(rest); rest = prmcdr(rest)) { | |
Fixnum *tmp = rawptr<Fixnum>(prmcar(rest)); | |
res += tmp->value(); | |
} | |
return lptr(new Fixnum(res)); | |
} | |
lptr prm_multiply(const lptr& args) { | |
lptr rest; | |
int res = 1; | |
for (rest = args; !isNIL(rest); rest = prmcdr(rest)) { | |
Fixnum *tmp = rawptr<Fixnum>(prmcar(rest)); | |
res *= tmp->value(); | |
} | |
return lptr(new Fixnum(res)); | |
} | |
/* | |
* Syntax | |
*/ | |
class Syntax : public True { | |
private: | |
string name; | |
public: | |
Syntax(const string& s) : name(s) {} | |
string getname() const { return name; } | |
void setname(const string& s) { | |
name = s; | |
} | |
virtual string str() const { | |
return string("#<Syntax " + name + ">"); | |
} | |
virtual lptr eval_syntax(const lptr& expr, const lptr& env) const = 0; | |
}; | |
bool isSyntax(const lptr& p) { | |
return isa<Syntax>(p); | |
} | |
bool isSyntaxForm(const lptr& expr) { | |
return isCons(expr) && isSyntax(prmcar(expr)); | |
} | |
class SpecialForm : public Syntax { | |
private: | |
specialform_t sf; | |
public: | |
SpecialForm(const string& s, specialform_t f) : Syntax(s), sf(f) {} | |
lptr eval_syntax(const lptr& expr, const lptr& env) const { | |
return sf(expr, env); | |
} | |
}; | |
lptr makeSpecialForm(const string& s, specialform_t f) { | |
return lptr(new SpecialForm(s, f)); | |
} | |
lptr sf_begin(const lptr& args, const lptr& env) { | |
lptr clause, lst, ret; | |
ret = THE_NIL; | |
for (lst = args; !isNIL(lst); lst = prmcdr(lst)) { | |
ret = eval(prmcar(lst), env); | |
} | |
return ret; | |
} | |
lptr sf_cond(const lptr& args, const lptr& env) { | |
lptr clause, rest, cond; | |
for (rest = args; !isNIL(rest); rest = prmcdr(rest)) { | |
clause = prmcar(rest); | |
cond = eval(prmcar(clause), env); | |
if (isNIL(cond)) continue; | |
return sf_begin(prmcdr(clause), env); | |
} | |
return THE_NIL; | |
} | |
lptr sf_quote(const lptr& args, const lptr& env) { | |
return args; | |
} | |
lptr sf_lambda(const lptr& args, const lptr& env) { | |
return makeCompoundProc("Anonymous", prmcar(args), prmcdr(args), env); | |
} | |
/* | |
* Evaluator | |
*/ | |
lptr eval_symbol(const lptr& sym, const lptr& env) { | |
return envget(env, sym); | |
} | |
lptr eval_apply_values(const lptr& expr, const lptr& env) { | |
lptr acc, rest, tmp; | |
for (acc = THE_NIL, rest = expr; !isNIL(rest); rest = prmcdr(rest)) { | |
tmp = eval(prmcar(rest), env); | |
if (eqtype<Error>(tmp)) return tmp; | |
acc = makeCons(tmp, acc); | |
} | |
//return acc; | |
return prmnreverse(acc); | |
} | |
lptr eval(const lptr& expr, const lptr& env) { | |
if (isAtom(expr)) { | |
if (isSymbol(expr)) return eval_symbol(expr, env); | |
else return expr; | |
} else { | |
lptr car = eval(prmcar(expr), env); | |
if (isa<Syntax>(car)) { | |
return rawptr<Syntax>(car)->eval_syntax(prmcdr(expr), env); | |
} else if (isa<Proc>(car)) { | |
lptr av = eval_apply_values(prmcdr(expr), env); | |
if (eqtype<Error>(av)) { | |
return av; | |
} else { | |
//dpr(av->str()); | |
return rawptr<Proc>(car)->apply(av); | |
} | |
} else { | |
return makeError("Cannot Evaluate: " + car->str()); | |
} | |
} | |
} | |
/* | |
* Reader | |
*/ | |
lptr reader(istream& is); | |
void read_skip_space(istream& is) { | |
char c; | |
while (is.get(c)) { | |
if (!isspace(c)) { | |
is.unget(); | |
break; | |
} | |
} | |
} | |
lptr read_list(istream& is) { | |
char c; | |
lptr acc = THE_NIL; | |
while (is.get(c)) { | |
if (c == ')') { | |
break; | |
} else { | |
is.unget(); | |
acc = makeCons(reader(is), acc); | |
} | |
} | |
return prmnreverse(acc); | |
} | |
lptr read_symbol(istream& is) { | |
char c; | |
string token; | |
bool fixnum = true; | |
while (is.get(c)) { | |
if (c == '(' || c == ')' || isspace(c)) { | |
is.unget(); | |
break; | |
} | |
fixnum = (fixnum && isdigit(c)); | |
token.push_back(toupper(c)); | |
} | |
if (fixnum) { | |
return makeFixnum(token); | |
} else { | |
return getSymbol(token); | |
} | |
} | |
lptr read_string(istream& is) { | |
char c; | |
string token; | |
while (is.get(c)) { | |
if (c == '\\') { | |
is.get(c); | |
} else if (c == '"') { | |
break; | |
} | |
token.push_back(c); | |
} | |
return makeString(token); | |
} | |
lptr read_quote(istream& is, char q) { | |
string quote; | |
switch (q) { | |
case '\'': | |
quote = "QUOTE"; | |
break; | |
case '`': | |
quote = "QUASIQUOTE"; | |
break; | |
case ',': | |
char c; | |
is.get(c); | |
if (c == '@') { | |
quote = "UNQUOTE-SPLICING"; | |
} else { | |
is.unget(); | |
quote = "UNQUOTE"; | |
} | |
break; | |
} | |
return makeCons(getSymbol(quote), reader(is)); | |
} | |
lptr reader(istream& is) { | |
char c; | |
read_skip_space(is); | |
if (is.get(c)) { | |
if (c == '(') { | |
return read_list(is); | |
} else if (c == ')') { | |
return makeError("READ: Additional close paren."); | |
} else if (c == '"') { | |
return read_string(is); | |
} else if (c == '\'' || c == '`' || c == ',') { | |
return read_quote(is, c); | |
} else { | |
is.unget(); | |
return read_symbol(is); | |
} | |
} | |
return makeError("READ: Recieve EOF."); | |
} | |
/* | |
* Printer | |
*/ | |
void printlptr(ostream& os, const lptr& p); | |
void printCons(ostream& os, const lptr& p) { | |
lptr x = prmcar(p); | |
lptr rest = prmcdr(p); | |
printlptr(os, x); | |
if (isNIL(rest)) { | |
return; | |
} else if (eqtype<Cons>(rest)) { | |
os << " "; | |
printCons(os, rest); | |
} else { | |
os << " . "; | |
printlptr(os, rest); | |
} | |
} | |
void printString(ostream& os, const lptr& p) { | |
for (auto& c:p->str()) { | |
if (c == '"') os << '\\'; | |
os << c; | |
} | |
} | |
void printlptr(ostream& os, const lptr& p) { | |
if (eqtype<Cons>(p)) { | |
os << "("; | |
printCons(os, p); | |
os << ")"; | |
} else if (eqtype<String>(p)) { | |
os << "\""; | |
printString(os, p); | |
os << "\""; | |
} else { | |
os << p->str(); | |
} | |
} | |
ostream& operator<<(ostream& os, const lptr& p) { | |
printlptr(os, p); | |
return os; | |
} | |
void register_specialform(const string& name, specialform_t sf) { | |
lptr sym = getSymbol(name); | |
envdefine(THE_ENVIRONMENT, sym, makeSpecialForm(name, sf)); | |
} | |
void register_primitive_proc(const string& name, primfunc_t f) { | |
lptr sym = getSymbol(name); | |
envdefine(THE_ENVIRONMENT, sym, makePrimitiveProc(name, f)); | |
} | |
void setup_specialforms() { | |
register_specialform("BEGIN", sf_begin); | |
register_specialform("COND", sf_cond); | |
register_specialform("QUOTE", sf_quote); | |
register_specialform("LAMBDA", sf_lambda); | |
} | |
void setup_primitive_procs() { | |
register_primitive_proc("CAR", prmcar); | |
register_primitive_proc("CDR", prmcdr); | |
register_primitive_proc("CADR", prmcadr); | |
register_primitive_proc("CONS", prmcons); | |
register_primitive_proc("SET-CAR!", prmset_car); | |
register_primitive_proc("SET-CDR!", prmset_cdr); | |
register_primitive_proc("REVERSE!", prmnreverse); | |
register_primitive_proc("+", prm_plus); | |
register_primitive_proc("*", prm_multiply); | |
} | |
void setup_self_evaluatings() { | |
lptr sym; | |
sym = getSymbol("NIL"); | |
envdefine(THE_ENVIRONMENT, sym, THE_NIL); | |
sym = getSymbol("T"); | |
envdefine(THE_ENVIRONMENT, sym, THE_T); | |
} | |
void setup() { | |
setup_self_evaluatings(); | |
setup_specialforms(); | |
setup_primitive_procs(); | |
} | |
int main(int argc, char **argv) { | |
setup(); | |
while (!cin.eof()) { | |
cout << " * "; | |
cout.flush(); | |
lptr val = reader(cin); | |
//dpr(val->str()); | |
//cout << "-> " << val << endl; | |
cout << "=> " << eval(val, THE_ENVIRONMENT) << endl; | |
} | |
/* | |
lptr x = lptr(new Fixnum(10)); | |
lptr y = lptr(new Fixnum(1)); | |
lptr z = lptr(new Fixnum(3)); | |
lptr a = lptr(new Fixnum(5)); | |
lptr b = lptr(new Fixnum(7)); | |
lptr c = makeCons(x, makeCons(y, makeCons(z, makeCons(a, makeCons(b, THE_NIL))))); | |
dpr(THE_NIL); | |
dpr(x); | |
dpr(x->eqtype<Fixnum>()); | |
dpr(eqtype<Fixnum>(x)); | |
dpr(eqtype<Cons>(x)); | |
cout << endl; | |
dpr(y); | |
cout << endl; | |
dpr(c); | |
c = prmnreverse(c); | |
dpr(c); | |
cout << endl; | |
lptr d = makeCons(x, y); | |
dpr(d); | |
dpr(prmcar(d)); | |
dpr(prmcdr(d)); | |
lptr tmp = prmcar(d); | |
set_car(d, prmcdr(d)); | |
set_cdr(d, tmp); | |
dpr(d); | |
cout << endl; | |
//dpr(prm_add_fixnum(x, y)); | |
cout << endl; | |
envdefine(THE_ENVIRONMENT, "LST", c); | |
dpr(envget(THE_ENVIRONMENT, "LST")); | |
dpr(envget(THE_ENVIRONMENT, "CAR")); | |
lptr op = getSymbol("CONS"); | |
lptr form = makeCons(op, makeCons(x, makeCons(b, THE_NIL))); | |
dpr(form); | |
dpr(eval(form, THE_ENVIRONMENT)); | |
*/ | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment