aboutsummaryrefslogtreecommitdiff
path: root/lib/Parser/ParserExpression.cc
blob: 3e2665f01e8e16c8144b9f1716f99a693e3bc0b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

#include "cps/Parser.h"
#include "cps/FunctionAST.h"
#include <cstdio>

using namespace cps;

int Parser::GetTokPrecedence() {
    if (BinopPrecedence.find(CurTok) == BinopPrecedence.end()) return -1;
    int TokPrec = BinopPrecedence[CurTok];
    if (TokPrec <= 0) return -1;
    return TokPrec;
}


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;
}

std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() {
    std::string IdName = Lex.IdentifierStr;
    int Line = Lex.getLine();
    getNextToken();

    if (CurTok == '(') {
        getNextToken();
        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",
                    Lex.getLine());
            return nullptr;
        }
        return std::make_unique<CallExprAST>(IdName, std::move(*Args), Line);
    }

    std::vector<DesignatorAccess> Accesses;
    if (!ParseDesignatorSuffix(Accesses)) return nullptr;

    // Degrade rule: shapes that existed before designators keep their nodes.
    if (Accesses.empty())
        return std::make_unique<VariableExprAST>(IdName);
    if (Accesses.size() == 1 && Accesses[0].Kind == AccessKind::Index)
        return std::make_unique<ArrayAccessExprAST>(IdName, std::move(Accesses[0].Indices), Line);
    return std::make_unique<DesignatorExprAST>(IdName, std::move(Accesses), Line);
}

// Postfix accessor chain after an identifier: '[' exprs ']' | '.' IDENT | '^'
bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out) {
    while (true) {
        if (CurTok == '[') {
            getNextToken();
            auto Indices = ParseExprList(']', false);
            if (!Indices) return false;
            DesignatorAccess A;
            A.Kind = AccessKind::Index;
            A.Indices = std::move(*Indices);
            Out.push_back(std::move(A));
        } else if (CurTok == '.') {
            getNextToken();
            if (CurTok != tok_identifier) {
                fprintf(stderr, "error: Expected field name after '.' at line %d\n", Lex.getLine());
                return false;
            }
            DesignatorAccess A;
            A.Kind = AccessKind::Field;
            A.FieldName = Lex.IdentifierStr;
            Out.push_back(std::move(A));
            getNextToken();
        } else if (CurTok == '^') {
            getNextToken();
            DesignatorAccess A;
            A.Kind = AccessKind::Deref;
            Out.push_back(std::move(A));
        } else {
            return true;
        }
    }
}


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 != ')') {
        fprintf(stderr, "error: expected ')'\n");
        return nullptr;
    }
    getNextToken();
    return V;
}

std::unique_ptr<ExprAST> Parser::ParsePrimary() {
    switch (CurTok) {
    case tok_identifier: return ParseIdentifierExpr();
    case tok_number_int: return ParseNumberExpr();
    case tok_number_real: return ParseNumberExpr();
    case tok_string_literal: {
        auto Res = std::make_unique<StringExprAST>(Lex.StringVal);
        getNextToken();
        return Res;
    }
    case tok_char_literal: {
        auto Res = std::make_unique<CharExprAST>(Lex.CharVal);
        getNextToken();
        return Res;
    }
    case tok_true: {
        getNextToken();
        return std::make_unique<BooleanExprAST>(true);
    }
    case tok_false: {
        getNextToken();
        return std::make_unique<BooleanExprAST>(false);
    }
    case '(':            return ParseParenExpr();
    default:
        fprintf(stderr, "error: unknown token '%c' (%d) at line %d when expecting an expression\n",
                (char)CurTok, CurTok, Lex.getLine());
        return nullptr;
    }
}

