From 17cec530b0a4d792bde2d37583b6c26e89f9b1bd Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 13 Oct 2026 12:32:03 +0000 Subject: fix: distinguish compiler errors and report compilation termination Co-Authored-By: Claude Fable 5.1 --- AGENTS/ARCH.md | 9 ++++-- AGENTS/CONVENTIONS.md | 7 ++-- doc/cpsc.md | 6 ++-- lib/CodeGen/ArithmeticHandler.cc | 8 ++--- lib/CodeGen/CodeGen.cc | 3 +- lib/CodeGen/FunctionGen.cc | 20 ++++++------ lib/Lexer/Lexer.cc | 6 ++-- lib/Parser/Parser.cc | 64 ++++++++++++++++++------------------ lib/Parser/ParserFile.cc | 14 ++++---- lib/Parser/ParserFunction.cc | 36 ++++++++++----------- lib/Parser/ParserType.cc | 22 ++++++------- tools/driver/EmbeddedRunner.cc | 10 +++--- tools/driver/main.cc | 70 +++++++++++++++++++++------------------- 13 files changed, 145 insertions(+), 130 deletions(-) diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index 181c207..8245027 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -57,12 +57,17 @@ release because MSYS2 has no Pandoc package. Only the Linux build is verified. Driver behavior (`tools/driver/main.cc`): - One source file, with optional `-o path` and `-r`; options may precede or follow the source. - No stdin source input. No arguments exits silently with status 0 (help TODO). + No stdin source input. Missing input, including no arguments, prints + `cpsc: fatal error: no input files` and `compilation terminated.`, then returns 1. - IR is written only after successful parsing, code generation and module verification. Diagnostics stay on stderr; failures do not overwrite existing IR. - Lexer, Parser and CodeGen track error flags (`hadError()`); any error, including invalid IR from `verifyModule`, makes the driver return 1. File/argument errors - also return 1. Check the exit code rather than grepping stderr. + also return 1. Ordinary compiler errors use `error:`; failures that prevent + compilation, such as file I/O errors, use `fatal error:`. The driver prints + `compilation terminated.` once on compilation failure, including argument errors. + Execution-stage failures do not print that line. Check the exit code rather + than grepping stderr. - Successful compilation overwrites the output, but paths aliasing the source (including symlinks/hard links) are rejected. - In PATH mode, `-r` executes PATH's `lli` using LLVM Support process APIs, with diff --git a/AGENTS/CONVENTIONS.md b/AGENTS/CONVENTIONS.md index a49b381..638013a 100644 --- a/AGENTS/CONVENTIONS.md +++ b/AGENTS/CONVENTIONS.md @@ -92,12 +92,15 @@ scope stack. `FunctionGen` saves/clears/restores the table around a function bod `emitExpr` walk a linear if-chain of casts. **A statement type missing from the chain is silently ignored** — no error. Adding an AST node always means touching these chains. -**Error style everywhere: print an `Error:` line, set the component's error flag, and +**Compiler error style: print an `error:` line, set the component's error flag, and keep compiling.** No exceptions, no exit mid-compile. `Lexer`/`Parser`/`CodeGen` each expose `hadError()`; `CodeGen::reportError(fmt, ...)` is the helper for codegen-side diagnostics (`ArrayHandler` reaches it through its `CodeGen &` parameter, `FunctionGen`/`ArithmeticHandler` hold a `bool &HadError` reference). The driver exits -non-zero when any flag is set. The parser drops a failed statement and resyncs at the +non-zero when any flag is set, printing `compilation terminated.` once. Errors that +prevent compilation (missing input, file I/O or invalid IR) use `fatal error:`. +Execution-stage errors do not print the compilation termination line. +The parser drops a failed statement and resyncs at the next token that can start a statement or end a block (`syncToStatementStart`), then continues; the failure is always diagnosed and reflected in the exit code. diff --git a/doc/cpsc.md b/doc/cpsc.md index bfd54d0..98340aa 100644 --- a/doc/cpsc.md +++ b/doc/cpsc.md @@ -64,13 +64,15 @@ cpsc test.cps -r -o result.ir # EXIT STATUS **0** -: Compilation succeeded. Invoking `cpsc` with no arguments also exits silently with status 0. +: Compilation succeeded. **1** -: An argument, file, compilation or execution-engine error occurred, including when PATH mode cannot find `lli` or NONE mode receives **-r**. +: An argument, file, compilation or execution-engine error occurred, including missing input, when PATH mode cannot find `lli` or NONE mode receives **-r**. With **-r**, return the executed program's exit status. In PATH mode, failure to launch `lli` or a child-process crash returns 1. # NOTES Compiler diagnostics go to stderr. Failed compilation does not write IR. Successful compilation overwrites an existing output file, but an output path that refers to the source file (including a symbolic or hard link) is rejected. + +Missing input, including invoking `cpsc` without arguments, reports `cpsc: fatal error: no input files`. Ordinary lexical, syntax and semantic errors use `error:`; failures that prevent compilation, such as file I/O errors, use `fatal error:`. Compilation and argument failures end with one `compilation terminated.` line. Execution-stage failures do not print that line. diff --git a/lib/CodeGen/ArithmeticHandler.cc b/lib/CodeGen/ArithmeticHandler.cc index b7f0665..cd4e7b3 100644 --- a/lib/CodeGen/ArithmeticHandler.cc +++ b/lib/CodeGen/ArithmeticHandler.cc @@ -28,7 +28,7 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) if (LVal->getType()->isIntegerTy(64)) LVal = Builder.CreateSIToFP(LVal, Type::getDoubleTy(Context)); if (RVal->getType()->isIntegerTy(64)) RVal = Builder.CreateSIToFP(RVal, Type::getDoubleTy(Context)); if (!LVal->getType()->isDoubleTy() || !RVal->getType()->isDoubleTy()) { - fprintf(stderr, "Error: '/' requires numeric operands (line %d).\n", Line); + fprintf(stderr, "error: '/' requires numeric operands (line %d).\n", Line); HadError = true; return nullptr; } @@ -39,7 +39,7 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) if (Op == tok_div || Op == tok_mod) { if (!LIsInt || !RIsInt) { - fprintf(stderr, "Error: DIV and MOD operators require INTEGER operands (line %d).\n", Line); + fprintf(stderr, "error: DIV and MOD operators require INTEGER operands (line %d).\n", Line); HadError = true; return nullptr; } @@ -96,7 +96,7 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) R = widenSmallInt(Builder, Context, R); } if (L->getType() != R->getType()) { - fprintf(stderr, "Error: Mismatched operand types for binary operator (line %d).\n", Line); + fprintf(stderr, "error: Mismatched operand types for binary operator (line %d).\n", Line); HadError = true; return nullptr; } @@ -114,7 +114,7 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) } } - fprintf(stderr, "Error: Unsupported operand types for binary operator (line %d).\n", Line); + fprintf(stderr, "error: Unsupported operand types for binary operator (line %d).\n", Line); HadError = true; return nullptr; } diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index c0da7ee..f12c6ce 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -56,7 +56,7 @@ bool CodeGen::isBuiltinName(const std::string &Name) { void CodeGen::reportError(const char *Fmt, ...) { va_list Args; va_start(Args, Fmt); - fprintf(stderr, "Error: "); + fprintf(stderr, "error: "); vfprintf(stderr, Fmt, Args); fprintf(stderr, "\n"); va_end(Args); @@ -536,6 +536,7 @@ void CodeGen::compile(const std::vector> &Statements) { } if (verifyModule(*TheModule, &errs())) { + errs() << "fatal error: LLVM module verification failed\n"; HadError = true; } } diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 99c6a1e..2589797 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -24,7 +24,7 @@ static bool isReservedRuntimeName(const std::string &Name) { Type *FunctionGen::getLLVMType(const std::string &TypeName) { Type *Resolved = Types.getLLVMType(TypeName); if (!Resolved) { - fprintf(stderr, "Error: Unknown type %s\n", TypeName.c_str()); + fprintf(stderr, "error: Unknown type %s\n", TypeName.c_str()); HadError = true; return Type::getInt64Ty(Context); } @@ -35,11 +35,11 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vectorarg_begin(); for (const ParamDecl &P : Params) { if (Types.lookupEnumConstant(P.Name)) { - fprintf(stderr, "Error: parameter '%s' collides with an enum value\n", P.Name.c_str()); + fprintf(stderr, "error: parameter '%s' collides with an enum value\n", P.Name.c_str()); HadError = true; } if (Types.resolve(P.Name)) { - fprintf(stderr, "Error: parameter '%s' collides with a type name\n", P.Name.c_str()); + fprintf(stderr, "error: parameter '%s' collides with a type name\n", P.Name.c_str()); HadError = true; } Value *ArgVal = &(*AI); @@ -85,25 +85,25 @@ const FuncSig *FunctionGen::getSignature(const std::string &Name) const { Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { if (isReservedRuntimeName(Proto->getName())) { - fprintf(stderr, "Error: '%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name\n", + fprintf(stderr, "error: '%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name\n", Proto->getName().c_str()); HadError = true; return nullptr; } if (CodeGen::isBuiltinName(Proto->getName())) { - fprintf(stderr, "Error: '%s' is a built-in function and cannot be redefined\n", + fprintf(stderr, "error: '%s' is a built-in function and cannot be redefined\n", Proto->getName().c_str()); HadError = true; return nullptr; } if (Types.lookupEnumConstant(Proto->getName())) { - fprintf(stderr, "Error: '%s' is an enum value and cannot be a FUNCTION/PROCEDURE name\n", + fprintf(stderr, "error: '%s' is an enum value and cannot be a FUNCTION/PROCEDURE name\n", Proto->getName().c_str()); HadError = true; return nullptr; } if (Types.resolve(Proto->getName())) { - fprintf(stderr, "Error: '%s' is a type name and cannot be a FUNCTION/PROCEDURE name\n", + fprintf(stderr, "error: '%s' is a type name and cannot be a FUNCTION/PROCEDURE name\n", Proto->getName().c_str()); HadError = true; return nullptr; @@ -141,7 +141,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { FunctionType *FT = FunctionType::get(RetType, ArgTypes, false); Function *F = Module.getFunction(Proto->getName()); if (F && F->getFunctionType() != FT) { - fprintf(stderr, "Error: Function %s conflicts with an existing declaration of a different type\n", + fprintf(stderr, "error: Function %s conflicts with an existing declaration of a different type\n", Proto->getName().c_str()); HadError = true; return nullptr; @@ -174,7 +174,7 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, if (!TheFunction) return nullptr; if (!TheFunction->empty()) { - fprintf(stderr, "Error: Function %s cannot be redefined.\n", Proto->getName().c_str()); + fprintf(stderr, "error: Function %s cannot be redefined.\n", Proto->getName().c_str()); HadError = true; return nullptr; } @@ -210,7 +210,7 @@ static Value *GenerateCall(llvm::Module &Module, bool &HadError) { Function *CalleeF = Module.getFunction(CalleeName); if (!CalleeF) { - fprintf(stderr, "Error: Call to undefined function %s\n", CalleeName.c_str()); + fprintf(stderr, "error: Call to undefined function %s\n", CalleeName.c_str()); HadError = true; return nullptr; } diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 6299371..4f7f997 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -25,7 +25,7 @@ int Lexer::gettok() { if (LastChar == '"') { LastChar = Input.get(); } else { - fprintf(stderr, "Error: Unterminated string literal at line %d\n", CurrentLine); + fprintf(stderr, "error: Unterminated string literal at line %d\n", CurrentLine); HadError = true; } @@ -35,7 +35,7 @@ int Lexer::gettok() { if (LastChar == '\'') { int CharCode = Input.get(); if (CharCode == EOF || CharCode == '\n') { - fprintf(stderr, "Error: Unterminated char literal at line %d\n", CurrentLine); + fprintf(stderr, "error: Unterminated char literal at line %d\n", CurrentLine); HadError = true; CharVal = '?'; StringVal.assign(1, CharVal); @@ -46,7 +46,7 @@ int Lexer::gettok() { int ClosingQuote = Input.get(); if (ClosingQuote != '\'') { - fprintf(stderr, "Error: CHAR literal must contain exactly one character (line %d)\n", CurrentLine); + fprintf(stderr, "error: CHAR literal must contain exactly one character (line %d)\n", CurrentLine); HadError = true; while (ClosingQuote != EOF && ClosingQuote != '\n' && ClosingQuote != '\'') { ClosingQuote = Input.get(); diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 25fc342..e68b405 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -60,7 +60,7 @@ std::unique_ptr Parser::ParseIdentifierExpr() { auto Args = ParseExprList(')', true); if (!Args) return nullptr; if (CurTok == '[' || CurTok == '.' || CurTok == '^') { - fprintf(stderr, "Error: record/array/pointer access on a function result is not supported at line %d\n", + fprintf(stderr, "error: record/array/pointer access on a function result is not supported at line %d\n", Lex.getLine()); return nullptr; } @@ -92,7 +92,7 @@ bool Parser::ParseDesignatorSuffix(std::vector &Out) { } else if (CurTok == '.') { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected field name after '.' at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected field name after '.' at line %d\n", Lex.getLine()); return false; } DesignatorAccess A; @@ -135,14 +135,14 @@ std::string Parser::ParseTypeName(bool AllowVoid) { if (CurTok == tok_identifier) { std::string TypeName = Lex.IdentifierStr; if (!AllowVoid && TypeName == "VOID") { - fprintf(stderr, "Error: VOID is not allowed here\n"); + fprintf(stderr, "error: VOID is not allowed here\n"); return ""; } getNextToken(); return TypeName; } - fprintf(stderr, "Error: Unknown type name\n"); + fprintf(stderr, "error: Unknown type name\n"); return ""; } @@ -151,7 +151,7 @@ std::unique_ptr Parser::ParseParenExpr() { auto V = ParseExpression(); if (!V) return nullptr; if (CurTok != ')') { - fprintf(stderr, "Error: expected ')'\n"); + fprintf(stderr, "error: expected ')'\n"); return nullptr; } getNextToken(); @@ -183,7 +183,7 @@ std::unique_ptr Parser::ParsePrimary() { } case '(': return ParseParenExpr(); default: - fprintf(stderr, "Error: unknown token '%c' (%d) at line %d when expecting an expression\n", + fprintf(stderr, "error: unknown token '%c' (%d) at line %d when expecting an expression\n", (char)CurTok, CurTok, Lex.getLine()); return nullptr; } @@ -202,14 +202,14 @@ std::unique_ptr Parser::ParseUnary() { int Line = Lex.getLine(); getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: '^' (address-of) requires a variable, array element or record field at line %d\n", + fprintf(stderr, "error: '^' (address-of) requires a variable, array element or record field at line %d\n", Line); return nullptr; } std::string BaseName = Lex.IdentifierStr; getNextToken(); if (CurTok == '(') { - fprintf(stderr, "Error: cannot take the address of a function result at line %d\n", Line); + fprintf(stderr, "error: cannot take the address of a function result at line %d\n", Line); return nullptr; } std::vector Accesses; @@ -317,7 +317,7 @@ std::optional>> Parser::ParseExprList(char if (CurTok == CloseDelim) break; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' or '%c' at line %d\n", CloseDelim, Lex.getLine()); + fprintf(stderr, "error: Expected ',' or '%c' at line %d\n", CloseDelim, Lex.getLine()); return std::nullopt; } getNextToken(); @@ -329,14 +329,14 @@ std::optional>> Parser::ParseExprList(char std::unique_ptr Parser::ParseDeclare() { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected identifier after DECLARE at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected identifier after DECLARE at line %d\n", Lex.getLine()); return nullptr; } std::string Name = Lex.IdentifierStr; getNextToken(); if (CurTok != tok_colon) { - fprintf(stderr, "Error: Expected ':' after variable name '%s' at line %d\n", Name.c_str(), Lex.getLine()); + fprintf(stderr, "error: Expected ':' after variable name '%s' at line %d\n", Name.c_str(), Lex.getLine()); return nullptr; } getNextToken(); @@ -344,7 +344,7 @@ std::unique_ptr Parser::ParseDeclare() { if (CurTok == tok_array) { getNextToken(); if (CurTok != '[') { - fprintf(stderr, "Error: Expected '[' after ARRAY\n"); + fprintf(stderr, "error: Expected '[' after ARRAY\n"); return nullptr; } getNextToken(); @@ -356,7 +356,7 @@ std::unique_ptr Parser::ParseDeclare() { if (!Lower) return nullptr; if (CurTok != tok_colon) { - fprintf(stderr, "Error: Expected ':' in array range (e.g. 1:10)\n"); + fprintf(stderr, "error: Expected ':' in array range (e.g. 1:10)\n"); return nullptr; } getNextToken(); @@ -371,13 +371,13 @@ std::unique_ptr Parser::ParseDeclare() { getNextToken(); continue; } - fprintf(stderr, "Error: Expected ',' or ']' in array dims\n"); + fprintf(stderr, "error: Expected ',' or ']' in array dims\n"); return nullptr; } getNextToken(); if (CurTok != tok_of) { - fprintf(stderr, "Error: Expected OF after array dims\n"); + fprintf(stderr, "error: Expected OF after array dims\n"); return nullptr; } getNextToken(); @@ -404,7 +404,7 @@ std::unique_ptr Parser::ParseIfStmt() { if (!Cond) return nullptr; if (CurTok != tok_then) { - fprintf(stderr, "Error: expected THEN\n"); + fprintf(stderr, "error: expected THEN\n"); return nullptr; } getNextToken(); @@ -418,7 +418,7 @@ std::unique_ptr Parser::ParseIfStmt() { } if (CurTok != tok_endif) { - fprintf(stderr, "Error: expected ENDIF\n"); + fprintf(stderr, "error: expected ENDIF\n"); return nullptr; } getNextToken(); @@ -432,7 +432,7 @@ std::unique_ptr Parser::ParseWhileStmt() { if (!Cond) return nullptr; if (CurTok != tok_do) { - fprintf(stderr, "Error: expected DO after WHILE condition\n"); + fprintf(stderr, "error: expected DO after WHILE condition\n"); return nullptr; } getNextToken(); @@ -440,7 +440,7 @@ std::unique_ptr Parser::ParseWhileStmt() { std::vector> Body = ParseBlock({tok_endwhile}); if (CurTok != tok_endwhile) { - fprintf(stderr, "Error: expected ENDWHILE\n"); + fprintf(stderr, "error: expected ENDWHILE\n"); return nullptr; } getNextToken(); @@ -454,7 +454,7 @@ std::unique_ptr Parser::ParseRepeatStmt() { std::vector> Body = ParseBlock({tok_until}); if (CurTok != tok_until) { - fprintf(stderr, "Error: expected UNTIL\n"); + fprintf(stderr, "error: expected UNTIL\n"); return nullptr; } getNextToken(); @@ -468,14 +468,14 @@ std::unique_ptr Parser::ParseRepeatStmt() { std::unique_ptr Parser::ParseForStmt() { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: expected identifier after FOR\n"); + fprintf(stderr, "error: expected identifier after FOR\n"); return nullptr; } std::string VarName = Lex.IdentifierStr; getNextToken(); if (CurTok != tok_assign) { - fprintf(stderr, "Error: expected '<-' in FOR loop\n"); + fprintf(stderr, "error: expected '<-' in FOR loop\n"); return nullptr; } getNextToken(); @@ -484,7 +484,7 @@ std::unique_ptr Parser::ParseForStmt() { if (!Start) return nullptr; if (CurTok != tok_to) { - fprintf(stderr, "Error: expected TO in FOR loop\n"); + fprintf(stderr, "error: expected TO in FOR loop\n"); return nullptr; } getNextToken(); @@ -502,18 +502,18 @@ std::unique_ptr Parser::ParseForStmt() { std::vector> Body = ParseBlock({tok_next}); if (CurTok != tok_next) { - fprintf(stderr, "Error: expected NEXT\n"); + fprintf(stderr, "error: expected NEXT\n"); return nullptr; } getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: expected identifier after NEXT (e.g., NEXT %s)\n", VarName.c_str()); + fprintf(stderr, "error: expected identifier after NEXT (e.g., NEXT %s)\n", VarName.c_str()); return nullptr; } if (Lex.IdentifierStr != VarName) { - fprintf(stderr, "Error: NEXT identifier '%s' does not match FOR variable '%s'\n", + fprintf(stderr, "error: NEXT identifier '%s' does not match FOR variable '%s'\n", Lex.IdentifierStr.c_str(), VarName.c_str()); return nullptr; } @@ -538,7 +538,7 @@ std::unique_ptr Parser::ParseStatementImpl() { else if (CurTok == tok_input) { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected variable name after INPUT at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected variable name after INPUT at line %d\n", Lex.getLine()); return nullptr; } std::string Name = Lex.IdentifierStr; @@ -548,7 +548,7 @@ std::unique_ptr Parser::ParseStatementImpl() { // Consume the whole designator so it cannot replay as statements. std::vector Dummy; ParseDesignatorSuffix(Dummy); - fprintf(stderr, "Error: INPUT target must be a plain variable at line %d\n", Line); + fprintf(stderr, "error: INPUT target must be a plain variable at line %d\n", Line); return nullptr; } return std::make_unique(Name); @@ -605,17 +605,17 @@ std::unique_ptr Parser::ParseStatementImpl() { return ParseTypeDecl(); } else if (CurTok == tok_define) { - fprintf(stderr, "Error: DEFINE (SET literals) is not implemented at line %d\n", Lex.getLine()); + fprintf(stderr, "error: DEFINE (SET literals) is not implemented at line %d\n", Lex.getLine()); getNextToken(); return nullptr; } else if (CurTok == tok_endtype) { - fprintf(stderr, "Error: ENDTYPE without matching TYPE at line %d\n", Lex.getLine()); + fprintf(stderr, "error: ENDTYPE without matching TYPE at line %d\n", Lex.getLine()); getNextToken(); return nullptr; } - fprintf(stderr, "Error: Unexpected token at line %d when expecting a statement\n", Lex.getLine()); + fprintf(stderr, "error: Unexpected token at line %d when expecting a statement\n", Lex.getLine()); getNextToken(); // guarantee progress so error recovery cannot loop forever return nullptr; } @@ -632,7 +632,7 @@ std::unique_ptr Parser::ParseAssignStmt() { if (!ParseDesignatorSuffix(Accesses)) return nullptr; if (CurTok != tok_assign) { - fprintf(stderr, "Error: Expected '<-' after identifier '%s' at line %d\n", Name.c_str(), Line); + fprintf(stderr, "error: Expected '<-' after identifier '%s' at line %d\n", Name.c_str(), Line); return nullptr; } getNextToken(); diff --git a/lib/Parser/ParserFile.cc b/lib/Parser/ParserFile.cc index 4a4ae31..9aa4979 100644 --- a/lib/Parser/ParserFile.cc +++ b/lib/Parser/ParserFile.cc @@ -13,7 +13,7 @@ std::unique_ptr Parser::ParseOpenFile() { if (!FileName) return nullptr; if (CurTok != tok_for) { - fprintf(stderr, "Error: Expected FOR after OPENFILE filename at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected FOR after OPENFILE filename at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); // eat FOR @@ -24,11 +24,11 @@ std::unique_ptr Parser::ParseOpenFile() { case tok_write: Mode = FileMode::Write; break; case tok_append: Mode = FileMode::Append; break; case tok_random: - fprintf(stderr, "Error: File mode RANDOM is not implemented (text files only) at line %d\n", + fprintf(stderr, "error: File mode RANDOM is not implemented (text files only) at line %d\n", Lex.getLine()); return nullptr; default: - fprintf(stderr, "Error: Expected file mode READ, WRITE or APPEND at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected file mode READ, WRITE or APPEND at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); // eat the mode keyword @@ -45,13 +45,13 @@ std::unique_ptr Parser::ParseReadFile() { if (!FileName) return nullptr; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' after READFILE filename at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected ',' after READFILE filename at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); // eat ',' if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected variable name after ',' in READFILE at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected variable name after ',' in READFILE at line %d\n", Lex.getLine()); return nullptr; } std::string VarName = Lex.IdentifierStr; @@ -61,7 +61,7 @@ std::unique_ptr Parser::ParseReadFile() { // Consume the whole designator so it cannot replay as statements. std::vector Dummy; ParseDesignatorSuffix(Dummy); - fprintf(stderr, "Error: READFILE target must be a plain STRING variable at line %d\n", Line); + fprintf(stderr, "error: READFILE target must be a plain STRING variable at line %d\n", Line); return nullptr; } @@ -77,7 +77,7 @@ std::unique_ptr Parser::ParseWriteFile() { if (!FileName) return nullptr; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' after WRITEFILE filename at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected ',' after WRITEFILE filename at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); // eat ',' diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index 81db2c7..f4e39de 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -13,7 +13,7 @@ bool Parser::ParseSignedIntBound(int64_t &Out) { getNextToken(); } if (CurTok != tok_number_int) { - fprintf(stderr, "Error: Array parameter bounds must be integer literals at line %d\n", + fprintf(stderr, "error: Array parameter bounds must be integer literals at line %d\n", Lex.getLine()); return false; } @@ -37,7 +37,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef while (CurTok != ')') { if (CurTok == tok_byref) { if (!AllowByRef) { - fprintf(stderr, "Error: BYREF parameters are not allowed in a FUNCTION (procedures only) at line %d\n", + fprintf(stderr, "error: BYREF parameters are not allowed in a FUNCTION (procedures only) at line %d\n", Lex.getLine()); return std::nullopt; } @@ -49,7 +49,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef } if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected argument name at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected argument name at line %d\n", Lex.getLine()); return std::nullopt; } ParamDecl P; @@ -58,7 +58,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef getNextToken(); if (CurTok != tok_colon) { - fprintf(stderr, "Error: Expected ':' after argument name at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected ':' after argument name at line %d\n", Lex.getLine()); return std::nullopt; } getNextToken(); @@ -72,7 +72,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef int64_t Lo = 0, Hi = 0; if (!ParseSignedIntBound(Lo)) return std::nullopt; if (CurTok != tok_colon) { - fprintf(stderr, "Error: Expected ':' between array parameter bounds at line %d\n", + fprintf(stderr, "error: Expected ':' between array parameter bounds at line %d\n", Lex.getLine()); return std::nullopt; } @@ -81,7 +81,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef P.DeclaredBounds.emplace_back(Lo, Hi); if (CurTok == ']') break; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' or ']' in array parameter bounds at line %d\n", + fprintf(stderr, "error: Expected ',' or ']' in array parameter bounds at line %d\n", Lex.getLine()); return std::nullopt; } @@ -93,7 +93,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef P.Rank = 1; // ARRAY OF T: bounds travel with the argument } if (CurTok != tok_of) { - fprintf(stderr, "Error: Expected OF after ARRAY in parameter '%s' at line %d\n", + fprintf(stderr, "error: Expected OF after ARRAY in parameter '%s' at line %d\n", P.Name.c_str(), Lex.getLine()); return std::nullopt; } @@ -102,13 +102,13 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef P.TypeName = ParseTypeName(false); if (P.TypeName.empty()) { - fprintf(stderr, "Error: Expected argument type for '%s'\n", P.Name.c_str()); + fprintf(stderr, "error: Expected argument type for '%s'\n", P.Name.c_str()); return std::nullopt; } for (const ParamDecl &Prev : Params) { if (Prev.Name == P.Name) { - fprintf(stderr, "Error: Duplicate parameter name '%s' at line %d\n", + fprintf(stderr, "error: Duplicate parameter name '%s' at line %d\n", P.Name.c_str(), Lex.getLine()); return std::nullopt; } @@ -117,12 +117,12 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef if (CurTok == ')') break; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' or ')'\n"); + fprintf(stderr, "error: Expected ',' or ')'\n"); return std::nullopt; } getNextToken(); if (CurTok == ')') { - fprintf(stderr, "Error: Expected another parameter after ',' at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected another parameter after ',' at line %d\n", Lex.getLine()); return std::nullopt; } } @@ -133,7 +133,7 @@ std::optional> Parser::ParsePrototypeArgs(bool AllowByRef std::unique_ptr Parser::ParseFunction() { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected function name\n"); + fprintf(stderr, "error: Expected function name\n"); return nullptr; } @@ -147,13 +147,13 @@ std::unique_ptr Parser::ParseFunction() { if (CurTok == tok_returns) { getNextToken(); if (CurTok == tok_array) { - fprintf(stderr, "Error: A FUNCTION cannot RETURN an array; use a BYREF PROCEDURE parameter instead at line %d\n", + fprintf(stderr, "error: A FUNCTION cannot RETURN an array; use a BYREF PROCEDURE parameter instead at line %d\n", Lex.getLine()); return nullptr; } RetType = ParseTypeName(true); if (RetType.empty()) { - fprintf(stderr, "Error: Expected return type for function '%s'\n", Name.c_str()); + fprintf(stderr, "error: Expected return type for function '%s'\n", Name.c_str()); return nullptr; } } @@ -163,7 +163,7 @@ std::unique_ptr Parser::ParseFunction() { std::vector> Body = ParseBlock({tok_endfunction}); if (CurTok != tok_endfunction) { - fprintf(stderr, "Error: expected ENDFUNCTION\n"); + fprintf(stderr, "error: expected ENDFUNCTION\n"); return nullptr; } getNextToken(); @@ -174,7 +174,7 @@ std::unique_ptr Parser::ParseFunction() { std::unique_ptr Parser::ParseProcedure() { getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected procedure name\n"); + fprintf(stderr, "error: Expected procedure name\n"); return nullptr; } @@ -188,7 +188,7 @@ std::unique_ptr Parser::ParseProcedure() { std::vector> Body = ParseBlock({tok_endprocedure}); if (CurTok != tok_endprocedure) { - fprintf(stderr, "Error: expected ENDPROCEDURE\n"); + fprintf(stderr, "error: expected ENDPROCEDURE\n"); return nullptr; } getNextToken(); @@ -200,7 +200,7 @@ std::unique_ptr Parser::ParseCallStmt() { int Line = Lex.getLine(); getNextToken(); if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected callee name after CALL\n"); + fprintf(stderr, "error: Expected callee name after CALL\n"); return nullptr; } diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index 50ee53f..8be7e46 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -19,7 +19,7 @@ std::unique_ptr Parser::ParseTypeDecl() { getNextToken(); // eat TYPE if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected type name after TYPE at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected type name after TYPE at line %d\n", Lex.getLine()); return nullptr; } std::string Name = Lex.IdentifierStr; @@ -35,7 +35,7 @@ std::unique_ptr Parser::ParseTypeDecl() { std::vector Values; while (true) { if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected an enum value name in TYPE %s at line %d\n", + fprintf(stderr, "error: Expected an enum value name in TYPE %s at line %d\n", Name.c_str(), Lex.getLine()); return nullptr; } @@ -43,7 +43,7 @@ std::unique_ptr Parser::ParseTypeDecl() { getNextToken(); if (CurTok == ')') break; if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' or ')' in TYPE %s at line %d\n", + fprintf(stderr, "error: Expected ',' or ')' in TYPE %s at line %d\n", Name.c_str(), Lex.getLine()); return nullptr; } @@ -57,11 +57,11 @@ std::unique_ptr Parser::ParseTypeDecl() { if (Pointee.empty()) return nullptr; Decl = std::make_unique(Name, Pointee); } else if (CurTok == tok_set) { - fprintf(stderr, "Error: SET OF types are not implemented at line %d\n", Lex.getLine()); + fprintf(stderr, "error: SET OF types are not implemented at line %d\n", Lex.getLine()); getNextToken(); return nullptr; } else { - fprintf(stderr, "Error: Expected '(' or '^' after '=' in TYPE %s at line %d\n", + fprintf(stderr, "error: Expected '(' or '^' after '=' in TYPE %s at line %d\n", Name.c_str(), Lex.getLine()); return nullptr; } @@ -79,13 +79,13 @@ std::unique_ptr Parser::ParseTypeDecl() { } } if (CurTok != tok_endtype) { - fprintf(stderr, "Error: Expected DECLARE or ENDTYPE in TYPE %s at line %d\n", + fprintf(stderr, "error: Expected DECLARE or ENDTYPE in TYPE %s at line %d\n", Name.c_str(), Lex.getLine()); return nullptr; // token left for the outer recovery } getNextToken(); // eat ENDTYPE if (Fields.empty()) { - fprintf(stderr, "Error: Record TYPE %s has no fields at line %d\n", Name.c_str(), Line); + fprintf(stderr, "error: Record TYPE %s has no fields at line %d\n", Name.c_str(), Line); return nullptr; } if (HadFieldError) return nullptr; @@ -93,7 +93,7 @@ std::unique_ptr Parser::ParseTypeDecl() { } if (!AtTopLevel) { - fprintf(stderr, "Error: TYPE declarations are only allowed at top level at line %d\n", Line); + fprintf(stderr, "error: TYPE declarations are only allowed at top level at line %d\n", Line); return nullptr; } return Decl; @@ -103,21 +103,21 @@ std::unique_ptr Parser::ParseTypeDecl() { bool Parser::ParseRecordField(std::vector &Out) { getNextToken(); // eat DECLARE if (CurTok != tok_identifier) { - fprintf(stderr, "Error: Expected field name after DECLARE at line %d\n", Lex.getLine()); + fprintf(stderr, "error: Expected field name after DECLARE at line %d\n", Lex.getLine()); return false; } std::string FieldName = Lex.IdentifierStr; getNextToken(); if (CurTok != tok_colon) { - fprintf(stderr, "Error: Expected ':' after field name %s at line %d\n", + fprintf(stderr, "error: Expected ':' after field name %s at line %d\n", FieldName.c_str(), Lex.getLine()); return false; } getNextToken(); if (CurTok == tok_array) { - fprintf(stderr, "Error: Array fields inside records are not implemented at line %d\n", + fprintf(stderr, "error: Array fields inside records are not implemented at line %d\n", Lex.getLine()); return false; } diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc index 81a2265..83489b1 100644 --- a/tools/driver/EmbeddedRunner.cc +++ b/tools/driver/EmbeddedRunner.cc @@ -10,12 +10,12 @@ int runEmbedded(const cps::CodeGen &CG) { if (llvm::InitializeNativeTarget() || llvm::InitializeNativeTargetAsmPrinter()) { - llvm::errs() << "Error: cannot initialize the embedded LLVM target\n"; + llvm::errs() << "error: cannot initialize the embedded LLVM target\n"; return 1; } std::string Error; if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr, &Error)) { - llvm::errs() << "Error: cannot load process symbols: " << Error << "\n"; + llvm::errs() << "error: cannot load process symbols: " << Error << "\n"; return 1; } // Keep CodeGen's module and context alive while the engine owns its clone. @@ -27,17 +27,17 @@ int runEmbedded(const cps::CodeGen &CG) { Builder.setMCJITMemoryManager(std::make_unique()); std::unique_ptr Engine(Builder.create()); if (!Engine) { - llvm::errs() << "Error: cannot create the embedded LLVM engine: " << Error << "\n"; + llvm::errs() << "error: cannot create the embedded LLVM engine: " << Error << "\n"; return 1; } Engine->finalizeObject(); if (Engine->hasError()) { - llvm::errs() << "Error: embedded LLVM execution failed: " << Engine->getErrorMessage() << "\n"; + llvm::errs() << "error: embedded LLVM execution failed: " << Engine->getErrorMessage() << "\n"; return 1; } auto MainAddress = Engine->getFunctionAddress("main"); if (!MainAddress) { - llvm::errs() << "Error: embedded LLVM could not resolve main\n"; + llvm::errs() << "error: embedded LLVM could not resolve main\n"; return 1; } auto Main = reinterpret_cast(MainAddress); diff --git a/tools/driver/main.cc b/tools/driver/main.cc index 4df8562..d670ac0 100644 --- a/tools/driver/main.cc +++ b/tools/driver/main.cc @@ -13,12 +13,16 @@ #include #include -int main(int argc, char **argv) { - if (argc == 1) { - // TODO: Print help here when help text is available. - return 0; - } +namespace { + +int compilationTerminated() { + llvm::errs() << "compilation terminated.\n"; + return 1; +} +} // namespace + +int main(int argc, char **argv) { std::string InputPath; std::string OutputPath; bool Run = false; @@ -28,28 +32,28 @@ int main(int argc, char **argv) { Run = true; } else if (Arg == "-o") { if (!OutputPath.empty()) { - llvm::errs() << "Error: -o may only be specified once\n"; - return 1; + llvm::errs() << "cpsc: error: -o may only be specified once\n"; + return compilationTerminated(); } if (I + 1 == argc || argv[I + 1][0] == '-' || argv[I + 1][0] == '\0') { - llvm::errs() << "Error: -o requires an output path\n"; - return 1; + llvm::errs() << "cpsc: error: -o requires an output path\n"; + return compilationTerminated(); } OutputPath = argv[++I]; } else if (Arg.empty() || Arg[0] == '-') { - llvm::errs() << "Error: unknown argument '" << Arg << "'\n"; - return 1; + llvm::errs() << "cpsc: error: unknown argument '" << Arg << "'\n"; + return compilationTerminated(); } else if (!InputPath.empty()) { - llvm::errs() << "Error: only one source file is supported\n"; - return 1; + llvm::errs() << "cpsc: error: only one source file is supported\n"; + return compilationTerminated(); } else { InputPath = Arg; } } if (InputPath.empty()) { - llvm::errs() << "Error: no source file specified\n"; - return 1; + llvm::errs() << "cpsc: fatal error: no input files\n"; + return compilationTerminated(); } #ifdef CPSC_RUN_PATH std::string LliPath; @@ -58,13 +62,13 @@ int main(int argc, char **argv) { #ifdef CPSC_RUN_PATH auto Lli = llvm::sys::findProgramByName("lli"); if (!Lli) { - llvm::errs() << "Error: -r requires lli in PATH: " << Lli.getError().message() << "\n"; - return 1; + llvm::errs() << "cpsc: fatal error: -r requires lli in PATH: " << Lli.getError().message() << "\n"; + return compilationTerminated(); } LliPath = *Lli; #elif defined(CPSC_RUN_NONE) - llvm::errs() << "Error: -r is disabled in this build (CPSC_RUN_MODE=NONE)\n"; - return 1; + llvm::errs() << "cpsc: fatal error: -r is disabled in this build (CPSC_RUN_MODE=NONE)\n"; + return compilationTerminated(); #endif } if (OutputPath.empty() && !Run) { @@ -75,12 +79,12 @@ int main(int argc, char **argv) { std::ifstream Input(InputPath, std::ios::binary); if (!Input) { - llvm::errs() << "Error: cannot open source file '" << InputPath << "'\n"; - return 1; + llvm::errs() << "cpsc: fatal error: cannot open source file '" << InputPath << "'\n"; + return compilationTerminated(); } if (llvm::sys::fs::equivalent(InputPath, OutputPath)) { - llvm::errs() << "Error: output file must not be the source file\n"; - return 1; + llvm::errs() << "cpsc: error: output file must not be the source file\n"; + return compilationTerminated(); } cps::Lexer Lex(Input); @@ -88,15 +92,15 @@ int main(int argc, char **argv) { cps::Parser Parser(Lex); auto Statements = Parser.Parse(); if (Input.bad()) { - llvm::errs() << "Error: cannot read source file '" << InputPath << "'\n"; - return 1; + llvm::errs() << "cpsc: fatal error: cannot read source file '" << InputPath << "'\n"; + return compilationTerminated(); } - if (Lex.hadError() || Parser.hadError()) return 1; + if (Lex.hadError() || Parser.hadError()) return compilationTerminated(); cps::CodeGen CG; CG.compile(Statements); - if (CG.hadError()) return 1; + if (CG.hadError()) return compilationTerminated(); #ifdef CPSC_RUN_EMBEDDED if (Run && OutputPath.empty()) return runEmbedded(CG); @@ -115,32 +119,32 @@ int main(int argc, char **argv) { EC = llvm::sys::fs::openFileForWrite(OutputPath, OutputFD); } if (EC) { - llvm::errs() << "Error: cannot open output file '" << OutputPath + llvm::errs() << "cpsc: fatal error: cannot open output file '" << OutputPath << "': " << EC.message() << "\n"; - return 1; + return compilationTerminated(); } llvm::raw_fd_ostream Output(OutputFD, true); CG.print(Output); Output.close(); if (Output.has_error()) { - llvm::errs() << "Error: cannot write output file '" << OutputPath + llvm::errs() << "cpsc: fatal error: cannot write output file '" << OutputPath << "': " << Output.error().message() << "\n"; Output.clear_error(); - return 1; + return compilationTerminated(); } if (!Run) return 0; #ifdef CPSC_RUN_PATH llvm::SmallString<256> RunPath(OutputPath); if (auto PathError = llvm::sys::fs::make_absolute(RunPath)) { - llvm::errs() << "Error: cannot resolve IR path: " << PathError.message() << "\n"; + llvm::errs() << "cpsc: error: cannot resolve IR path: " << PathError.message() << "\n"; return 1; } std::string ExecutionError; int Result = llvm::sys::ExecuteAndWait(LliPath, {LliPath, RunPath}, std::nullopt, {}, 0, 0, &ExecutionError); if (Result < 0) { - llvm::errs() << "Error: lli execution failed (status " << Result << "): " + llvm::errs() << "cpsc: error: lli execution failed (status " << Result << "): " << ExecutionError << "\n"; return 1; } -- cgit v1.3.1-4-g156e