aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/CTypeGen.cc20
-rw-r--r--lib/CodeGen/ControlFlowGen.cc58
-rw-r--r--lib/CodeGen/ExpressionGen.cc59
-rw-r--r--lib/CodeGen/StatementGen.cc4
-rw-r--r--lib/CodeGen/TypeCheckGen.cc15
-rw-r--r--lib/Lexer/Lexer.cc46
-rw-r--r--lib/Parser/Parser.cc18
-rw-r--r--lib/Parser/ParserControlFlow.cc86
-rw-r--r--lib/Parser/ParserExpression.cc4
-rw-r--r--lib/Parser/ParserFunction.cc3
10 files changed, 251 insertions, 62 deletions
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc
index 22ecb9f..a20306e 100644
--- a/lib/CodeGen/CTypeGen.cc
+++ b/lib/CodeGen/CTypeGen.cc
@@ -78,10 +78,10 @@ Value *CodeGen::emitCast(CastExprAST *Cast) {
return nullptr;
}
-Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const TypeInfo *Right) {
- int Op = Bin->getOp();
+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)", Bin->getLine());
+ reportError("C and pseudocode operands require an explicit cast (line %d)", Line);
return nullptr;
}
if (Left->isPointer() || Right->isPointer()) {
@@ -90,9 +90,6 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty
reportError("C pointers support only = and <> between the same C pointer type");
return nullptr;
}
- Value *L = emitExpr(Bin->getLHS());
- Value *R = emitExpr(Bin->getRHS());
- if (!L || !R) return nullptr;
return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "c_ptr_eq")
: Builder->CreateICmpNE(L, R, "c_ptr_ne");
}
@@ -102,12 +99,9 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty
return nullptr;
}
if (Op == '&' || ((Op == tok_div || Op == tok_mod) && !Common->isIntegral())) {
- reportError("Operator is not supported for these C operand types (line %d)", Bin->getLine());
+ reportError("Operator is not supported for these C operand types (line %d)", Line);
return nullptr;
}
- Value *L = emitExpr(Bin->getLHS());
- Value *R = emitExpr(Bin->getRHS());
- if (!L || !R) return nullptr;
if (Op == tok_and || Op == tok_or) {
L = convertNumericValue(L, Left, resolveType("BOOLEAN"));
R = convertNumericValue(R, Right, resolveType("BOOLEAN"));
@@ -119,7 +113,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty
if (Op == '/') {
L = convertNumericValue(L, Left, resolveType("REAL"));
R = convertNumericValue(R, Right, resolveType("REAL"));
- RuntimeChecker->emitDivZeroCheck(R, Bin->getLine());
+ RuntimeChecker->emitDivZeroCheck(R, Line);
return Builder->CreateFDiv(L, R, "c_real_div");
}
L = convertNumericValue(L, Left, Common);
@@ -142,7 +136,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty
case '-': return Builder->CreateSub(L, R, "c_sub");
case '*': return Builder->CreateMul(L, R, "c_mul");
case tok_div: case tok_mod:
- RuntimeChecker->emitDivZeroCheck(R, Bin->getLine());
+ RuntimeChecker->emitDivZeroCheck(R, Line);
if (Op == tok_div)
return Common->CSigned ? Builder->CreateSDiv(L, R, "c_div")
: Builder->CreateUDiv(L, R, "c_div");
@@ -160,7 +154,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty
: Builder->CreateICmpUGE(L, R, "c_ge");
}
}
- reportError("Unsupported operator for C operands (line %d)", Bin->getLine());
+ reportError("Unsupported operator for C operands (line %d)", Line);
return nullptr;
}
diff --git a/lib/CodeGen/ControlFlowGen.cc b/lib/CodeGen/ControlFlowGen.cc
index ebd5d3c..f9300dd 100644
--- a/lib/CodeGen/ControlFlowGen.cc
+++ b/lib/CodeGen/ControlFlowGen.cc
@@ -173,3 +173,61 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) {
Builder->SetInsertPoint(AfterBB);
}
+
+void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) {
+ VariableExprAST Selector(Stmt->getSelector());
+ if (isWholeArrayVar(&Selector)) {
+ reportError("CASE selector cannot be a whole array");
+ return;
+ }
+ const TypeInfo *SelectorType = getExprTypeInfo(&Selector);
+ Value *Selected = emitExpr(&Selector);
+ if (!Selected || !SelectorType) return;
+
+ auto *F = Builder->GetInsertBlock()->getParent();
+ F->addFnAttr("no-jump-tables", "true");
+ auto *After = BasicBlock::Create(*TheContext, "case_cont", F);
+ auto SavedArrays = Arrays->snapshotTable();
+ for (const auto &Branch : Stmt->getBranches()) {
+ auto *Body = BasicBlock::Create(*TheContext, "case_body", F);
+ auto *Next = BasicBlock::Create(*TheContext, "case_next", F);
+ auto Compare = [&](int Op, ExprAST *Label) -> Value * {
+ if (isWholeArrayVar(Label)) {
+ reportError("CASE label cannot be a whole array");
+ return nullptr;
+ }
+ const TypeInfo *LabelType = getExprTypeInfo(Label);
+ if (!LabelType) {
+ reportError("Cannot determine CASE label type at line %d", Branch.Line);
+ return nullptr;
+ }
+ Value *LabelValue = emitExpr(Label);
+ return emitBinaryOp(Op, Selected, SelectorType, LabelValue, LabelType, Branch.Line);
+ };
+ if (!Branch.Lower) {
+ Builder->CreateBr(Body);
+ } else {
+ Value *Match = Compare(Branch.Upper ? tok_ge : tok_eq, Branch.Lower.get());
+ if (!Match) {
+ // Finish the CFG even after a diagnostic; the driver discards it.
+ Builder->CreateBr(Next);
+ } else if (Branch.Upper) {
+ auto *Upper = BasicBlock::Create(*TheContext, "case_upper", F);
+ Builder->CreateCondBr(Match, Upper, Next);
+ Builder->SetInsertPoint(Upper);
+ Match = Compare(tok_le, Branch.Upper.get());
+ if (Match) Builder->CreateCondBr(Match, Body, Next);
+ else Builder->CreateBr(Next);
+ } else {
+ Builder->CreateCondBr(Match, Body, Next);
+ }
+ }
+ Builder->SetInsertPoint(Body);
+ for (const auto &S : Branch.Body) emitStmt(S.get());
+ if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(After);
+ Arrays->exchangeTable(SavedArrays);
+ Builder->SetInsertPoint(Next);
+ }
+ Builder->CreateBr(After);
+ Builder->SetInsertPoint(After);
+}
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 0d69408..9135d6c 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -117,38 +117,9 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
// '&' 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());
+ return emitBinaryOp(Bin->getOp(), L, LT, R, RT, Bin->getLine());
}
if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) {
@@ -271,3 +242,31 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return nullptr;
}
+
+// 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) {
+ if (!L || !R) return nullptr;
+ if ((LT && LT->isC()) || (RT && RT->isC()))
+ return emitCBinaryOp(Op, L, LT, R, RT, Line);
+ if ((LT && LT->isUserKind()) || (RT && RT->isUserKind()))
+ return emitUserKindBinaryOp(Op, L, LT, R, RT, Line);
+ if (Op == '&') {
+ L = coerceValueToType(L, resolveType("STRING"));
+ R = coerceValueToType(R, resolveType("STRING"));
+ return L && R ? StrHandler->emitConcat(L, R) : nullptr;
+ }
+ switch (Op) {
+ 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);
+ return nullptr;
+ }
+ return StrHandler->emitCompare(Op, L, R);
+ }
+ break;
+ default: break;
+ }
+ return ArithHandler->emitBinaryOp(Op, L, R, Line);
+}
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index f10d78b..8f8711b 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -248,6 +248,10 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
return;
}
+ if (auto *Case = dynamic_cast<CaseStmtAST*>(Stmt)) {
+ emitCaseStmt(Case);
+ return;
+ }
if (auto *IfStmt = dynamic_cast<IfStmtAST*>(Stmt)) {
emitIfStmt(IfStmt);
return;
diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc
index a86fdb1..654afc8 100644
--- a/lib/CodeGen/TypeCheckGen.cc
+++ b/lib/CodeGen/TypeCheckGen.cc
@@ -121,8 +121,8 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
// Binary operators with at least one Enum/Record/Pointer operand. Decides
// admissibility by type name and emits directly; ArithmeticHandler stays
// ignorant of user kinds.
-Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT) {
- int Op = Bin->getOp();
+Value *CodeGen::emitUserKindBinaryOp(int Op, Value *L, const TypeInfo *LT,
+ Value *R, const TypeInfo *RT, int Line) {
const char *LName = LT ? LT->Name.c_str() : "?";
const char *RName = RT ? RT->Name.c_str() : "?";
@@ -135,9 +135,6 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con
if ((LT && LT->isPointer()) || (RT && RT->isPointer())) {
bool SameName = LT && RT && LT->isPointer() && RT->isPointer() && LT->Name == RT->Name;
if (SameName && (Op == tok_eq || Op == tok_ne)) {
- Value *L = emitExpr(Bin->getLHS());
- Value *R = emitExpr(Bin->getRHS());
- if (!L || !R) return nullptr;
return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "ptr_eq")
: Builder->CreateICmpNE(L, R, "ptr_ne");
}
@@ -152,9 +149,6 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con
switch (Op) {
case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: {
if (!BothSameEnum) break;
- Value *L = emitExpr(Bin->getLHS());
- Value *R = emitExpr(Bin->getRHS());
- if (!L || !R) return nullptr;
switch (Op) {
case tok_eq: return Builder->CreateICmpEQ(L, R, "enum_eq");
case tok_ne: return Builder->CreateICmpNE(L, R, "enum_ne");
@@ -170,13 +164,10 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con
// The one legal arithmetic shape (guide ยง4.2): enum on the left,
// INTEGER on the right, result checked against the enum's range.
if (!(LT && LT->isEnum() && RT && RT->Kind == TypeKind::Integer)) break;
- Value *L = emitExpr(Bin->getLHS());
- Value *R = emitExpr(Bin->getRHS());
- if (!L || !R) return nullptr;
Value *Res = Op == '+' ? Builder->CreateAdd(L, R, "enum_step")
: Builder->CreateSub(L, R, "enum_step");
RuntimeChecker->emitEnumRangeCheck(Res, LT->asEnum()->ValueNames.size(),
- LT->Name, Bin->getLine());
+ LT->Name, Line);
return Res;
}
diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc
index 31bafe1..fcc9a40 100644
--- a/lib/Lexer/Lexer.cc
+++ b/lib/Lexer/Lexer.cc
@@ -7,12 +7,14 @@
using namespace cps;
-int Lexer::gettok() {
+int Lexer::readToken() {
while (isspace(LastChar)) {
if (LastChar == '\n') CurrentLine++;
LastChar = Input.get();
}
+ TokenLine = CurrentLine;
+
if (LastChar == '"') {
StringVal.clear();
LastChar = Input.get();
@@ -86,6 +88,10 @@ int Lexer::gettok() {
if (IdentifierStr == "INPUT") return tok_input;
if (IdentifierStr == "OUTPUT") return tok_output;
+ if (IdentifierStr == "CASE") return tok_case;
+ if (IdentifierStr == "ENDCASE") return tok_endcase;
+ if (IdentifierStr == "OTHERWISE") return tok_otherwise;
+
if (IdentifierStr == "IF") return tok_if;
if (IdentifierStr == "THEN") return tok_then;
if (IdentifierStr == "ELSE") return tok_else;
@@ -225,7 +231,7 @@ int Lexer::gettok() {
} while (LastChar != EOF && LastChar != '\n' && LastChar != '\r');
if (LastChar != EOF)
- return gettok();
+ return readToken();
} else {
LastChar = NextChar;
return '/';
@@ -239,3 +245,39 @@ int Lexer::gettok() {
LastChar = Input.get();
return ThisChar;
}
+
+Lexer::TokenData Lexer::captureToken(int Kind) const {
+ return {Kind, TokenLine, IdentifierStr, StringVal, NumVal, RealVal, CharVal};
+}
+
+void Lexer::restoreToken(const TokenData &Token) {
+ TokenLine = Token.Line;
+ IdentifierStr = Token.Identifier;
+ StringVal = Token.String;
+ NumVal = Token.Integer;
+ RealVal = Token.Real;
+ CharVal = Token.Character;
+}
+
+int Lexer::gettok() {
+ if (Lookahead.empty()) return readToken();
+ auto Token = std::move(Lookahead.front());
+ Lookahead.pop_front();
+ restoreToken(Token);
+ return Token.Kind;
+}
+
+int Lexer::peekToken(unsigned Offset) {
+ auto Current = captureToken(0);
+ while (Lookahead.size() <= Offset) {
+ int Kind = readToken();
+ Lookahead.push_back(captureToken(Kind));
+ }
+ restoreToken(Current);
+ return Lookahead[Offset].Kind;
+}
+
+int Lexer::peekLine(unsigned Offset) {
+ peekToken(Offset);
+ return Lookahead[Offset].Line;
+}
diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc
index 3e8a096..58422fc 100644
--- a/lib/Parser/Parser.cc
+++ b/lib/Parser/Parser.cc
@@ -29,7 +29,10 @@ Parser::Parser(Lexer &L) : Lex(L) {
}
int Parser::getNextToken() {
- return CurTok = Lex.gettok();
+ int PreviousLine = CurTok == tok_eof ? 0 : Lex.getLine();
+ CurTok = Lex.gettok();
+ StartsLine = Lex.getLine() != PreviousLine;
+ return CurTok;
}
std::vector<std::unique_ptr<StmtAST>> Parser::Parse() {
@@ -43,6 +46,7 @@ void Parser::syncToStatementStart() {
while (CurTok != tok_eof) {
switch (CurTok) {
case tok_declare: case tok_identifier: case tok_input: case tok_output:
+ case tok_case: case tok_endcase: case tok_otherwise:
case tok_if: case tok_while: case tok_repeat: case tok_for:
case tok_function: case tok_procedure: case tok_call: case tok_return:
case tok_else: case tok_endif: case tok_endwhile: case tok_until:
@@ -58,7 +62,7 @@ void Parser::syncToStatementStart() {
}
}
-std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<int> Terminators) {
+std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<int> Terminators, bool CaseBody) {
auto AtTerminator = [&] {
for (int T : Terminators) {
if (CurTok == T) return true;
@@ -67,14 +71,16 @@ std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<i
};
++BlockDepth; // 1 = top level (Parse() calls ParseBlock({}))
+ if (CaseBody) CaseBodyDepths.push_back(BlockDepth);
std::vector<std::unique_ptr<StmtAST>> Body;
- while (!AtTerminator() && CurTok != tok_eof) {
+ while (!AtTerminator() && CurTok != tok_eof && !(CaseBody && isCaseLabel())) {
if (auto Stmt = ParseStatement()) {
Body.push_back(std::move(Stmt));
} else {
syncToStatementStart();
}
}
+ if (CaseBody) CaseBodyDepths.pop_back();
--BlockDepth;
return Body;
}
@@ -86,6 +92,12 @@ std::unique_ptr<StmtAST> Parser::ParseStatement() {
}
std::unique_ptr<StmtAST> Parser::ParseStatementImpl() {
+ if (CurTok == tok_case) return ParseCaseStmt();
+ if (CurTok == tok_endcase || CurTok == tok_otherwise) {
+ fprintf(stderr, "error: Unexpected CASE terminator at line %d\n", Lex.getLine());
+ getNextToken();
+ return nullptr;
+ }
if (CurTok == tok_declare) {
return ParseDeclare();
}
diff --git a/lib/Parser/ParserControlFlow.cc b/lib/Parser/ParserControlFlow.cc
index cb32466..d01e561 100644
--- a/lib/Parser/ParserControlFlow.cc
+++ b/lib/Parser/ParserControlFlow.cc
@@ -144,3 +144,89 @@ std::unique_ptr<StmtAST> Parser::ParseForStmt() {
return std::make_unique<ForStmtAST>(VarName, std::move(Start), std::move(End), std::move(Step), std::move(Body));
}
+
+// A label begins a source line and ends in ':' on that line. Lookahead
+// preserves token payloads; assignments and statement keywords are not labels.
+bool Parser::isCaseLabel() {
+ if (!StartsLine) return false;
+ int Line = Lex.getLine();
+ int Depth = 0;
+ for (unsigned I = 0;; ++I) {
+ int T = I == 0 ? CurTok : Lex.peekToken(I - 1);
+ int TokenLine = I == 0 ? Line : Lex.peekLine(I - 1);
+ if (TokenLine != Line) return false;
+ if (T == tok_colon) return Depth == 0;
+ switch (T) {
+ case '(': case '[': ++Depth; break;
+ case ')': case ']': if (--Depth < 0) return false; break;
+ case tok_identifier: case tok_number_int: case tok_number_real:
+ case tok_string_literal: case tok_char_literal: case tok_true: case tok_false:
+ case tok_integer_kw: case tok_real_kw: case tok_boolean_kw:
+ case tok_string_kw: case tok_char_kw: case tok_c_int: case tok_c_char:
+ case tok_c_string: case tok_c_double:
+ case tok_to: case tok_otherwise: case tok_and: case tok_or: case tok_not:
+ case tok_eq: case tok_ne: case tok_le: case tok_ge: case tok_div: case tok_mod:
+ case '+': case '-': case '*': case '/': case '&': case '^':
+ case '<': case '>': case '.': case ',': break;
+ default: return false;
+ }
+ }
+}
+
+bool Parser::atCaseBoundary() {
+ return !ParsingCaseLabel && !CaseBodyDepths.empty() &&
+ CaseBodyDepths.back() == BlockDepth && isCaseLabel();
+}
+
+std::unique_ptr<StmtAST> Parser::ParseCaseStmt() {
+ getNextToken();
+ if (CurTok != tok_of) {
+ fprintf(stderr, "error: Expected OF after CASE at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ getNextToken();
+ if (CurTok != tok_identifier) {
+ fprintf(stderr, "error: Expected identifier after CASE OF at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ std::string Selector = Lex.IdentifierStr;
+ getNextToken();
+ std::vector<CaseBranch> Branches;
+ bool SeenOtherwise = false;
+ while (CurTok != tok_endcase && CurTok != tok_eof) {
+ if (SeenOtherwise || !isCaseLabel()) {
+ fprintf(stderr, "error: Expected CASE label on a new line (OTHERWISE must be last) at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ CaseBranch Branch;
+ Branch.Line = Lex.getLine();
+ if (CurTok == tok_otherwise) {
+ SeenOtherwise = true;
+ getNextToken();
+ } else {
+ bool Saved = ParsingCaseLabel;
+ ParsingCaseLabel = true;
+ Branch.Lower = ParseExpression();
+ if (Branch.Lower && CurTok == tok_to) {
+ getNextToken();
+ Branch.Upper = ParseExpression();
+ if (!Branch.Upper) Branch.Lower.reset();
+ }
+ ParsingCaseLabel = Saved;
+ if (!Branch.Lower) return nullptr;
+ }
+ if (CurTok != tok_colon) {
+ fprintf(stderr, "error: Expected ':' after CASE label at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ getNextToken();
+ Branch.Body = ParseBlock({tok_endcase}, true);
+ Branches.push_back(std::move(Branch));
+ }
+ if (CurTok != tok_endcase || Branches.empty()) {
+ fprintf(stderr, "error: Expected nonempty CASE and ENDCASE at line %d\n", Lex.getLine());
+ return nullptr;
+ }
+ getNextToken();
+ return std::make_unique<CaseStmtAST>(std::move(Selector), std::move(Branches));
+}
diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc
index 3e2665f..d32c407 100644
--- a/lib/Parser/ParserExpression.cc
+++ b/lib/Parser/ParserExpression.cc
@@ -5,6 +5,7 @@
using namespace cps;
int Parser::GetTokPrecedence() {
+ if (atCaseBoundary()) return -1;
if (BinopPrecedence.find(CurTok) == BinopPrecedence.end()) return -1;
int TokPrec = BinopPrecedence[CurTok];
if (TokPrec <= 0) return -1;
@@ -28,7 +29,7 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() {
int Line = Lex.getLine();
getNextToken();
- if (CurTok == '(') {
+ if (CurTok == '(' && !atCaseBoundary()) {
getNextToken();
auto Args = ParseExprList(')', true);
if (!Args) return nullptr;
@@ -54,6 +55,7 @@ std::unique_ptr<ExprAST> Parser::ParseIdentifierExpr() {
// Postfix accessor chain after an identifier: '[' exprs ']' | '.' IDENT | '^'
bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out) {
while (true) {
+ if (atCaseBoundary()) return true;
if (CurTok == '[') {
getNextToken();
auto Indices = ParseExprList(']', false);
diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc
index 25375f2..c47485e 100644
--- a/lib/Parser/ParserFunction.cc
+++ b/lib/Parser/ParserFunction.cc
@@ -222,7 +222,8 @@ std::unique_ptr<StmtAST> Parser::ParseReturnStmt() {
getNextToken();
std::unique_ptr<ExprAST> Expr = nullptr;
// A bare RETURN is recognized right before any block terminator.
- if (CurTok != tok_endif && CurTok != tok_else && CurTok != tok_endfunction &&
+ if (!atCaseBoundary() && CurTok != tok_endcase && CurTok != tok_otherwise &&
+ CurTok != tok_endif && CurTok != tok_else && CurTok != tok_endfunction &&
CurTok != tok_endprocedure && CurTok != tok_endwhile && CurTok != tok_until &&
CurTok != tok_next && CurTok != tok_catch && CurTok != tok_endtry &&
CurTok != tok_eof) {