/* Tuplr: A programming language * Copyright (C) 2008-2009 David Robillard * * Tuplr is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * Tuplr is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General * Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Tuplr. If not, see . */ /** @file * @brief Parsing (build an AST from a SExp) */ #include "tuplr.hpp" using namespace std; /*************************************************************************** * Macro Functions * ***************************************************************************/ inline SExp macDef(PEnv& penv, const SExp& exp) { SExp::const_iterator i = exp.begin(); THROW_IF(i == exp.end(), exp.loc, "Unexpected end of `def' macro call"); const SExp& name = *(++i); THROW_IF(i == exp.end(), name.loc, "Unexpected end of `def' macro call"); if (name.type == SExp::ATOM) { return exp; } else { // (def (f x) y) => (def f (fn (x) y)) SExp argsExp(exp.loc); SExp::const_iterator j = name.begin(); for (++j; j != name.end(); ++j) argsExp.push_back(*j); const SExp& body = *(++i); SExp fnExp(body.loc); fnExp.push_back(SExp(body.loc, new AString(body.loc, "fn"))); fnExp.push_back(argsExp); for (; i != exp.end(); ++i) fnExp.push_back(*i); SExp ret(exp.loc); ret.push_back(exp.front()); ret.push_back(name.front()); ret.push_back(fnExp); return ret; } } /*************************************************************************** * Parser Functions * ***************************************************************************/ template inline AST* parseCall(PEnv& penv, const SExp& exp, void* arg) { return new C(exp, penv.parseTuple(exp)); } template inline AST* parseLiteral(PEnv& penv, const SExp& exp, void* arg) { return new ALiteral(*reinterpret_cast(arg), exp.loc); } inline AST* parseFn(PEnv& penv, const SExp& exp, void* arg) { SExp::const_iterator a = exp.begin(); THROW_IF(++a == exp.end(), exp.loc, "Unexpected end of `fn' form"); AFn* ret = tup(exp.loc, penv.sym("fn"), new ATuple(penv.parseTuple(*a++)), 0); while (a != exp.end()) ret->push_back(penv.parse(*a++)); return ret; } /*************************************************************************** * Language Definition * ***************************************************************************/ /// Set up language void initLang(PEnv& penv, TEnv& tenv) { // Types tenv.def(penv.sym("Nothing"), make_pair((AST*)0, new AType(penv.sym("Nothing")))); tenv.def(penv.sym("Bool"), make_pair((AST*)0, new AType(penv.sym("Bool")))); tenv.def(penv.sym("Int"), make_pair((AST*)0, new AType(penv.sym("Int")))); tenv.def(penv.sym("Float"), make_pair((AST*)0, new AType(penv.sym("Float")))); // Literals static bool trueVal = true; static bool falseVal = false; penv.reg(false, "#t", PEnv::Handler(parseLiteral, &trueVal)); penv.reg(false, "#f", PEnv::Handler(parseLiteral, &falseVal)); // Macros penv.defmac("def", macDef); // Special forms penv.reg(true, "fn", PEnv::Handler(parseFn)); penv.reg(true, "if", PEnv::Handler(parseCall)); penv.reg(true, "def", PEnv::Handler(parseCall)); // Numeric primitives penv.reg(true, "+", PEnv::Handler(parseCall)); penv.reg(true, "-", PEnv::Handler(parseCall)); penv.reg(true, "*", PEnv::Handler(parseCall)); penv.reg(true, "/", PEnv::Handler(parseCall)); penv.reg(true, "%", PEnv::Handler(parseCall)); penv.reg(true, "and", PEnv::Handler(parseCall)); penv.reg(true, "or", PEnv::Handler(parseCall)); penv.reg(true, "xor", PEnv::Handler(parseCall)); penv.reg(true, "=", PEnv::Handler(parseCall)); penv.reg(true, "!=", PEnv::Handler(parseCall)); penv.reg(true, ">", PEnv::Handler(parseCall)); penv.reg(true, ">=", PEnv::Handler(parseCall)); penv.reg(true, "<", PEnv::Handler(parseCall)); penv.reg(true, "<=", PEnv::Handler(parseCall)); }