From abe7fec15a97f71f2ee1df15a3d85ce59a232dc6 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 5 Mar 2009 05:27:07 +0000 Subject: GCC style error reporting with line and column number. git-svn-id: http://svn.drobilla.net/resp/tuplr@40 ad02d1e2-f140-0410-9f75-f8b11f17cedd --- tuplr.cpp | 218 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 116 insertions(+), 102 deletions(-) (limited to 'tuplr.cpp') diff --git a/tuplr.cpp b/tuplr.cpp index c1cd43a..c4ee5d2 100644 --- a/tuplr.cpp +++ b/tuplr.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "llvm/Analysis/Verifier.h" #include "llvm/DerivedTypes.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" @@ -39,21 +40,32 @@ using namespace llvm; using namespace std; +using boost::format; + +struct Cursor { + Cursor(const string& n="", unsigned l=1, unsigned c=0) : name(n), line(l), col(c) {} + string str() const { return (format("%1%:%2%:%3%") % name % line % col).str(); } + string name; + unsigned line; + unsigned col; +}; -struct Error : public std::exception { - Error(const char* m) : msg(m) {} - const char* what() const throw() { return msg; } - const char* msg; +struct Error { + Error(const string& m, Cursor c=Cursor()) : msg(m), loc(c) {} + const string what() const throw() { return loc.str() + ": error: " + msg; } + string msg; + Cursor loc; }; template struct Exp { // ::= Atom | (Exp*) - Exp() : type(LIST) {} - Exp(const A& a) : type(ATOM), atom(a) {} + Exp(Cursor c) : loc(c), type(LIST) {} + Exp(Cursor c, const A& a) : loc(c), type(ATOM), atom(a) {} + Cursor loc; enum { ATOM, LIST } type; typedef std::vector< Exp > List; - A atom; - List list; + A atom; + List list; }; @@ -61,34 +73,38 @@ struct Exp { // ::= Atom | (Exp*) * S-Expression Lexer :: text -> S-Expressions (SExp) * ***************************************************************************/ -struct SyntaxError : public Error { SyntaxError(const char* m) : Error(m) {} }; typedef Exp SExp; static SExp -readExpression(std::istream& in) +readExpression(Cursor& loc, std::istream& in) { -#define PUSH(s, t) { if (t != "") { s.top().list.push_back(t); t = ""; } } -#define YIELD(s, t) { if (s.empty()) return t; else PUSH(s, t) } +#define PUSH(s, t) { if (t != "") { s.top().list.push_back(SExp(loc, t)); t = ""; } } +#define YIELD(s, t) { if (s.empty()) return SExp(loc, t); else PUSH(s, t) } stack stk; string tok; while (char ch = in.get()) { + ++loc.col; switch (ch) { case EOF: - return SExp(); - case ' ': case '\t': case '\n': + if (!stk.empty()) throw Error("unexpected end of file", loc); + return SExp(loc); + case '\n': + ++loc.line; + loc.col = 0; + case ' ': case '\t': if (tok != "") YIELD(stk, tok); break; case '"': - do { tok.push_back(ch); } while ((ch = in.get()) != '"'); + do { tok.push_back(ch); ++loc.col; } while ((ch = in.get()) != '"'); YIELD(stk, tok + '"'); break; case '(': - stk.push(SExp()); + stk.push(SExp(loc)); break; case ')': switch (stk.size()) { case 0: - throw SyntaxError("Unexpected ')'"); + throw Error("unexpected `)'", loc); case 1: PUSH(stk, tok); return stk.top(); @@ -104,11 +120,11 @@ readExpression(std::istream& in) } } switch (stk.size()) { - case 0: return tok; + case 0: return SExp(loc, tok); case 1: return stk.top(); - default: throw SyntaxError("Missing ')'"); + default: throw Error("missing `)'", loc); } - return SExp(); + return SExp(loc); } @@ -139,7 +155,7 @@ struct ASTLiteral : public AST { const ASTLiteral* r = dynamic_cast*>(&rhs); return r && val == r->val; } - string str() const { ostringstream s; s << val; return s.str(); } + string str() const { return (format("%1%") % val).str(); } void constrain(TEnv& tenv) const; Value* compile(CEnv& cenv); const VT val; @@ -193,7 +209,7 @@ struct ASTTuple : public AST, public vector { bool isForm(const string& f) { return !empty() && at(0)->str() == f; } bool contains(AST* child) const; void constrain(TEnv& tenv) const; - Value* compile(CEnv& cenv) { throw SyntaxError("Tuple compiled"); } + Value* compile(CEnv& cenv) { throw Error("tuple compiled"); } }; /// Type Expression, e.g. "(Int)" or "(Fn ((Int)) (Float))" @@ -205,7 +221,7 @@ struct AType : public ASTTuple { } string str() const { if (var) { - ostringstream s; s << "?" << id; return s.str(); + return (format("?%1%") % id).str(); } else { return ASTTuple::str(); } @@ -267,7 +283,7 @@ struct ASTClosure : public ASTTuple { ASTClosure(ASTTuple* p, AST* b, const string& n="") : ASTTuple(0, p, b), prot(p), func(0), name(n) {} bool operator==(const AST& rhs) const { return this == &rhs; } - string str() const { ostringstream s; s << this; return s.str(); } + string str() const { return (format("%1%") % this).str(); } void constrain(TEnv& tenv) const; void lift(CEnv& cenv); Value* compile(CEnv& cenv); @@ -279,15 +295,16 @@ private: /// Function call/application, e.g. "(func arg1 arg2)" struct ASTCall : public ASTTuple { - ASTCall(const ASTTuple& t) : ASTTuple(t) {} + ASTCall(const SExp& e, const ASTTuple& t) : ASTTuple(t), exp(e) {} void constrain(TEnv& tenv) const; void lift(CEnv& cenv); Value* compile(CEnv& cenv); + const SExp& exp; }; /// Definition special form, e.g. "(def x 2)" struct ASTDefinition : public ASTCall { - ASTDefinition(const ASTTuple& t) : ASTCall(t) {} + ASTDefinition(const SExp& e, const ASTTuple& t) : ASTCall(e, t) {} void constrain(TEnv& tenv) const; void lift(CEnv& cenv); Value* compile(CEnv& cenv); @@ -295,14 +312,14 @@ struct ASTDefinition : public ASTCall { /// Conditional special form, e.g. "(if cond thenexp elseexp)" struct ASTIf : public ASTCall { - ASTIf(const ASTTuple& t) : ASTCall(t) {} + ASTIf(const SExp& e, const ASTTuple& t) : ASTCall(e, t) {} void constrain(TEnv& tenv) const; Value* compile(CEnv& cenv); }; /// Primitive (builtin arithmetic function), e.g. "(+ 2 3)" struct ASTPrimitive : public ASTCall { - ASTPrimitive(const ASTTuple& t, int o, int a=0) : ASTCall(t), op(o), arg(a) {} + ASTPrimitive(const SExp& e, const ASTTuple& t, int o, int a=0) : ASTCall(e, t), op(o), arg(a) {} void constrain(TEnv& tenv) const; Value* compile(CEnv& cenv); unsigned op; @@ -311,12 +328,11 @@ struct ASTPrimitive : public ASTCall { /// Cons special form, e.g. "(cons 1 2)" struct ASTConsCall : public ASTCall { - ASTConsCall(const ASTTuple& t) : ASTCall(t), val(NULL) {} + ASTConsCall(const SExp& e, const ASTTuple& t) : ASTCall(e, t) {} AType* functionType(CEnv& cenv); void constrain(TEnv& tenv) const; void lift(CEnv& cenv); Value* compile(CEnv& cenv); - Value* val; static Funcs funcs; }; @@ -324,14 +340,14 @@ Funcs ASTConsCall::funcs; /// Car special form, e.g. "(car p)" struct ASTCarCall : public ASTCall { - ASTCarCall(const ASTTuple& t) : ASTCall(t) {} + ASTCarCall(const SExp& e, const ASTTuple& t) : ASTCall(e, t) {} void constrain(TEnv& tenv) const; Value* compile(CEnv& cenv); }; /// Cdr special form, e.g. "(cdr p)" struct ASTCdrCall : public ASTCall { - ASTCdrCall(const ASTTuple& t) : ASTCall(t) {} + ASTCdrCall(const SExp& e, const ASTTuple& t) : ASTCall(e, t) {} void constrain(TEnv& tenv) const; Value* compile(CEnv& cenv); }; @@ -348,7 +364,7 @@ typedef Op UD; // User Data argument for parse functions // Parse Time Environment (symbol table) struct PEnv : private map { - typedef AST* (*PF)(PEnv&, const SExp::List&, UD); // Parse Function + typedef AST* (*PF)(PEnv&, const SExp&, UD); // Parse Function struct Parser { Parser(PF f, UD d) : pf(f), ud(d) {} PF pf; UD ud; }; map parsers; void reg(const string& s, const Parser& p) { @@ -383,13 +399,13 @@ static AST* parseExpression(PEnv& penv, const SExp& exp) { if (exp.type == SExp::LIST) { - if (exp.list.empty()) throw SyntaxError("Call to empty list"); + if (exp.list.empty()) throw Error("call to empty list", exp.loc); if (exp.list.front().type == SExp::ATOM) { const PEnv::Parser* handler = penv.parser(exp.list.front().atom); if (handler) // Dispatch to parse function - return handler->pf(penv, exp.list, handler->ud); + return handler->pf(penv, exp, handler->ud); } - return new ASTCall(pmap(penv, exp.list)); // Parse as regular call + return new ASTCall(exp, pmap(penv, exp.list)); // Parse as regular call } else if (isdigit(exp.atom[0])) { if (exp.atom.find('.') == string::npos) return new ASTLiteral(strtol(exp.atom.c_str(), NULL, 10)); @@ -402,37 +418,37 @@ parseExpression(PEnv& penv, const SExp& exp) // Special forms static AST* -parseIf(PEnv& penv, const SExp::List& c, UD) - { return new ASTIf(pmap(penv, c)); } +parseIf(PEnv& penv, const SExp& exp, UD) + { return new ASTIf(exp, pmap(penv, exp.list)); } static AST* -parseDef(PEnv& penv, const SExp::List& c, UD) - { return new ASTDefinition(pmap(penv, c)); } +parseDef(PEnv& penv, const SExp& exp, UD) + { return new ASTDefinition(exp, pmap(penv, exp.list)); } static AST* -parsePrim(PEnv& penv, const SExp::List& c, UD data) - { return new ASTPrimitive(pmap(penv, c), data.op, data.arg); } +parsePrim(PEnv& penv, const SExp& exp, UD data) + { return new ASTPrimitive(exp, pmap(penv, exp.list), data.op, data.arg); } static AST* -parseFn(PEnv& penv, const SExp::List& c, UD) +parseFn(PEnv& penv, const SExp& exp, UD) { - SExp::List::const_iterator a = c.begin(); ++a; + SExp::List::const_iterator a = exp.list.begin(); ++a; return new ASTClosure( new ASTTuple(pmap(penv, (*a++).list)), parseExpression(penv, *a++)); } static AST* -parseCons(PEnv& penv, const SExp::List& c, UD) - { return new ASTConsCall(pmap(penv, c)); } +parseCons(PEnv& penv, const SExp& exp, UD) + { return new ASTConsCall(exp, pmap(penv, exp.list)); } static AST* -parseCar(PEnv& penv, const SExp::List& c, UD) - { return new ASTCarCall(pmap(penv, c)); } +parseCar(PEnv& penv, const SExp& exp, UD) + { return new ASTCarCall(exp, pmap(penv, exp.list)); } static AST* -parseCdr(PEnv& penv, const SExp::List& c, UD) - { return new ASTCdrCall(pmap(penv, c)); } +parseCdr(PEnv& penv, const SExp& exp, UD) + { return new ASTCdrCall(exp, pmap(penv, exp.list)); } /*************************************************************************** @@ -447,7 +463,7 @@ struct Env : public list< map > { const V& def(const K& k, const V& v) { typename Frame::iterator existing = this->front().find(k); if (existing != this->front().end() && existing->second != v) - throw SyntaxError("Redefinition"); + throw Error("redefinition"); return (this->front()[k] = v); } V* ref(const K& name) { @@ -464,8 +480,6 @@ struct Env : public list< map > { * Typing * ***************************************************************************/ -struct TypeError : public Error { TypeError (const char* m) : Error(m) {} }; - struct TSubst : public map { TSubst(AType* s=0, AType* t=0) { if (s && t) insert(make_pair(s, t)); } }; @@ -482,7 +496,7 @@ struct TEnv { } AType* named(const string& name) const { Types::const_iterator i = namedTypes.find(penv.sym(name)); - if (i == namedTypes.end()) throw TypeError("Unknown named type"); + if (i == namedTypes.end()) throw Error("Unknown named type"); return i->second; } void name(const string& name, const Type* type) { @@ -539,9 +553,9 @@ void ASTDefinition::constrain(TEnv& tenv) const { if (size() != 3) - throw SyntaxError("\"def\" not passed 2 arguments"); + throw Error("`def' requires exactly 2 arguments", exp.loc); if (!dynamic_cast(at(1))) - throw SyntaxError("\"def\" name is not a symbol"); + throw Error("`def' name is not a symbol", exp.loc); FOREACH(const_iterator, p, *this) (*p)->constrain(tenv); AType* tvar = tenv.type(this); @@ -566,16 +580,18 @@ ASTPrimitive::constrain(TEnv& tenv) const FOREACH(const_iterator, p, *this) (*p)->constrain(tenv); if (OP_IS_A(op, Instruction::BinaryOps)) { - if (size() <= 1) throw SyntaxError("Primitive call with 0 args"); + if (size() <= 2) throw Error((format("`%1%' requires at least 2 arguments") + % at(0)->str()).str(), exp.loc); AType* tvar = tenv.type(this); for (size_t i = 1; i < size(); ++i) tenv.constrain(at(i), tvar); } else if (op == Instruction::ICmp) { - if (size() != 3) throw SyntaxError("Comparison call with != 2 args"); + if (size() != 3) throw Error((format("`%1%' requires exactly 2 arguments") + % at(0)->str()).str(), exp.loc); tenv.constrain(at(1), tenv.type(at(2))); tenv.constrain(this, tenv.named("Bool")); } else { - throw TypeError("Unknown primitive"); + throw Error((format("unknown primitive `%1%'") % at(0)->str()).str(), exp.loc); } } @@ -687,7 +703,7 @@ TEnv::unify(const Constraints& constraints) // TAPL 22.4 cp.push_back(make_pair(s2, t2)); return unify(cp); } else { - throw TypeError("Type unification failed"); + throw Error("Type unification failed"); } } @@ -705,8 +721,6 @@ TEnv::apply(const TSubst& substs) * Code Generation * ***************************************************************************/ -struct CompileError : public Error { CompileError(const char* m) : Error(m) {} }; - class PEnv; /// Compile-Time Environment @@ -721,9 +735,7 @@ struct CEnv { opt.add(createGVNPass()); // Eliminate Common Subexpressions opt.add(createCFGSimplificationPass()); // Simplify control flow } - string gensym(const char* base="_") { - ostringstream s; s << base << symID++; return s.str(); - } + string gensym(const char* s="_") { return (format("%1%%2%") % s % symID++).str(); } void push() { code.push_front(); vals.push_front(); } void pop() { code.pop_front(); vals.pop_front(); } Value* compile(AST* obj) { @@ -769,17 +781,17 @@ compileFunction(CEnv& cenv, const std::string& name, const Type* retT, ASTTuple& vector cprot; for (size_t i = 0; i < prot.size(); ++i) { AType* at = cenv.tenv.type(prot.at(i)); - if (!at->type() || at->var) throw CompileError("Parameter is untyped"); + if (!at->type() || at->var) throw Error("function parameter is untyped"); cprot.push_back(at->type()); } - if (!retT) throw CompileError("Return is untyped"); + if (!retT) throw Error("function return is untyped"); FunctionType* fT = FunctionType::get(retT, cprot, false); Function* f = Function::Create(fT, linkage, name, cenv.module); if (f->getName() != name) { f->eraseFromParent(); - throw CompileError("Function redefined"); + throw Error("function redefined"); } // Set argument names in generated code @@ -798,7 +810,7 @@ Value* ASTSymbol::compile(CEnv& cenv) { AST** c = cenv.code.ref(this); - if (!c) throw SyntaxError((string("Undefined symbol: ") + cppstr).c_str()); + if (!c) throw Error((string("Undefined symbol: ") + cppstr).c_str()); return cenv.compile(*c); } @@ -806,10 +818,10 @@ void ASTClosure::lift(CEnv& cenv) { if (cenv.tenv.type(at(2))->var) - throw CompileError("Closure with untyped body lifted"); + throw Error("Closure with untyped body lifted"); for (size_t i = 0; i < prot->size(); ++i) if (cenv.tenv.type(prot->at(i))->var) - throw CompileError("Closure with untyped parameter lifted"); + throw Error("Closure with untyped parameter lifted"); assert(!func); cenv.push(); @@ -833,7 +845,7 @@ ASTClosure::lift(CEnv& cenv) cenv.builder.CreateRet(retVal); // Finish function cenv.optimise(*f); func = f; - } catch (exception e) { + } catch (Error& e) { f->eraseFromParent(); // Error reading body, remove function throw e; } @@ -866,8 +878,10 @@ ASTCall::lift(CEnv& cenv) // Extend environment with bound and typed parameters cenv.push(); - if (c->prot->size() != size() - 1) - throw CompileError("Call to closure with mismatched arguments"); + if (c->prot->size() < size() - 1) + throw Error((format("too many arguments to function `%1%'") % at(0)->str()).str(), exp.loc); + if (c->prot->size() > size() - 1) + throw Error((format("too few arguments to function `%1%'") % at(0)->str()).str(), exp.loc); for (size_t i = 1; i < size(); ++i) cenv.code.def(c->prot->at(i-1), at(i)); @@ -887,7 +901,7 @@ ASTCall::compile(CEnv& cenv) assert(c); Function* f = dynamic_cast(cenv.compile(c)); - if (!f) throw CompileError("Callee failed to compile"); + if (!f) throw Error("callee failed to compile", exp.loc); vector params(size() - 1); for (size_t i = 1; i < size(); ++i) @@ -899,6 +913,8 @@ ASTCall::compile(CEnv& cenv) void ASTDefinition::lift(CEnv& cenv) { + if (cenv.code.ref((ASTSymbol*)at(1))) + throw Error(string("`") + at(1)->str() + "' redefined", exp.loc); cenv.code.def((ASTSymbol*)at(1), at(2)); // Define first for recursion at(2)->lift(cenv); } @@ -960,7 +976,6 @@ ASTIf::compile(CEnv& cenv) Value* ASTPrimitive::compile(CEnv& cenv) { - if (size() < 3) throw SyntaxError("Too few arguments"); Value* a = cenv.compile(at(1)); Value* b = cenv.compile(at(2)); @@ -985,12 +1000,12 @@ ASTPrimitive::compile(CEnv& cenv) case CmpInst::ICMP_SGE: arg = CmpInst::FCMP_OGE; break; case CmpInst::ICMP_SLT: arg = CmpInst::FCMP_OLT; break; case CmpInst::ICMP_SLE: arg = CmpInst::FCMP_OLE; break; - default: throw CompileError("Unknown primitive"); + default: throw Error("Unknown primitive", exp.loc); } return cenv.builder.CreateFCmp((CmpInst::Predicate)arg, a, b); } } - throw CompileError("Unknown primitive"); + throw Error("Unknown primitive", exp.loc); } AType* @@ -1052,15 +1067,11 @@ ASTConsCall::lift(CEnv& cenv) Value* ASTConsCall::compile(CEnv& cenv) { - if (val != NULL) - return val; - vector params(size() - 1); for (size_t i = 1; i < size(); ++i) params[i-1] = cenv.compile(at(i)); - val = cenv.builder.CreateCall(funcs.find(functionType(cenv)), params.begin(), params.end()); - return val; + return cenv.builder.CreateCall(funcs.find(functionType(cenv)), params.begin(), params.end()); } Value* @@ -1104,31 +1115,33 @@ call(AType* retT, void* fp) } int -eval(CEnv& cenv, ExecutionEngine* engine, istream& is) +eval(CEnv& cenv, ExecutionEngine* engine, const string& name, istream& is) { AST* result = NULL; AType* resultType = NULL; list< pair > exprs; - while (true) { - SExp exp = readExpression(is); - if (exp.type == SExp::LIST && exp.list.empty()) - break; + Cursor cursor(name); + try { + while (true) { + SExp exp = readExpression(cursor, is); + if (exp.type == SExp::LIST && exp.list.empty()) + break; - try { result = parseExpression(cenv.penv, exp); // Parse input result->constrain(cenv.tenv); // Constrain types cenv.tenv.solve(); // Solve and apply type constraints resultType = cenv.tenv.type(result); - if (!resultType) throw TypeError("Call to untyped body"); result->lift(cenv); // Lift functions exprs.push_back(make_pair(exp, result)); - } catch (Error e) { - std::cerr << "Error: " << e.what() << endl; } + + if (!resultType || resultType->var) throw Error("body is undefined/untyped", cursor); + + } catch (Error& e) { + std::cerr << e.what() << endl; + return 1; } - if (resultType->var) throw TypeError("Call to variable typed body"); - // Create function for top-level of program ASTTuple prot; const Type* ctype = resultType->type(); @@ -1157,7 +1170,8 @@ repl(CEnv& cenv, ExecutionEngine* engine) while (1) { std::cout << "() "; std::cout.flush(); - SExp exp = readExpression(std::cin); + Cursor cursor("(stdin)"); + SExp exp = readExpression(cursor, std::cin); if (exp.type == SExp::LIST && exp.list.empty()) break; @@ -1167,8 +1181,8 @@ repl(CEnv& cenv, ExecutionEngine* engine) cenv.tenv.solve(); // Solve and apply type constraints AType* bodyT = cenv.tenv.type(body); - if (!bodyT) throw TypeError("REPL call to untyped body"); - if (bodyT->var) throw TypeError("REPL call to variable typed body"); + if (!bodyT) throw Error("call to untyped body", cursor); + if (bodyT->var) throw Error("call to variable typed body", cursor); body->lift(cenv); @@ -1182,7 +1196,7 @@ repl(CEnv& cenv, ExecutionEngine* engine) Value* retVal = cenv.compile(body); cenv.builder.CreateRet(retVal); // Finish function cenv.optimise(*f); - } catch (SyntaxError e) { + } catch (Error& e) { f->eraseFromParent(); // Error reading body, remove function throw e; } @@ -1193,8 +1207,8 @@ repl(CEnv& cenv, ExecutionEngine* engine) } std::cout << " : " << cenv.tenv.type(body)->str() << endl; - } catch (Error e) { - std::cerr << "Error: " << e.what() << endl; + } catch (Error& e) { + std::cerr << e.what() << endl; } } @@ -1245,10 +1259,10 @@ main(int argc, char** argv) int ret; if (argc > 2 && !strncmp(argv[1], "-e", 3)) { std::istringstream is(argv[2]); - ret = eval(cenv, engine, is); + ret = eval(cenv, engine, "(command line)", is); } else if (argc > 2 && !strncmp(argv[1], "-f", 3)) { std::ifstream is(argv[2]); - ret = eval(cenv, engine, is); + ret = eval(cenv, engine, argv[2], is); is.close(); } else { ret = repl(cenv, engine); -- cgit v1.2.1