aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-14 03:31:47 +0000
committer_ov4 <ov4@ov4.org>2026-10-14 03:31:47 +0000
commit2147449f4d6f37b4624990ff26bce58ebf3cfb4b (patch)
treebbf09cea539b8ea01ee3d392ec828067552fa4b7
parent699e18b65b07af73ccd14b28f83572b1843c7d37 (diff)
feat(parser): add explicit parenthesized type casts
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/cps/CodeGen.h4
-rw-r--r--include/cps/Parser.h5
-rw-r--r--include/cps/TypeAST.h10
-rw-r--r--lib/CodeGen/CTypeGen.cc79
-rw-r--r--lib/CodeGen/CodeGen.cc19
-rw-r--r--lib/CodeGen/Designator.cc3
-rw-r--r--lib/Parser/Parser.cc21
-rw-r--r--lib/Parser/ParserType.cc1
9 files changed, 139 insertions, 4 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6048a9b..679ba62 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,7 @@ add_library(CPSParser STATIC
add_library(CPSCodeGen STATIC
lib/CodeGen/CodeGen.cc
lib/CodeGen/Designator.cc
+ lib/CodeGen/CTypeGen.cc
lib/CodeGen/ArrayHandler.cc
lib/CodeGen/RuntimeCheck.cc
lib/CodeGen/ExceptionHandler.cc
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index acd6cb2..5043417 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -20,6 +20,7 @@ namespace cps {
class ArrayHandler;
class FileHandler;
class ExceptionHandler;
+class CastExprAST;
class CodeGen {
std::unique_ptr<llvm::LLVMContext> TheContext;
@@ -66,6 +67,9 @@ class CodeGen {
bool isWholeArrayVar(ExprAST *Expr) const;
const TypeInfo *getExprTypeInfo(ExprAST *Expr) const;
llvm::Value *coerceValueToType(llvm::Value *Val, const TypeInfo *TargetInfo);
+ llvm::Value *convertNumericValue(llvm::Value *Val, const TypeInfo *Source,
+ const TypeInfo *Target);
+ llvm::Value *emitCast(CastExprAST *Cast);
// TYPE-system helpers (Designator.cc).
void runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements);
diff --git a/include/cps/Parser.h b/include/cps/Parser.h
index 49a9791..5713587 100644
--- a/include/cps/Parser.h
+++ b/include/cps/Parser.h
@@ -10,6 +10,7 @@
#include <map>
#include <optional>
#include <string>
+#include <set>
namespace cps {
@@ -20,6 +21,10 @@ class Parser {
unsigned BlockDepth = 0; // ParseBlock nesting; 1 = top level
std::map<int, int> BinopPrecedence;
+ std::set<std::string> TypeNames = {
+ "INTEGER", "REAL", "BOOLEAN", "STRING", "CHAR", "VOID",
+ "C_INT", "C_CHAR", "C_STRING", "C_DOUBLE"
+ };
int getNextToken();
int GetTokPrecedence();
diff --git a/include/cps/TypeAST.h b/include/cps/TypeAST.h
index 95c1d6d..873ca4a 100644
--- a/include/cps/TypeAST.h
+++ b/include/cps/TypeAST.h
@@ -8,6 +8,16 @@
namespace cps {
+class CastExprAST : public ExprAST {
+ std::string TypeName;
+ std::unique_ptr<ExprAST> Operand;
+public:
+ CastExprAST(std::string TypeName, std::unique_ptr<ExprAST> Operand)
+ : TypeName(std::move(TypeName)), Operand(std::move(Operand)) {}
+ const std::string &getTypeName() const { return TypeName; }
+ ExprAST *getOperand() const { return Operand.get(); }
+};
+
// TYPE <name> = C_TYPE("<C type expression>")
class CTypeDeclAST : public StmtAST {
std::string Name;
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc
new file mode 100644
index 0000000..03f398f
--- /dev/null
+++ b/lib/CodeGen/CTypeGen.cc
@@ -0,0 +1,79 @@
+#include "cps/CodeGen.h"
+#include "cps/TypeAST.h"
+
+using namespace llvm;
+using namespace cps;
+
+namespace {
+
+bool isScalar(const TypeInfo *Info) {
+ if (!Info || Info->isVoid()) return false;
+ return Info->Kind == TypeKind::CScalar || Info->Kind == TypeKind::Integer ||
+ Info->Kind == TypeKind::Real || Info->Kind == TypeKind::Boolean ||
+ Info->Kind == TypeKind::Char;
+}
+
+bool isSigned(const TypeInfo *Info) {
+ return Info->isC() ? Info->CSigned : Info->Kind == TypeKind::Integer;
+}
+
+} // namespace
+
+// Source type identity is essential: native signed char and pseudocode CHAR
+// can share i8 while requiring different extensions and FP conversions.
+Value *CodeGen::convertNumericValue(Value *Val, const TypeInfo *Source, const TypeInfo *Target) {
+ if (!Val || !isScalar(Source) || !isScalar(Target)) return nullptr;
+ Type *From = Val->getType();
+ Type *To = Target->LLVMType;
+ if (From == To) return Val;
+ if (To->isIntegerTy(1)) {
+ if (From->isIntegerTy())
+ return Builder->CreateICmpNE(Val, Constant::getNullValue(From), "cast_bool");
+ return Builder->CreateFCmpUNE(Val, ConstantFP::get(From, 0.0), "cast_bool");
+ }
+ if (From->isIntegerTy() && To->isIntegerTy())
+ return Builder->CreateIntCast(Val, To, isSigned(Source), "cast_int");
+ if (From->isIntegerTy() && To->isFloatingPointTy())
+ return isSigned(Source) ? Builder->CreateSIToFP(Val, To, "cast_fp")
+ : Builder->CreateUIToFP(Val, To, "cast_fp");
+ if (From->isFloatingPointTy() && To->isIntegerTy())
+ return isSigned(Target) ? Builder->CreateFPToSI(Val, To, "cast_int")
+ : Builder->CreateFPToUI(Val, To, "cast_int");
+ return Builder->CreateFPCast(Val, To, "cast_fp");
+}
+
+Value *CodeGen::emitCast(CastExprAST *Cast) {
+ const TypeInfo *Target = resolveType(Cast->getTypeName());
+ ExprAST *Operand = Cast->getOperand();
+ if (!Target || Target->isVoid()) {
+ reportError("Invalid cast target %s", Cast->getTypeName().c_str());
+ return nullptr;
+ }
+ if (isWholeArrayVar(Operand)) {
+ reportError("Cannot cast a whole array; index it");
+ return nullptr;
+ }
+ if (auto *Address = dynamic_cast<AddrOfExprAST*>(Operand)) {
+ LValueInfo LV;
+ if (!emitLValue(Address->getTarget(), LV)) return nullptr;
+ const TypeInfo *Pointee = resolveType(LV.TypeName);
+ if (Target->Kind == TypeKind::CPointer && Pointee && Pointee->isC()) return LV.Addr;
+ reportError("Address casts require a C pointee and C pointer target");
+ return nullptr;
+ }
+ const TypeInfo *Source = getExprTypeInfo(Operand);
+ if (!Source || Source->isVoid() || Source->isUserKind() || Target->isUserKind()) {
+ reportError("Unsupported operand or target for cast to %s", Target->Name.c_str());
+ return nullptr;
+ }
+ if (isScalar(Source) && isScalar(Target))
+ return convertNumericValue(emitExpr(Operand), Source, Target);
+ if (Source->Kind == TypeKind::CPointer && Target->Kind == TypeKind::CPointer)
+ return emitExpr(Operand);
+ if (Source->isString() && Target->CIdentity == "char*") return emitExpr(Operand);
+ if (Source->CIdentity == "char*" && Target->isString())
+ return emitStringNullGuard(emitExpr(Operand));
+ if (Source->isString() && Target->isString()) return emitExpr(Operand);
+ reportError("Cannot cast %s to %s", Source->Name.c_str(), Target->Name.c_str());
+ return nullptr;
+}
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 8c01972..ce34ebc 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -185,6 +185,8 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const {
if (dynamic_cast<BooleanExprAST*>(Expr)) return resolveType("BOOLEAN");
if (dynamic_cast<CharExprAST*>(Expr)) return resolveType("CHAR");
if (dynamic_cast<StringExprAST*>(Expr)) return resolveType("STRING");
+ if (auto *Cast = dynamic_cast<CastExprAST*>(Expr))
+ return resolveType(Cast->getTypeName());
if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) {
const SymbolInfo *Info = getSymbolInfo(Var->getName());
@@ -240,7 +242,10 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const {
if (Unary->getOp() == tok_not) {
return resolveType("BOOLEAN");
}
- return getExprTypeInfo(Unary->getOperand());
+ const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand());
+ if (Operand && !Operand->isC() && !Operand->isUserKind())
+ return Operand->isReal() ? resolveType("REAL") : resolveType("INTEGER");
+ return nullptr;
}
if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) {
@@ -572,6 +577,7 @@ void CodeGen::print(raw_ostream &Output) {
}
Value *CodeGen::emitExpr(ExprAST *Expr) {
+ if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) return emitCast(Cast);
if (!Expr) return nullptr;
if (auto *Num = dynamic_cast<IntegerExprAST*>(Expr)) {
@@ -632,6 +638,17 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
if (!Operand) return nullptr;
return Builder->CreateNot(Operand, "nottmp");
}
+ if (Unary->getOp() == '-') {
+ const TypeInfo *Info = getExprTypeInfo(Unary->getOperand());
+ if (!Info || Info->isC() || Info->isUserKind() || Info->isString() || Info->isVoid()) {
+ reportError("Unary '-' requires a supported numeric operand");
+ return nullptr;
+ }
+ Value *Operand = emitExpr(Unary->getOperand());
+ if (!Operand) return nullptr;
+ return ArithHandler->emitBinaryOp('-', ConstantInt::get(Type::getInt64Ty(*TheContext), 0),
+ Operand, 0);
+ }
return nullptr;
}
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index c7d0796..019c19e 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -231,6 +231,9 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
return nullptr;
}
if (Src && Src->sameType(Target) && !Target->isVoid()) return emitExpr(E);
+ if (Src && Src->Kind == TypeKind::CScalar && Target->Kind == TypeKind::CScalar &&
+ !Src->isVoid() && !Target->isVoid())
+ return convertNumericValue(emitExpr(E), Src, Target);
reportError("Cannot implicitly convert %s to %s; use an explicit cast",
Src ? Src->Name.c_str() : "expression", Target->Name.c_str());
return nullptr;
diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc
index 3bc095e..db2790b 100644
--- a/lib/Parser/Parser.cc
+++ b/lib/Parser/Parser.cc
@@ -155,6 +155,23 @@ std::string Parser::ParseTypeName(bool AllowVoid) {
std::unique_ptr<ExprAST> Parser::ParseParenExpr() {
getNextToken();
+ bool IsType = CurTok == tok_integer_kw || CurTok == tok_real_kw ||
+ CurTok == tok_boolean_kw || CurTok == tok_string_kw || CurTok == tok_char_kw ||
+ CurTok == tok_c_int || CurTok == tok_c_char || CurTok == tok_c_string ||
+ CurTok == tok_c_double ||
+ (CurTok == tok_identifier && TypeNames.count(Lex.IdentifierStr));
+ if (IsType) {
+ std::string TypeName = ParseTypeName();
+ if (TypeName.empty()) return nullptr;
+ if (CurTok != ')') {
+ fprintf(stderr, "error: Expected ')' after cast type at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ getNextToken();
+ auto Operand = ParseUnary();
+ if (!Operand) return nullptr;
+ return std::make_unique<CastExprAST>(TypeName, std::move(Operand));
+ }
auto V = ParseExpression();
if (!V) return nullptr;
if (CurTok != ')') {
@@ -226,12 +243,10 @@ std::unique_ptr<ExprAST> Parser::ParseUnary() {
}
if (CurTok == '-') {
- int Line = Lex.getLine();
getNextToken();
auto Operand = ParseUnary();
if (!Operand) return nullptr;
- auto Zero = std::make_unique<IntegerExprAST>(0);
- return std::make_unique<BinaryExprAST>('-', std::move(Zero), std::move(Operand), Line);
+ return std::make_unique<UnaryExprAST>('-', std::move(Operand));
}
return ParsePrimary();
diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc
index e249c4c..de1f3ea 100644
--- a/lib/Parser/ParserType.cc
+++ b/lib/Parser/ParserType.cc
@@ -101,6 +101,7 @@ std::unique_ptr<StmtAST> Parser::ParseTypeDecl() {
fprintf(stderr, "error: %s declarations are only allowed at top level at line %d\n", Keyword, Line);
return nullptr;
}
+ TypeNames.insert(Name);
return Decl;
}