Skip to content

Commit e91f80a

Browse files
author
half-arch
committed
feat: add inline statement support for control flow
Support single-statement inline forms for: - if: if condition statement [else statement] - while: while condition statement - for: for x in xs statement - loop: loop [while cond] statement - repeat: repeat n statement Examples: if x < 5 x = x + 1 if x == 1 print("one") else print("other") while y > 5 y = y - 1 for i in [1,2,3] print(i) repeat 3 print("hello") loop while running tick() Design: - Block form still supported: if cond { stmt } - Inline form: single statement only (no braces) - New parseInlineStatement() that doesn't skip newlines - Clean grammar, easy to parse and optimize
1 parent 69c5f1b commit e91f80a

3 files changed

Lines changed: 109 additions & 29 deletions

File tree

src/havel-lang/ast/AST.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -781,13 +781,13 @@ struct SleepStatement : public Statement {
781781
void accept(ASTVisitor &visitor) const override;
782782
};
783783

784-
// Repeat Statement - repeat n { body }
784+
// Repeat Statement - repeat n { body } or repeat n statement
785785
struct RepeatStatement : public Statement {
786786
int count;
787-
std::unique_ptr<BlockStatement> body;
787+
std::unique_ptr<Statement> body;
788788

789789
RepeatStatement() : count(0) { kind = NodeType::RepeatStatement; }
790-
RepeatStatement(int c, std::unique_ptr<BlockStatement> b)
790+
RepeatStatement(int c, std::unique_ptr<Statement> b)
791791
: count(c), body(std::move(b)) { kind = NodeType::RepeatStatement; }
792792

793793
std::string toString() const override {

src/havel-lang/parser/Parser.cpp

Lines changed: 105 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,54 @@ Parser::produceASTStrict(const std::string &sourceCode) {
158158
return program;
159159
}
160160

161+
// Parse a single statement without skipping leading newlines (for inline forms)
162+
std::unique_ptr<havel::ast::Statement> Parser::parseInlineStatement() {
163+
// Don't skip newlines - they terminate inline statements
164+
// If we hit a newline or EOF, return null
165+
if (at().type == havel::TokenType::NewLine || at().type == havel::TokenType::EOF_TOKEN) {
166+
return nullptr;
167+
}
168+
169+
// Parse based on current token
170+
switch (at().type) {
171+
case havel::TokenType::Let:
172+
return parseLetDeclaration();
173+
case havel::TokenType::If:
174+
return parseIfStatement();
175+
case havel::TokenType::While:
176+
return parseWhileStatement();
177+
case havel::TokenType::For:
178+
return parseForStatement();
179+
case havel::TokenType::Loop:
180+
return parseLoopStatement();
181+
case havel::TokenType::Repeat:
182+
return parseRepeatStatement();
183+
case havel::TokenType::Break:
184+
return parseBreakStatement();
185+
case havel::TokenType::Continue:
186+
return parseContinueStatement();
187+
case havel::TokenType::Return:
188+
case havel::TokenType::Ret:
189+
return parseReturnStatement();
190+
case havel::TokenType::Fn:
191+
return parseFunctionDeclaration();
192+
case havel::TokenType::Switch:
193+
return parseSwitchStatement();
194+
case havel::TokenType::Try:
195+
return parseTryStatement();
196+
case havel::TokenType::Catch:
197+
failAt(at(), "'catch' can only appear within a 'try' statement");
198+
case havel::TokenType::Finally:
199+
failAt(at(), "'finally' can only appear within a 'try' statement");
200+
case havel::TokenType::Throw:
201+
return parseThrowStatement();
202+
default:
203+
// Expression statement (including assignments, function calls, etc.)
204+
auto expr = parseExpression();
205+
return std::make_unique<havel::ast::ExpressionStatement>(std::move(expr));
206+
}
207+
}
208+
161209
std::unique_ptr<havel::ast::Statement> Parser::parseStatement() {
162210
// Skip leading newlines within statement context
163211
// This allows multiple newlines between statements
@@ -1045,20 +1093,27 @@ std::unique_ptr<havel::ast::Statement> Parser::parseIfStatement() {
10451093
auto condition = parseExpression();
10461094
allowBraceCallSugar = prevAllow;
10471095

1048-
if (at().type != havel::TokenType::OpenBrace) {
1049-
failAt(at(), "Expected '{' after if condition");
1096+
std::unique_ptr<havel::ast::Statement> consequence;
1097+
1098+
// Block form or inline form
1099+
if (at().type == havel::TokenType::OpenBrace) {
1100+
consequence = parseBlockStatement();
1101+
} else {
1102+
// Inline form - single statement (don't skip newlines)
1103+
consequence = parseInlineStatement();
10501104
}
10511105

1052-
auto consequence = parseBlockStatement();
1053-
10541106
std::unique_ptr<havel::ast::Statement> alternative = nullptr;
10551107
if (at().type == havel::TokenType::Else) {
10561108
advance(); // consume "else"
10571109

10581110
if (at().type == havel::TokenType::If) {
10591111
alternative = parseIfStatement();
1060-
} else {
1112+
} else if (at().type == havel::TokenType::OpenBrace) {
10611113
alternative = parseBlockStatement();
1114+
} else {
1115+
// Inline else - single statement (don't skip newlines)
1116+
alternative = parseInlineStatement();
10621117
}
10631118
}
10641119

@@ -1074,12 +1129,16 @@ std::unique_ptr<havel::ast::Statement> Parser::parseWhileStatement() {
10741129
auto condition = parseExpression();
10751130
allowBraceCallSugar = prevAllow;
10761131

1077-
if (at().type != havel::TokenType::OpenBrace) {
1078-
failAt(at(), "Expected '{' after while condition");
1132+
std::unique_ptr<havel::ast::Statement> body;
1133+
1134+
// Block form or inline form
1135+
if (at().type == havel::TokenType::OpenBrace) {
1136+
body = parseBlockStatement();
1137+
} else {
1138+
// Inline form - single statement (don't skip newlines)
1139+
body = parseInlineStatement();
10791140
}
10801141

1081-
auto body = parseBlockStatement();
1082-
10831142
return std::make_unique<havel::ast::WhileStatement>(std::move(condition),
10841143
std::move(body));
10851144
}
@@ -1249,17 +1308,21 @@ std::unique_ptr<havel::ast::Statement> Parser::parseForStatement() {
12491308
auto iterable = parseExpression();
12501309
allowBraceCallSugar = prevAllow;
12511310

1252-
// Skip newlines before opening brace
1311+
// Skip newlines before body
12531312
while (at().type == havel::TokenType::NewLine) {
12541313
advance();
12551314
}
12561315

1257-
if (at().type != havel::TokenType::OpenBrace) {
1258-
failAt(at(), "Expected '{' after for iterable");
1316+
std::unique_ptr<havel::ast::Statement> body;
1317+
1318+
// Block form or inline form
1319+
if (at().type == havel::TokenType::OpenBrace) {
1320+
body = parseBlockStatement();
1321+
} else {
1322+
// Inline form - single statement (don't skip newlines)
1323+
body = parseInlineStatement();
12591324
}
12601325

1261-
auto body = parseBlockStatement();
1262-
12631326
return std::make_unique<havel::ast::ForStatement>(
12641327
std::move(iterators), std::move(iterable), std::move(body));
12651328
}
@@ -1269,33 +1332,37 @@ std::unique_ptr<havel::ast::Statement> Parser::parseLoopStatement() {
12691332

12701333
// Check for optional "while condition"
12711334
std::unique_ptr<havel::ast::Expression> condition;
1272-
1335+
12731336
// Skip newlines
12741337
while (at().type == havel::TokenType::NewLine) {
12751338
advance();
12761339
}
12771340

12781341
if (at().type == havel::TokenType::While) {
12791342
advance(); // consume "while"
1280-
1343+
12811344
// Parse condition expression
12821345
bool prevAllow = allowBraceCallSugar;
12831346
allowBraceCallSugar = false;
12841347
condition = parseExpression();
12851348
allowBraceCallSugar = prevAllow;
1286-
1287-
// Skip newlines before opening brace
1349+
1350+
// Skip newlines before body
12881351
while (at().type == havel::TokenType::NewLine) {
12891352
advance();
12901353
}
12911354
}
12921355

1293-
if (at().type != havel::TokenType::OpenBrace) {
1294-
failAt(at(), "Expected '{' after 'loop' or loop condition");
1356+
std::unique_ptr<havel::ast::Statement> body;
1357+
1358+
// Block form or inline form
1359+
if (at().type == havel::TokenType::OpenBrace) {
1360+
body = parseBlockStatement();
1361+
} else {
1362+
// Inline form - single statement (don't skip newlines)
1363+
body = parseInlineStatement();
12951364
}
12961365

1297-
auto body = parseBlockStatement();
1298-
12991366
return std::make_unique<havel::ast::LoopStatement>(std::move(body), std::move(condition));
13001367
}
13011368

@@ -1684,20 +1751,32 @@ std::unique_ptr<havel::ast::Statement> Parser::parseWhenBlock() {
16841751
std::move(statements));
16851752
}
16861753

1687-
// Parse repeat statement: repeat count { body }
1754+
// Parse repeat statement: repeat count { body } or repeat count statement
16881755
std::unique_ptr<havel::ast::Statement> Parser::parseRepeatStatement() {
16891756
advance(); // consume 'repeat'
16901757

16911758
// Parse count - must be a number literal
16921759
if (at().type != havel::TokenType::Number) {
16931760
failAt(at(), "repeat count must be a number");
16941761
}
1695-
1762+
16961763
int count = static_cast<int>(std::stod(at().value));
16971764
advance(); // consume number
16981765

1699-
// Parse body as block statement (parseBlockStatement consumes the '{')
1700-
auto body = parseBlockStatement();
1766+
// Skip newlines before body
1767+
while (at().type == havel::TokenType::NewLine) {
1768+
advance();
1769+
}
1770+
1771+
std::unique_ptr<havel::ast::Statement> body;
1772+
1773+
// Block form or inline form
1774+
if (at().type == havel::TokenType::OpenBrace) {
1775+
body = parseBlockStatement();
1776+
} else {
1777+
// Inline form - single statement (don't skip newlines)
1778+
body = parseInlineStatement();
1779+
}
17011780

17021781
return std::make_unique<ast::RepeatStatement>(count, std::move(body));
17031782
}

src/havel-lang/parser/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class Parser {
7171

7272
// Parser methods (following Tyler's structure)
7373
std::unique_ptr<ast::Statement> parseStatement();
74+
std::unique_ptr<ast::Statement> parseInlineStatement(); // For inline forms (doesn't skip newlines)
7475
std::unique_ptr<ast::Expression> parseExpression();
7576
std::unique_ptr<ast::Expression> parseAssignmentExpression();
7677
std::unique_ptr<ast::Expression> parsePipelineExpression();

0 commit comments

Comments
 (0)