std::unique_ptr<ExprAST> Parser::ParseUnary() {
    if (CurTok == tok_not) {
        getNextToken();
        auto Operand = ParseUnary();
        if (!Operand) return nullptr;
        return std::make_unique<UnaryExprAST>(tok_not, std::move(Operand));
    }

    // ^<designator> — address-of; the operand must be an lvalue shape.
    if (CurTok == '^') {
        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",
                    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);
            return nullptr;
        }
        std::vector<DesignatorAccess> Accesses;
        if (!ParseDesignatorSuffix(Accesses)) return nullptr;
        auto Target = std::make_unique<DesignatorExprAST>(BaseName, std::move(Accesses), Line);
        return std::make_unique<AddrOfExprAST>(std::move(Target));
    }

    if (CurTok == '-') {
        getNextToken();
        auto Operand = ParseUnary();
        if (!Operand) return nullptr;
        return std::make_unique<UnaryExprAST>('-', std::move(Operand));
    }

    return ParsePrimary();
}

std::unique_ptr<ExprAST> Parser::ParseBinOpRHS(int ExprPrec, std::unique_ptr<ExprAST> LHS) {
    while (true) {
        int TokPrec = GetTokPrecedence();
        if (TokPrec < ExprPrec) return LHS;

        int BinOp = CurTok;
        int Line = Lex.getLine();
        getNextToken();

        auto RHS = ParseUnary();
        if (!RHS) return nullptr;

        int NextPrec = GetTokPrecedence();
        if (TokPrec < NextPrec) {
            RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS));
            if (!RHS) return nullptr;
        }

        LHS = std::make_unique<BinaryExprAST>(BinOp, std::move(LHS), std::move(RHS), Line);
    }
}

std::unique_ptr<ExprAST> Parser::ParseExpression() {
    auto LHS = ParseUnary();
    if (!LHS) return nullptr;
    return ParseBinOpRHS(0, std::move(LHS));
}


