diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-16 13:21:05 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-16 13:21:05 +0000 |
| commit | e44bdc6a2a0cf624b08046ecc7e5c5d5d9d2d66c (patch) | |
| tree | f5276cc0dd2f34e4d30a58f80223912265a1c5a0 /lib | |
| parent | e1e789ac0249149a4aa1671f5f9260d488cb617a (diff) | |
fix(compiler): correct CIE semantics and diagnostic locations
Use standard WHILE headers and default-public class fields, resolve
pseudocode identifiers without case sensitivity, and check signed i64
literal boundaries before conversion. Preserve source locations across
recursive emission and function diagnostics.
Add 22 regression cases and update compiler examples and documentation.
Validated all 69 CTest entries with the original Linux Actions seed and
verified the release package's embedded, native and IR output paths.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/CodeGen/ArrayHandler.cc | 4 | ||||
| -rw-r--r-- | lib/CodeGen/Builtins.h | 3 | ||||
| -rw-r--r-- | lib/CodeGen/CTypeGen.cc | 6 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 6 | ||||
| -rw-r--r-- | lib/CodeGen/ConstantGen.cc | 2 | ||||
| -rw-r--r-- | lib/CodeGen/ControlFlowGen.cc | 3 | ||||
| -rw-r--r-- | lib/CodeGen/Designator.cc | 3 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 5 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 58 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 1 | ||||
| -rw-r--r-- | lib/CodeGen/TypeCheckGen.cc | 7 | ||||
| -rw-r--r-- | lib/CodeGen/TypeSystem.cc | 6 | ||||
| -rw-r--r-- | lib/Lexer/Lexer.cc | 11 | ||||
| -rw-r--r-- | lib/Parser/Parser.cc | 2 | ||||
| -rw-r--r-- | lib/Parser/ParserControlFlow.cc | 12 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 42 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 6 | ||||
| -rw-r--r-- | lib/Parser/ParserType.cc | 27 |
18 files changed, 134 insertions, 70 deletions
diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc index 40a330f..5eb8a52 100644 --- a/lib/CodeGen/ArrayHandler.cc +++ b/lib/CodeGen/ArrayHandler.cc @@ -12,7 +12,7 @@ using namespace cps; ArrayHandler::ArrayHandler(LLVMContext &C, IRBuilder<> &B, Module &M, - std::map<std::string, SymbolInfo> &Sym, + IdentifierMap<SymbolInfo> &Sym, TypeSystem &TS, RuntimeCheck &RC) : TheContext(&C), @@ -253,7 +253,7 @@ Value *ArrayHandler::emitElementAddress(ArrayAccessExprAST *Expr, CG, ElemTypeNameOut); } -std::map<std::string, ArrayMetadata> ArrayHandler::exchangeTable(std::map<std::string, ArrayMetadata> NewTable) { +IdentifierMap<ArrayMetadata> ArrayHandler::exchangeTable(IdentifierMap<ArrayMetadata> NewTable) { std::swap(ArrayTable, NewTable); return NewTable; } diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h index 34e36a9..e7f60e4 100644 --- a/lib/CodeGen/Builtins.h +++ b/lib/CodeGen/Builtins.h @@ -1,4 +1,5 @@ #pragma once +#include "cps/Identifier.h" #include <string> namespace cps::detail { @@ -29,7 +30,7 @@ inline constexpr BuiltinInfo Builtins[] = { inline const BuiltinInfo *findBuiltin(const std::string &Name) { for (const auto &B : Builtins) { - if (Name == B.Name) return &B; + if (sameIdentifier(Name, B.Name)) return &B; } return nullptr; } diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc index a20306e..80a7d3b 100644 --- a/lib/CodeGen/CTypeGen.cc +++ b/lib/CodeGen/CTypeGen.cc @@ -81,7 +81,7 @@ Value *CodeGen::emitCast(CastExprAST *Cast) { Value *CodeGen::emitCBinaryOp(int Op, Value *L, const TypeInfo *Left, Value *R, const TypeInfo *Right, int Line) { if (!Left || !Right || !Left->isC() || !Right->isC()) { - reportError("C and pseudocode operands require an explicit cast (line %d)", Line); + reportError("C and pseudocode operands require an explicit cast"); return nullptr; } if (Left->isPointer() || Right->isPointer()) { @@ -99,7 +99,7 @@ Value *CodeGen::emitCBinaryOp(int Op, Value *L, const TypeInfo *Left, return nullptr; } if (Op == '&' || ((Op == tok_div || Op == tok_mod) && !Common->isIntegral())) { - reportError("Operator is not supported for these C operand types (line %d)", Line); + reportError("Operator is not supported for these C operand types"); return nullptr; } if (Op == tok_and || Op == tok_or) { @@ -154,7 +154,7 @@ Value *CodeGen::emitCBinaryOp(int Op, Value *L, const TypeInfo *Left, : Builder->CreateICmpUGE(L, R, "c_ge"); } } - reportError("Unsupported operator for C operands (line %d)", Line); + reportError("Unsupported operator for C operands"); return nullptr; } diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index b793657..dff4009 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -24,6 +24,7 @@ void CodeGen::reportError(const char *Fmt, ...) { va_start(Args, Fmt); fprintf(stderr, "error: "); vfprintf(stderr, Fmt, Args); + if (DiagnosticLine) fprintf(stderr, " at line %d", DiagnosticLine); fprintf(stderr, "\n"); va_end(Args); HadError = true; @@ -51,7 +52,7 @@ CodeGen::CodeGen() { *Types, *Exceptions, Symbols, - HadError); + HadError, DiagnosticLine); ArithHandler = std::make_unique<ArithmeticHandler>(*TheContext, *Builder, HadError, *RuntimeChecker); @@ -158,12 +159,14 @@ void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { // Pre-register every top-level FUNCTION/PROCEDURE prototype so calls may // precede definitions. for (const auto &Stmt : Statements) { + SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine()); if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt.get())) { FuncGen->emitPrototype(FuncDef->getProto()); } } for (const auto &Stmt : Statements) { + SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine()); emitStmt(Stmt.get()); } @@ -197,6 +200,7 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line, std::vector<Value*> &Out) { + SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine); const FuncSig *Sig = FuncGen->getSignature(Callee); if (!Sig) { reportError("Call to undefined function %s", Callee.c_str()); diff --git a/lib/CodeGen/ConstantGen.cc b/lib/CodeGen/ConstantGen.cc index e9f81ae..e14e22f 100644 --- a/lib/CodeGen/ConstantGen.cc +++ b/lib/CodeGen/ConstantGen.cc @@ -19,7 +19,7 @@ bool CodeGen::prepareSymbolDeclaration(const std::string &Name, bool IsConstant) auto &Scope = ConstantScopes.back(); auto It = Symbols.find(Name); if (It != Symbols.end()) { - if ((IsConstant || It->second.ConstantValue) && It->second.ScopeID == Scope.ID) { + if (It->second.ScopeID == Scope.ID) { reportError("Declaration of %s conflicts with an existing declaration in this scope", Name.c_str()); return false; } diff --git a/lib/CodeGen/ControlFlowGen.cc b/lib/CodeGen/ControlFlowGen.cc index d34f640..17f43f2 100644 --- a/lib/CodeGen/ControlFlowGen.cc +++ b/lib/CodeGen/ControlFlowGen.cc @@ -190,6 +190,7 @@ void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) { auto *After = BasicBlock::Create(*TheContext, "case_cont", F); auto SavedArrays = Arrays->snapshotTable(); for (const auto &Branch : Stmt->getBranches()) { + SaveAndRestore<int> Location(DiagnosticLine, Branch.Line); auto *Body = BasicBlock::Create(*TheContext, "case_body", F); auto *Next = BasicBlock::Create(*TheContext, "case_next", F); auto Compare = [&](int Op, ExprAST *Label) -> Value * { @@ -199,7 +200,7 @@ void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) { } const TypeInfo *LabelType = getExprTypeInfo(Label); if (!LabelType) { - reportError("Cannot determine CASE label type at line %d", Branch.Line); + reportError("Cannot determine CASE label type"); return nullptr; } Value *LabelValue = emitExpr(Label); diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index afaf0a4..fcc6919 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -15,6 +15,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem std::string Err; for (const auto &S : Statements) { + SaveAndRestore<int> Location(DiagnosticLine, S->getLine()); bool Ok = true; if (auto *C = dynamic_cast<CTypeDeclAST*>(S.get())) Ok = Types->defineCAlias(C->getName(), C->getExpression(), Err); @@ -28,6 +29,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem } for (const auto &S : Statements) { + SaveAndRestore<int> Location(DiagnosticLine, S->getLine()); bool Ok = true; if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get())) { Ok = Types->defineEnum(E->getName(), E->getValueNames(), Err); @@ -44,6 +46,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem // of its storage plus the type name stored there. Array indexing goes through // ArrayHandler's checked pipeline; dereferences emit the null-pointer check. bool CodeGen::emitLValue(ExprAST *E, LValueInfo &Out) { + SaveAndRestore<int> Location(DiagnosticLine, E && E->getLine() ? E->getLine() : DiagnosticLine); if (auto *Var = dynamic_cast<VariableExprAST*>(E)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (!Info) { diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index f3dadb7..b3655fd 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -11,6 +11,7 @@ using namespace cps; using namespace cps::detail; Value *CodeGen::emitExpr(ExprAST *Expr) { + SaveAndRestore<int> Location(DiagnosticLine, Expr && Expr->getLine() ? Expr->getLine() : DiagnosticLine); if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) return emitCast(Cast); if (!Expr) return nullptr; @@ -126,6 +127,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { std::string Name = Call->getCallee(); if (const BuiltinInfo *B = findBuiltin(Name)) { + Name = B->Name; if (Call->getArgs().size() != B->Arity) { reportError("%s expects %u arg%s", B->Name, B->Arity, B->Arity == 1 ? "" : "s"); return nullptr; @@ -247,6 +249,7 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { // Ordinary binary expressions and CASE use the same typed operations. Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT, Value *R, const TypeInfo *RT, int Line) { + SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine); if (!L || !R) return nullptr; if ((LT && LT->isC()) || (RT && RT->isC())) return emitCBinaryOp(Op, L, LT, R, RT, Line); @@ -261,7 +264,7 @@ Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT, case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: if ((LT && LT->isString()) || (RT && RT->isString())) { if (!(LT && LT->isString() && RT && RT->isString())) { - reportError("STRING comparison requires two STRING operands (line %d)", Line); + reportError("STRING comparison requires two STRING operands"); return nullptr; } return StrHandler->emitCompare(Op, L, R); diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 957a1f5..a155f60 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -2,6 +2,7 @@ #include "cps/CodeGen.h" #include "cps/ExceptionHandler.h" #include <cstdio> +#include <cstdarg> using namespace llvm; using namespace cps; @@ -19,16 +20,26 @@ static bool isReservedRuntimeName(const std::string &Name) { "__gxx_personality_seh0", "__gxx_personality_sj0", "_Unwind_Resume", "_ZTIi", }; for (const char *R : Reserved) { - if (Name == R) return true; + if (sameIdentifier(Name, R)) return true; } return false; } +void FunctionGen::reportError(const char *Fmt, ...) { + va_list Args; + va_start(Args, Fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, Fmt, Args); + if (DiagnosticLine) fprintf(stderr, " at line %d", DiagnosticLine); + fprintf(stderr, "\n"); + va_end(Args); + HadError = true; +} + Type *FunctionGen::getLLVMType(const std::string &TypeName) { Type *Resolved = Types.getLLVMType(TypeName); if (!Resolved) { - fprintf(stderr, "error: Unknown type %s\n", TypeName.c_str()); - HadError = true; + reportError("Unknown type %s", TypeName.c_str()); return Type::getInt64Ty(Context); } return Resolved; @@ -38,12 +49,10 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vector<ParamDecl Function::arg_iterator AI = F->arg_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()); - HadError = true; + reportError("parameter '%s' collides with an enum value", P.Name.c_str()); } if (Types.resolve(P.Name)) { - fprintf(stderr, "error: parameter '%s' collides with a type name\n", P.Name.c_str()); - HadError = true; + reportError("parameter '%s' collides with a type name", P.Name.c_str()); } Value *ArgVal = &(*AI); ++AI; @@ -88,39 +97,35 @@ 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", + reportError("'%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name", 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", + reportError("'%s' is a built-in function and cannot be redefined", 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", + reportError("'%s' is an enum value and cannot be a FUNCTION/PROCEDURE name", 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", + reportError("'%s' is a type name and cannot be a FUNCTION/PROCEDURE name", Proto->getName().c_str()); - HadError = true; return nullptr; } FuncSig Sig; + Sig.Name = Proto->getName(); Sig.ReturnTypeName = Proto->getReturnType(); std::vector<Type*> ArgTypes; for (const ParamDecl &P : Proto->getParams()) { const TypeInfo *Info = Types.resolve(P.TypeName); if (Info && Info->isVoid()) { - fprintf(stderr, "error: parameter %s cannot have void type\n", P.Name.c_str()); - HadError = true; + reportError("parameter %s cannot have void type", P.Name.c_str()); return nullptr; } ParamSig PS; @@ -148,11 +153,11 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { Type *RetType = getLLVMType(Proto->getReturnType()); FunctionType *FT = FunctionType::get(RetType, ArgTypes, false); - Function *F = Module.getFunction(Proto->getName()); + const FuncSig *Existing = getSignature(Proto->getName()); + Function *F = Module.getFunction(Existing ? Existing->Name : Proto->getName()); if (F && F->getFunctionType() != FT) { - fprintf(stderr, "error: Function %s conflicts with an existing declaration of a different type\n", + reportError("Function %s conflicts with an existing declaration of a different type", Proto->getName().c_str()); - HadError = true; return nullptr; } if (!F) { @@ -184,15 +189,14 @@ 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()); - HadError = true; + reportError("Function %s cannot be redefined.", Proto->getName().c_str()); return nullptr; } BasicBlock *BB = BasicBlock::Create(Context, "entry", TheFunction); Builder.SetInsertPoint(BB); - std::map<std::string, SymbolInfo> OldSymbols = Symbols; + IdentifierMap<SymbolInfo> OldSymbols = Symbols; Symbols.clear(); for (const auto &[Name, Info] : OldSymbols) { if (Info.ConstantValue && Info.ScopeID == 0) Symbols.emplace(Name, Info); @@ -209,7 +213,7 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, } if (!Builder.GetInsertBlock()->hasTerminator()) { - if (Proto->getReturnType() == "VOID") { + if (sameIdentifier(Proto->getReturnType(), "VOID")) { Builder.CreateRetVoid(); } else { Builder.CreateRet(Constant::getNullValue(TheFunction->getReturnType())); @@ -240,11 +244,13 @@ static Value *GenerateCall(llvm::Module &Module, } llvm::Value *FunctionGen::emitCallExpr(CallExprAST *Call, const std::vector<llvm::Value*> &Args) { - return GenerateCall(Module, Exceptions, Call->getCallee(), Args, HadError); + const FuncSig *Sig = getSignature(Call->getCallee()); + return GenerateCall(Module, Exceptions, Sig ? Sig->Name : Call->getCallee(), Args, HadError); } void FunctionGen::emitCallStmt(CallStmtAST *Call, const std::vector<llvm::Value*> &Args) { - GenerateCall(Module, Exceptions, Call->getCallee(), Args, HadError); + const FuncSig *Sig = getSignature(Call->getCallee()); + GenerateCall(Module, Exceptions, Sig ? Sig->Name : Call->getCallee(), Args, HadError); } void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) { diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc index 8907cfa..3ea0670 100644 --- a/lib/CodeGen/StatementGen.cc +++ b/lib/CodeGen/StatementGen.cc @@ -9,6 +9,7 @@ using namespace llvm; using namespace cps; void CodeGen::emitStmt(StmtAST *Stmt) { + SaveAndRestore<int> Location(DiagnosticLine, Stmt && Stmt->getLine() ? Stmt->getLine() : DiagnosticLine); if (!Stmt) return; if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt)) { diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc index 654afc8..690d005 100644 --- a/lib/CodeGen/TypeCheckGen.cc +++ b/lib/CodeGen/TypeCheckGen.cc @@ -33,6 +33,7 @@ std::string opToText(int Op) { // LLVM level); builtin targets additionally reject user-kind sources, then // keep the existing representation coercions unchanged. Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { + SaveAndRestore<int> Location(DiagnosticLine, E && E->getLine() ? E->getLine() : DiagnosticLine); if (!E || !Target) return nullptr; // A whole-array reference evaluates to its raw data pointer, and @@ -94,7 +95,7 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { } return nullptr; } - if (Src->Name != Target->Name) { + if (!Src->sameType(Target)) { reportError("Cannot use a value of type %s where %s is expected", Src->Name.c_str(), Target->Name.c_str()); return nullptr; @@ -133,7 +134,7 @@ Value *CodeGen::emitUserKindBinaryOp(int Op, Value *L, const TypeInfo *LT, } if ((LT && LT->isPointer()) || (RT && RT->isPointer())) { - bool SameName = LT && RT && LT->isPointer() && RT->isPointer() && LT->Name == RT->Name; + bool SameName = LT && RT && LT->isPointer() && RT->isPointer() && LT->sameType(RT); if (SameName && (Op == tok_eq || Op == tok_ne)) { return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "ptr_eq") : Builder->CreateICmpNE(L, R, "ptr_ne"); @@ -144,7 +145,7 @@ Value *CodeGen::emitUserKindBinaryOp(int Op, Value *L, const TypeInfo *LT, } // At least one enum operand from here on. - bool BothSameEnum = LT && RT && LT->isEnum() && RT->isEnum() && LT->Name == RT->Name; + bool BothSameEnum = LT && RT && LT->isEnum() && RT->isEnum() && LT->sameType(RT); switch (Op) { case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: { diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index a837c9a..f4620dc 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -12,12 +12,12 @@ bool TypeInfo::sameType(const TypeInfo *Other) const { if (!Other) return false; if (isC() || Other->isC()) return isC() && Other->isC() && CIdentity == Other->CIdentity; - return Name == Other->Name; + return sameIdentifier(Name, Other->Name); } const RecordFieldInfo *RecordPayload::findField(const std::string &Name) const { for (const auto &F : Fields) { - if (F.first == Name) return &F.second; + if (sameIdentifier(F.first, Name)) return &F.second; } return nullptr; } @@ -172,7 +172,7 @@ bool TypeSystem::defineEnum(const std::string &Name, for (size_t I = 0; I < Values.size(); ++I) { const std::string &V = Values[I]; for (size_t J = 0; J < I; ++J) { - if (Values[J] == V) { + if (sameIdentifier(Values[J], V)) { Err = "Duplicate value '" + V + "' in enum " + Name; return false; } diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 59247cf..9203db1 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -1,5 +1,7 @@ #include "cps/Lexer.h" #include <algorithm> +#include <charconv> +#include <limits> #include <cctype> #include <cstdio> #include <cstdlib> @@ -104,7 +106,6 @@ int Lexer::readToken() { if (IdentifierStr == "THROW") return tok_throw; if (IdentifierStr == "WHILE") return tok_while; - if (IdentifierStr == "DO") return tok_do; if (IdentifierStr == "ENDWHILE") return tok_endwhile; if (IdentifierStr == "REPEAT") return tok_repeat; @@ -175,7 +176,13 @@ int Lexer::readToken() { return tok_number_real; } - NumVal = strtoll(NumStr.c_str(), nullptr, 10); + auto Parsed = std::from_chars(NumStr.data(), NumStr.data() + NumStr.size(), NumVal); + if (Parsed.ec != std::errc{} || + NumVal > uint64_t(std::numeric_limits<int64_t>::max()) + 1) { + fprintf(stderr, "error: Integer literal is outside the i64 range at line %d\n", TokenLine); + HadError = true; + NumVal = 0; + } return tok_number_int; } diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index d8fecdc..f2fcbc8 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -86,7 +86,9 @@ std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<i } std::unique_ptr<StmtAST> Parser::ParseStatement() { + int Line = Lex.getLine(); auto Stmt = ParseStatementImpl(); + if (Stmt && !Stmt->getLine()) Stmt->setLine(Line); if (!Stmt) ++NumErrors; return Stmt; } diff --git a/lib/Parser/ParserControlFlow.cc b/lib/Parser/ParserControlFlow.cc index d01e561..afa8bd8 100644 --- a/lib/Parser/ParserControlFlow.cc +++ b/lib/Parser/ParserControlFlow.cc @@ -27,7 +27,7 @@ std::unique_ptr<StmtAST> Parser::ParseIfStmt() { if (!Cond) return nullptr; if (CurTok != tok_then) { - fprintf(stderr, "error: expected THEN\n"); + fprintf(stderr, "error: expected THEN at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); @@ -41,7 +41,7 @@ std::unique_ptr<StmtAST> Parser::ParseIfStmt() { } if (CurTok != tok_endif) { - fprintf(stderr, "error: expected ENDIF\n"); + fprintf(stderr, "error: expected ENDIF at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); @@ -54,12 +54,6 @@ std::unique_ptr<StmtAST> Parser::ParseWhileStmt() { auto Cond = ParseExpression(); if (!Cond) return nullptr; - if (CurTok != tok_do) { - fprintf(stderr, "error: expected DO after WHILE condition\n"); - return nullptr; - } - getNextToken(); - std::vector<std::unique_ptr<StmtAST>> Body = ParseBlock({tok_endwhile}); if (CurTok != tok_endwhile) { @@ -135,7 +129,7 @@ std::unique_ptr<StmtAST> Parser::ParseForStmt() { return nullptr; } - if (Lex.IdentifierStr != VarName) { + if (!sameIdentifier(Lex.IdentifierStr, VarName)) { fprintf(stderr, "error: NEXT identifier '%s' does not match FOR variable '%s'\n", Lex.IdentifierStr.c_str(), VarName.c_str()); return nullptr; diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index d32c407..b8952a8 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -1,6 +1,7 @@ #include "cps/Parser.h" #include "cps/FunctionAST.h" #include <cstdio> +#include <limits> using namespace cps; @@ -13,15 +14,29 @@ int Parser::GetTokPrecedence() { } +bool Parser::ParseIntegerValue(bool Negative, int64_t &Out) { + const uint64_t Max = uint64_t(std::numeric_limits<int64_t>::max()); + if (Lex.NumVal > Max + (Negative ? 1u : 0u)) { + fprintf(stderr, "error: Integer literal is outside the i64 range at line %d\n", Lex.getLine()); + getNextToken(); + return false; + } + Out = Negative && Lex.NumVal == Max + 1 + ? std::numeric_limits<int64_t>::min() + : Negative ? -static_cast<int64_t>(Lex.NumVal) : static_cast<int64_t>(Lex.NumVal); + getNextToken(); + return true; +} + std::unique_ptr<ExprAST> Parser::ParseNumberExpr() { if (CurTok == tok_number_real) { auto Result = std::make_unique<RealExprAST>(Lex.RealVal); getNextToken(); return Result; } - auto Result = std::make_unique<IntegerExprAST>(Lex.NumVal); - getNextToken(); - return Result; + int64_t Value; + if (!ParseIntegerValue(false, Value)) return nullptr; + return std::make_unique<IntegerExprAST>(Value); } std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() { @@ -109,7 +124,7 @@ std::unique_ptr<ExprAST> Parser::ParseParenExpr() { auto V = ParseExpression(); if (!V) return nullptr; if (CurTok != ')') { - fprintf(stderr, "error: expected ')'\n"); + fprintf(stderr, "error: expected ')' at line %d\n", Lex.getLine()); return nullptr; } getNextToken(); @@ -117,6 +132,13 @@ std::unique_ptr<ExprAST> Parser::ParseParenExpr() { } std::unique_ptr<ExprAST> Parser::ParsePrimary() { + int Line = Lex.getLine(); + auto Expr = ParsePrimaryImpl(); + if (Expr && !Expr->getLine()) Expr->setLine(Line); + return Expr; +} + +std::unique_ptr<ExprAST> Parser::ParsePrimaryImpl() { switch (CurTok) { case tok_identifier: return ParseIdentifierExpr(); case tok_number_int: return ParseNumberExpr(); @@ -148,6 +170,13 @@ std::unique_ptr<ExprAST> Parser::ParsePrimary() { } std::unique_ptr<ExprAST> Parser::ParseUnary() { + int Line = Lex.getLine(); + auto Expr = ParseUnaryImpl(); + if (Expr && !Expr->getLine()) Expr->setLine(Line); + return Expr; +} + +std::unique_ptr<ExprAST> Parser::ParseUnaryImpl() { if (CurTok == tok_not) { getNextToken(); auto Operand = ParseUnary(); @@ -178,6 +207,11 @@ std::unique_ptr<ExprAST> Parser::ParseUnary() { if (CurTok == '-') { getNextToken(); + if (CurTok == tok_number_int) { + int64_t Value; + if (!ParseIntegerValue(true, Value)) return nullptr; + return std::make_unique<IntegerExprAST>(Value); + } auto Operand = ParseUnary(); if (!Operand) return nullptr; return std::make_unique<UnaryExprAST>('-', std::move(Operand)); diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index c47485e..fe53731 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -17,9 +17,7 @@ bool Parser::ParseSignedIntBound(int64_t &Out) { Lex.getLine()); return false; } - Out = Negative ? -Lex.NumVal : Lex.NumVal; - getNextToken(); - return true; + return ParseIntegerValue(Negative, Out); } // ( [BYREF|BYVAL] <name> : <type> @@ -107,7 +105,7 @@ std::optional<std::vector<ParamDecl>> Parser::ParsePrototypeArgs(bool AllowByRef } for (const ParamDecl &Prev : Params) { - if (Prev.Name == P.Name) { + if (sameIdentifier(Prev.Name, P.Name)) { fprintf(stderr, "error: Duplicate parameter name '%s' at line %d\n", P.Name.c_str(), Lex.getLine()); return std::nullopt; diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index 918b7f6..a57c8a6 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -112,19 +112,19 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( const char *Keyword = IsClass ? "CLASS" : "TYPE"; const char *EndKeyword = IsClass ? "ENDCLASS" : "ENDTYPE"; auto AtField = [&] { - return IsClass ? (CurTok == tok_public || CurTok == tok_private) + return IsClass ? (CurTok == tok_public || CurTok == tok_private || CurTok == tok_identifier) : CurTok == tok_declare; }; std::vector<RecordFieldDecl> Fields; bool HadFieldError = false; while (CurTok != EndToken && CurTok != tok_eof) { if (AtField()) { - // Record fields are always public; class fields have no default. - bool IsPublic = !IsClass || CurTok == tok_public; + // Class fields default to public, like record fields. + bool IsPublic = CurTok != tok_private; if (ParseRecordField(Fields, IsPublic)) continue; } else { fprintf(stderr, "error: Expected %s or %s in %s %s at line %d\n", - IsClass ? "PUBLIC or PRIVATE" : "DECLARE", EndKeyword, + IsClass ? "field, PUBLIC or PRIVATE" : "DECLARE", EndKeyword, Keyword, Name.c_str(), Lex.getLine()); } HadFieldError = true; @@ -155,7 +155,7 @@ std::optional<std::vector<RecordFieldDecl>> Parser::ParseRecordBody( bool Parser::ParseRecordField(std::vector<RecordFieldDecl> &Out, bool IsPublic) { const char *Introducer = CurTok == tok_declare ? "DECLARE" : CurTok == tok_public ? "PUBLIC" : "PRIVATE"; - getNextToken(); // eat field introducer + if (CurTok != tok_identifier) getNextToken(); // optional class access keyword if (CurTok != tok_identifier) { fprintf(stderr, "error: Expected field name after %s at line %d\n", Introducer, Lex.getLine()); return false; @@ -213,7 +213,7 @@ std::string Parser::ParseTypeName(bool AllowVoid) { } if (CurTok == tok_identifier) { std::string TypeName = Lex.IdentifierStr; - if (!AllowVoid && TypeName == "VOID") { + if (!AllowVoid && sameIdentifier(TypeName, "VOID")) { fprintf(stderr, "error: VOID is not allowed here\n"); return ""; } @@ -321,9 +321,18 @@ std::unique_ptr<StmtAST> Parser::ParseConstant() { fprintf(stderr, "error: CONSTANT initializer must be a literal at line %d\n", Line); return nullptr; } - auto Value = ParsePrimary(); - if (!Value) return nullptr; - if (Negative) Value = std::make_unique<UnaryExprAST>('-', std::move(Value)); + std::unique_ptr<ExprAST> Value; + if (CurTok == tok_number_int) { + int Line = Lex.getLine(); + int64_t Integer; + if (!ParseIntegerValue(Negative, Integer)) return nullptr; + Value = std::make_unique<IntegerExprAST>(Integer); + Value->setLine(Line); + } else { + Value = ParsePrimary(); + if (!Value) return nullptr; + if (Negative) Value = std::make_unique<UnaryExprAST>('-', std::move(Value)); + } if (GetTokPrecedence() >= 0) { fprintf(stderr, "error: CONSTANT initializer must be a literal, not an expression, at line %d\n", Line); return nullptr; |