std::optional<std::vector<std::unique_ptr<ExprAST>>> Parser::ParseExprList(char CloseDelim, bool AllowEmpty) {
    std::vector<std::unique_ptr<ExprAST>> Elems;
    if (AllowEmpty && CurTok == CloseDelim) {
        getNextToken();
        return Elems;
    }

    while (true) {
        auto E = ParseExpression();
        if (!E) return std::nullopt;
        Elems.push_back(std::move(E));

        if (CurTok == CloseDelim) break;
        if (CurTok != ',') {
            fprintf(stderr, "error: Expected ',' or '%c' at line %d\n", CloseDelim, Lex.getLine());
            return std::nullopt;
        }
        getNextToken();
    }
    getNextToken();
    return Elems;
}
Value *Fmt = nullptr; // Exhaustive over TypeKind (no default): the compiler warns when a new // kind is added without deciding its OUTPUT story. switch (Info->Kind) { case TypeKind::Real: Fmt = AppendNewline ? PrintfFloatFormatStr : Builder->CreateGlobalString("%f", "fmt_flt_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Val); break; case TypeKind::Boolean: { Value *BoolVal = coerceValueToType(Val, resolveType("BOOLEAN")); Fmt = AppendNewline ? PrintfStringFormatStr : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Builder->CreateSelect(BoolVal, TrueStr, FalseStr)); break; } case TypeKind::String: { Value *StrVal = emitStringNullGuard(Val); Fmt = AppendNewline ? PrintfStringFormatStr : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(StrVal); break; } case TypeKind::Char: { Value *CharVal = coerceValueToType(Val, resolveType("CHAR")); Value *Promoted = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "char_for_printf"); Fmt = AppendNewline ? PrintfCharFormatStr : Builder->CreateGlobalString("%c", "fmt_chr_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(Promoted); break; } case TypeKind::Integer: case TypeKind::Enum: { // an enum prints its ordinal (user decision) Value *IntVal = Info->isEnum() ? Val : coerceValueToType(Val, resolveType("INTEGER")); Fmt = AppendNewline ? PrintfFormatStr : Builder->CreateGlobalString("%lld", "fmt_int_inline", 0, TheModule.get()); Args.push_back(Fmt); Args.push_back(IntVal); break; } case TypeKind::Record: reportError("Cannot OUTPUT a value of record type %s; output its fields individually", Info->Name.c_str()); return; case TypeKind::Pointer: reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str()); return; case TypeKind::CScalar: emitCOutput(Val, Info, AppendNewline); return; case TypeKind::CPointer: reportError("OUTPUT is not supported for type %s", Info->Name.c_str()); return; case TypeKind::Void: reportError("Cannot OUTPUT a VOID value"); return; } Builder->CreateCall(PrintfFunc, Args); } void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false); Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get()); BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F); Builder->SetInsertPoint(BB); Exceptions->prepareFunction(F); BasicBlock *UnhandledBB = BasicBlock::Create(*TheContext, "unhandled_exception", F); Exceptions->pushHandler(UnhandledBB); // User types first: prototypes may name them in parameters/returns. runTypePrePass(Statements); // Pre-register every top-level FUNCTION/PROCEDURE prototype so calls may // precede definitions. for (const auto &Stmt : Statements) { if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt.get())) { FuncGen->emitPrototype(FuncDef->getProto()); } } for (const auto &Stmt : Statements) { emitStmt(Stmt.get()); } if (!Builder->GetInsertBlock()->hasTerminator()) { Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0))); } auto *Exception = Exceptions->popHandler(); Builder->SetInsertPoint(UnhandledBB); Exceptions->beginCatch(Exception); Exceptions->endCatch(); Value *Msg = Builder->CreateGlobalString("Unhandled exception. Program terminated.\n", "err_unhandled", 0, TheModule.get()); Builder->CreateCall(PrintfFunc, {Msg}); Builder->CreateRet(ConstantInt::get(Type::getInt32Ty(*TheContext), 1)); if (verifyModule(*TheModule, &errs())) { errs() << "fatal error: LLVM module verification failed\n"; HadError = true; } } void CodeGen::print(raw_ostream &Output) { TheModule->print(Output, nullptr); } 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)) { return ConstantInt::get(*TheContext, APInt(64, Num->getVal(), true)); } if (auto *Real = dynamic_cast<RealExprAST*>(Expr)) { return ConstantFP::get(*TheContext, APFloat(Real->getVal())); } if (auto *Bool = dynamic_cast<BooleanExprAST*>(Expr)) { return ConstantInt::get(*TheContext, APInt(1, Bool->getVal() ? 1 : 0)); } if (auto *Chr = dynamic_cast<CharExprAST*>(Expr)) { return ConstantInt::get(Type::getInt8Ty(*TheContext), static_cast<unsigned char>(Chr->getVal())); } if (auto *Str = dynamic_cast<StringExprAST*>(Expr)) { return StrHandler->createLiteral(Str->getVal()); } if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (!Info) { // Enum values are bare identifiers; collisions with variables are // rejected at declaration time, so the order here cannot shadow. if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { return ConstantInt::get(*TheContext, APInt(64, static_cast<uint64_t>(EC->Ordinal), true)); } reportError("Unknown variable name %s", Var->getName().c_str()); return nullptr; } if (Info->IsArray) { return Info->Storage; } const TypeInfo *TypeInfo = resolveType(Info->TypeName); if (!TypeInfo || !TypeInfo->LLVMType) { reportError("Unknown type for variable %s", Var->getName().c_str()); return nullptr; } Value *Loaded = Builder->CreateLoad(TypeInfo->LLVMType, Info->Storage, Var->getName().c_str()); if (TypeInfo->isString()) { Loaded = emitStringNullGuard(Loaded); } return Loaded; } if (auto *ArrAcc = dynamic_cast<ArrayAccessExprAST*>(Expr)) { return Arrays->emitArrayAccess(ArrAcc, *this); } if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { if (Unary->getOp() == tok_not) { const TypeInfo *Info = getExprTypeInfo(Unary->getOperand()); Value *Operand = Info && Info->isC() ? convertNumericValue(emitExpr(Unary->getOperand()), Info, resolveType("BOOLEAN")) : emitCoercedExpr(Unary->getOperand(), resolveType("BOOLEAN")); if (Info && Info->Kind == TypeKind::CPointer) { reportError("NOT does not support C pointer operands"); return nullptr; } if (!Operand) return nullptr; return Builder->CreateNot(Operand, "nottmp"); } if (Unary->getOp() == '-') { const TypeInfo *Info = getExprTypeInfo(Unary->getOperand()); if (Info && Info->Kind == TypeKind::CScalar && !Info->isVoid()) { const TypeInfo *Promoted = Types->promoteCScalar(Info); Value *Operand = convertNumericValue(emitExpr(Unary->getOperand()), Info, Promoted); if (!Operand) return nullptr; return Promoted->isReal() ? Builder->CreateFNeg(Operand, "c_neg") : Builder->CreateNeg(Operand, "c_neg"); } 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; } if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { // Whole arrays evaluate to raw data pointers and ^x to raw addresses; // neither is a value any operator may consume. if (isWholeArrayVar(Bin->getLHS()) || isWholeArrayVar(Bin->getRHS())) { reportError("A whole array cannot be used as an operand; index it"); return nullptr; } if (dynamic_cast<AddrOfExprAST*>(Bin->getLHS()) || dynamic_cast<AddrOfExprAST*>(Bin->getRHS())) { reportError("'^' (address-of) can only be used where a pointer type is expected"); return nullptr; } // Enum/Record/Pointer operands never reach ArithmeticHandler or the // '&' path: their admissible operations are decided by name here. const TypeInfo *LT = getExprTypeInfo(Bin->getLHS()); const TypeInfo *RT = getExprTypeInfo(Bin->getRHS()); if ((LT && LT->isC()) || (RT && RT->isC())) { return emitCBinaryOp(Bin, LT, RT); } if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) { return emitUserKindBinaryOp(Bin, LT, RT); } Value *L = emitExpr(Bin->getLHS()); Value *R = emitExpr(Bin->getRHS()); if (!L || !R) return nullptr; if (Bin->getOp() == '&') { L = coerceValueToType(L, resolveType("STRING")); R = coerceValueToType(R, resolveType("STRING")); if (!L || !R) return nullptr; return StrHandler->emitConcat(L, R); } // STRING and explicit pointers share LLVM's ptr representation; // string comparisons must dispatch on the pseudocode type. switch (Bin->getOp()) { 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)", Bin->getLine()); return nullptr; } return StrHandler->emitCompare(Bin->getOp(), L, R); } break; default: break; } return ArithHandler->emitBinaryOp(Bin->getOp(), L, R, Bin->getLine()); } if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { std::string Name = Call->getCallee(); if (const BuiltinInfo *B = findBuiltin(Name)) { if (Call->getArgs().size() != B->Arity) { reportError("%s expects %u arg%s", B->Name, B->Arity, B->Arity == 1 ? "" : "s"); return nullptr; } } if (Name == "LENGTH") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrHandler->emitLength(Str); } if (Name == "MID") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); Value *Start = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); Value *Len = emitCoercedExpr(Call->getArgs()[2].get(), resolveType("INTEGER")); if (!Str || !Start || !Len) return nullptr; return StrHandler->emitMid(Str, Start, Len); } if (Name == "RIGHT") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); if (!Str || !Len) return nullptr; return StrHandler->emitRight(Str, Len); } if (Name == "LEFT") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER")); if (!Str || !Len) return nullptr; return StrHandler->emitLeft(Str, Len); } if (Name == "LCASE") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrHandler->emitLCase(Str); } if (Name == "UCASE") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrHandler->emitUCase(Str); } if (Name == "ASC") { const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get()); if (ArgType && (ArgType->isUserKind() || ArgType->isC())) { reportError("ASC expects a CHAR or STRING (got %s)", ArgType->Name.c_str()); return nullptr; } Value *ArgVal = emitExpr(Call->getArgs()[0].get()); if (!ArgVal) return nullptr; Value *CharVal = nullptr; if (ArgType && ArgType->isChar()) { CharVal = coerceValueToType(ArgVal, resolveType("CHAR")); } else { CharVal = Builder->CreateLoad(Type::getInt8Ty(*TheContext), ArgVal, "char_load"); } Value *AscVal = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "asc_val"); return coerceValueToType(AscVal, resolveType("INTEGER")); } if (Name == "CHR") { Value *IntVal = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("INTEGER")); if (!IntVal) return nullptr; Value *CharVal = Builder->CreateTrunc(IntVal, Type::getInt8Ty(*TheContext), "chr_val"); return coerceValueToType(CharVal, resolveType("STRING")); } if (Name == "IS_NUM") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrConvHandler->emitIsNum(Str); } if (Name == "NUM_TO_STR") { const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get()); if (ArgType && (ArgType->isUserKind() || ArgType->isC())) { reportError("NUM_TO_STR expects a number (got %s)", ArgType->Name.c_str()); return nullptr; } Value *NumV = emitExpr(Call->getArgs()[0].get()); if (!NumV) return nullptr; bool IsReal = NumV->getType()->isDoubleTy(); if (NumV->getType()->isIntegerTy(8)) { NumV = coerceValueToType(NumV, resolveType("INTEGER")); } return StrConvHandler->emitNumToStr(NumV, IsReal); } if (Name == "STR_TO_NUM") { Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!Str) return nullptr; return StrConvHandler->emitStrToNum(Str, true); } if (Name == "EOF") { if (isWholeArrayVar(Call->getArgs()[0].get())) { reportError("Cannot use a whole array as the EOF filename"); return nullptr; } Value *FileName = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING")); if (!FileName) return nullptr; return Files->emitEof(FileName, Call->getLine()); } std::vector<Value*> Args; if (!marshalCallArgs(Name, Call->getArgs(), Call->getLine(), Args)) return nullptr; return FuncGen->emitCallExpr(Call, Args); } if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { LValueInfo LV; if (!emitLValue(D, LV)) return nullptr; return loadFromLValue(LV); } // ^x produces a raw address; it is only admissible where a pointer type // is expected, which emitCoercedExpr enforces by pointee name. if (auto *Addr = dynamic_cast<AddrOfExprAST*>(Expr)) { LValueInfo LV; if (!emitLValue(Addr->getTarget(), LV)) return nullptr; return LV.Addr; } return nullptr; } // Collects the arguments for a call to a user FUNCTION/PROCEDURE, dispatching // on the *declared* signature: BYREF parameters take the variable's alloca, // BYVAL parameters are evaluated and coerced to the declared parameter type. bool CodeGen::marshalCallArgs(const std::string &Callee, const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line, std::vector<Value*> &Out) { const FuncSig *Sig = FuncGen->getSignature(Callee); if (!Sig) { reportError("Call to undefined function %s", Callee.c_str()); return false; } if (ArgExprs.size() != Sig->Params.size()) { reportError("Incorrect # arguments passed to %s", Callee.c_str()); return false; } for (size_t i = 0; i < ArgExprs.size(); ++i) { const ParamSig &P = Sig->Params[i]; const std::string &TypeName = P.TypeName; bool IsByRef = P.IsByRef; ExprAST *ArgExpr = ArgExprs[i].get(); if (P.IsArray) { if (!Arrays->emitArrayArgument(ArgExpr, P.TypeName, P.Rank, P.DeclaredBounds, Callee, Line, *this, Out)) { return false; } continue; } // A whole-array argument against a scalar parameter is always a user // error (and would smash the data pointer if let through). if (auto *Var = dynamic_cast<VariableExprAST*>(ArgExpr)) { const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (Info && Info->IsArray) { reportError("Cannot pass whole array %s to %s; the parameter is not an array (declare it ARRAY OF %s)", Var->getName().c_str(), Callee.c_str(), TypeName.c_str()); return false; } } if (IsByRef) { auto *Var = dynamic_cast<VariableExprAST*>(ArgExpr); if (!Var) { reportError("BYREF argument to %s must be a variable", Callee.c_str()); return false; } const SymbolInfo *Info = getSymbolInfo(Var->getName()); if (!Info) { reportError("Unknown variable %s in BYREF call", Var->getName().c_str()); return false; } // BYREF aliases the storage, so the types must match exactly. if (!Types->sameType(Info->TypeName, TypeName)) { reportError("BYREF argument %s to %s must be of type %s (got %s)", Var->getName().c_str(), Callee.c_str(), TypeName.c_str(), Info->TypeName.c_str()); return false; } Out.push_back(Info->Storage); } else { Value *V = emitCoercedExpr(ArgExpr, resolveType(TypeName)); if (!V) return false; Out.push_back(V); } } return true; } void CodeGen::emitTryStmt(TryStmtAST *Stmt) { auto *F = Builder->GetInsertBlock()->getParent(); auto *CatchBB = BasicBlock::Create(*TheContext, "catch", F); auto *AfterBB = BasicBlock::Create(*TheContext, "try_cont", F); auto SavedArrays = Arrays->snapshotTable(); Exceptions->pushHandler(CatchBB); for (const auto &S : Stmt->getBody()) emitStmt(S.get()); auto *Exception = Exceptions->popHandler(); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); Arrays->exchangeTable(SavedArrays); Builder->SetInsertPoint(CatchBB); Exceptions->beginCatch(Exception); for (const auto &S : Stmt->getCatchBody()) emitStmt(S.get()); Exceptions->endCatch(); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); Arrays->exchangeTable(std::move(SavedArrays)); Builder->SetInsertPoint(AfterBB); } void CodeGen::emitIfStmt(IfStmtAST *Stmt) { Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; Function *TheFunction = Builder->GetInsertBlock()->getParent(); BasicBlock *ThenBB = BasicBlock::Create(*TheContext, "then", TheFunction); BasicBlock *ElseBB = BasicBlock::Create(*TheContext, "else", TheFunction); BasicBlock *MergeBB = BasicBlock::Create(*TheContext, "ifcont", TheFunction); Builder->CreateCondBr(CondV, ThenBB, ElseBB); // Arrays are block-scoped: metadata SSA values and the buffer setup live // inside the branch, so a declaration must not survive it. auto SavedArrays = Arrays->snapshotTable(); Builder->SetInsertPoint(ThenBB); for (const auto &S : Stmt->getThenStmts()) emitStmt(S.get()); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); Arrays->exchangeTable(SavedArrays); Builder->SetInsertPoint(ElseBB); for (const auto &S : Stmt->getElseStmts()) emitStmt(S.get()); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); Arrays->exchangeTable(std::move(SavedArrays)); Builder->SetInsertPoint(MergeBB); } void CodeGen::emitWhileStmt(WhileStmtAST *Stmt) { Function *TheFunction = Builder->GetInsertBlock()->getParent(); BasicBlock *CondBB = BasicBlock::Create(*TheContext, "whilecond", TheFunction); BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "whileloop", TheFunction); BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "whilecont", TheFunction); Builder->CreateBr(CondBB); Builder->SetInsertPoint(CondBB); Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; Builder->CreateCondBr(CondV, LoopBB, AfterBB); // Arrays declared in the body are block-scoped (see emitIfStmt). auto SavedArrays = Arrays->snapshotTable(); Builder->SetInsertPoint(LoopBB); for (const auto &S : Stmt->getBody()) emitStmt(S.get()); Arrays->exchangeTable(std::move(SavedArrays)); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(CondBB); Builder->SetInsertPoint(AfterBB); } void CodeGen::emitRepeatStmt(RepeatStmtAST *Stmt) { Function *TheFunction = Builder->GetInsertBlock()->getParent(); BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "repeatloop", TheFunction); BasicBlock *CondBB = BasicBlock::Create(*TheContext, "repeatcond", TheFunction); BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "repeatcont", TheFunction); Builder->CreateBr(LoopBB); // Arrays declared in the body are block-scoped (see emitIfStmt). auto SavedArrays = Arrays->snapshotTable(); Builder->SetInsertPoint(LoopBB); for (const auto &S : Stmt->getBody()) emitStmt(S.get()); Arrays->exchangeTable(std::move(SavedArrays)); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(CondBB); Builder->SetInsertPoint(CondBB); Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; Builder->CreateCondBr(CondV, AfterBB, LoopBB); Builder->SetInsertPoint(AfterBB); } void CodeGen::emitForStmt(ForStmtAST *Stmt) { Function *TheFunction = Builder->GetInsertBlock()->getParent(); std::string VarName = Stmt->getVarName(); const SymbolInfo *Symbol = getSymbolInfo(VarName); if (!Symbol) { reportError("Unknown variable in FOR loop %s", VarName.c_str()); return; } if (Symbol->TypeName != "INTEGER") { reportError("FOR loop variable %s must be INTEGER", VarName.c_str()); return; } Value *StartVal = emitCoercedExpr(Stmt->getStart(), resolveType("INTEGER")); if (!StartVal) return; Value *Alloca = Symbol->Storage; Builder->CreateStore(StartVal, Alloca); // STEP is evaluated exactly once, before the loop. Value *StepVal = nullptr; if (Stmt->getStep()) { StepVal = emitCoercedExpr(Stmt->getStep(), resolveType("INTEGER")); if (!StepVal) return; } else { StepVal = ConstantInt::get(*TheContext, APInt(64, 1)); } BasicBlock *CondBB = BasicBlock::Create(*TheContext, "forcond", TheFunction); BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "forloop", TheFunction); BasicBlock *IncBB = BasicBlock::Create(*TheContext, "forinc", TheFunction); BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "forcont", TheFunction); Builder->CreateBr(CondBB); Builder->SetInsertPoint(CondBB); Value *CurVar = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); Value *EndVal = emitCoercedExpr(Stmt->getEnd(), resolveType("INTEGER")); if (!EndVal) return; // The loop direction depends on the STEP value at run time. Value *StepNonNeg = Builder->CreateICmpSGE(StepVal, ConstantInt::get(*TheContext, APInt(64, 0)), "step_nonneg"); Value *CondUp = Builder->CreateICmpSLE(CurVar, EndVal, "forcond_le"); Value *CondDown = Builder->CreateICmpSGE(CurVar, EndVal, "forcond_ge"); Value *CondV = Builder->CreateSelect(StepNonNeg, CondUp, CondDown, "forcond"); Builder->CreateCondBr(CondV, LoopBB, AfterBB); // Arrays declared in the body are block-scoped (see emitIfStmt). auto SavedArrays = Arrays->snapshotTable(); Builder->SetInsertPoint(LoopBB); for (const auto &S : Stmt->getBody()) emitStmt(S.get()); Arrays->exchangeTable(std::move(SavedArrays)); if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(IncBB); Builder->SetInsertPoint(IncBB); Value *CurValForInc = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); Value *NextVal = Builder->CreateAdd(CurValForInc, StepVal, "nextval"); Builder->CreateStore(NextVal, Alloca); Builder->CreateBr(CondBB); Builder->SetInsertPoint(AfterBB); } void CodeGen::emitStmt(StmtAST *Stmt) { if (!Stmt) return; if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt)) { BasicBlock *SavedBlock = Builder->GetInsertBlock(); // Function bodies get a fresh array scope (metadata SSA values belong // to the emitting function), mirroring FunctionGen's Symbols handling. auto SavedArrays = Arrays->exchangeTable({}); FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { this->emitStmt(S); }); Arrays->exchangeTable(std::move(SavedArrays)); if (SavedBlock) Builder->SetInsertPoint(SavedBlock); return; } // Keep diagnosing statements after RETURN/THROW without appending code // to a terminated block or making those statements reachable. if (Builder->GetInsertBlock()->hasTerminator()) { auto *F = Builder->GetInsertBlock()->getParent(); Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); } if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) { emitTryStmt(Try); return; } if (dynamic_cast<ThrowStmtAST*>(Stmt)) { Exceptions->emitThrow(); return; } if (auto *ArrDecl = dynamic_cast<ArrayDeclareStmtAST*>(Stmt)) { Arrays->emitArrayDeclare(ArrDecl, *this); return; } if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { Arrays->emitArrayAssign(ArrAssign, *this); return; } if (auto *Call = dynamic_cast<CallStmtAST*>(Stmt)) { std::vector<Value*> Args;