diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
index 75f08df..373bd94 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/scanners/OperatorTokenScanner.java
@@ -28,7 +28,7 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
*/
@Override
public boolean canHandle(char c, LexerContext ctx) {
- return "=!<>|&".indexOf(c) >= 0;
+ return "=!<>|&%".indexOf(c) >= 0;
}
/**
@@ -84,6 +84,10 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
type = TokenType.LESS_THAN;
}
break;
+ case '%':
+ lexeme = "%";
+ type = TokenType.MODULO;
+ break;
case '&':
if (ctx.match('&')) {
lexeme = "&&";
diff --git a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
index 891561d..2b06f73 100644
--- a/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
+++ b/src/main/java/org/jcnc/snow/compiler/lexer/token/TokenType.java
@@ -34,6 +34,9 @@ public enum TokenType {
/** 赋值符号 "=" */
EQUALS,
+ MODULO, // %
+
+
/** 加号 "+" */
PLUS,
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java b/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
index 351537d..6122c00 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/ast/IfNode.java
@@ -4,11 +4,24 @@ import java.util.List;
/**
* 表示 if 语句的 AST 节点。
- * 包含一个条件表达式和一个 then 分支的语句列表,不包含 else 分支。
- * 示例:{@code if (x > 0) { print(x); }}
+ * 包含条件表达式、then 分支语句列表,以及可选的 else 分支。
+ * 示例:
+ *
{@code
+ * if (x > 0) {
+ * print("Positive");
+ * } else {
+ * print("Negative");
+ * }
+ * }
+ * }
*
- * @param condition 条件表达式,控制是否执行 then 分支。
- * @param thenBranch 条件为真时执行的语句列表。
+ * @param condition 条件表达式,控制是否进入 thenBranch。
+ * @param thenBranch 条件为真时执行的语句列表。
+ * @param elseBranch 条件为假时执行的语句列表(可为空)。
*/
-public record IfNode(ExpressionNode condition, List thenBranch) implements StatementNode {
+public record IfNode(
+ ExpressionNode condition,
+ List thenBranch,
+ List elseBranch
+) implements StatementNode {
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
index de7af9f..3e350b2 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/expression/PrattExpressionParser.java
@@ -29,6 +29,7 @@ public class PrattExpressionParser implements ExpressionParser {
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("*", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put("/", new BinaryOperatorParselet(Precedence.PRODUCT, true));
+ infixes.put("%", new BinaryOperatorParselet(Precedence.PRODUCT, true));
infixes.put(">", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/BlockStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/BlockStatementParser.java
new file mode 100644
index 0000000..a6fab3b
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/BlockStatementParser.java
@@ -0,0 +1,38 @@
+package org.jcnc.snow.compiler.parser.statement;
+
+import org.jcnc.snow.compiler.lexer.token.Token;
+import org.jcnc.snow.compiler.lexer.token.TokenType;
+import org.jcnc.snow.compiler.parser.ast.StatementNode;
+import org.jcnc.snow.compiler.parser.context.ParserContext;
+import org.jcnc.snow.compiler.parser.factory.StatementParserFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BlockStatementParser {
+
+ public List parse(ParserContext ctx, String endKeyword, String endSubKeyword) {
+ List statements = new ArrayList<>();
+
+ while (!isAtEnd(ctx, endKeyword, endSubKeyword)) {
+ Token token = ctx.getTokens().peek();
+ String keyword = (token.getType() == TokenType.KEYWORD) ? token.getLexeme() : "";
+
+ StatementParser parser = StatementParserFactory.get(keyword);
+ statements.add(parser.parse(ctx));
+ }
+
+ return statements;
+ }
+
+ private boolean isAtEnd(ParserContext ctx, String kw1, String kw2) {
+ if (ctx.getTokens().isAtEnd()) return true;
+ Token t1 = ctx.getTokens().peek();
+ Token t2 = ctx.getTokens().peek(1);
+ return t1.getType() == TokenType.KEYWORD &&
+ t1.getLexeme().equals(kw1) &&
+ t2 != null &&
+ t2.getType() == TokenType.KEYWORD &&
+ t2.getLexeme().equals(kw2);
+ }
+}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
index 128b7b9..0e7efe9 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/ExpressionStatementParser.java
@@ -30,20 +30,26 @@ public class ExpressionStatementParser implements StatementParser {
public StatementNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
- // 检查是否是赋值形式(identifier = expr)
+ // 空行或非法起始符号,提前退出(安全防护)
+ if (ts.peek().getType() == TokenType.NEWLINE || ts.peek().getType() == TokenType.KEYWORD) {
+ throw new IllegalStateException("Cannot parse expression starting with keyword: " + ts.peek().getLexeme());
+ }
+
+ // 判断是否是赋值语句(形如:identifier = expr)
if (ts.peek().getType() == TokenType.IDENTIFIER
&& ts.peek(1).getLexeme().equals("=")) {
- String varName = ts.next().getLexeme(); // 消费 identifier
- ts.expect("="); // 消费 '='
+ String varName = ts.next().getLexeme(); // consume identifier
+ ts.expect("="); // consume '='
ExpressionNode value = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
return new AssignmentNode(varName, value);
}
- // 否则解析为普通表达式语句
+ // 普通表达式语句(如函数调用)
ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(expr);
}
+
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java
index 64bf84a..fcf8d8c 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/IfStatementParser.java
@@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.parser.statement;
+import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.IfNode;
import org.jcnc.snow.compiler.parser.ast.StatementNode;
@@ -11,45 +12,79 @@ import java.util.ArrayList;
import java.util.List;
/**
- * if 语句解析器,支持基本格式:
+ * if 语句解析器,支持:
* {@code
* if condition then
* statements...
+ * else
+ * statements...
* end if
* }
- * 不支持 else 分支,仅支持 then 分支。
*/
public class IfStatementParser implements StatementParser {
- /**
- * 解析 if 表达式,并构造 {@link IfNode}。
- *
- * @param ctx 当前解析上下文。
- * @return 表示 if 语句的 AST 节点。
- */
@Override
public IfNode parse(ParserContext ctx) {
- ctx.getTokens().expect("if");
+ var ts = ctx.getTokens();
+
+ ts.expect("if");
// 解析条件表达式
var cond = new PrattExpressionParser().parse(ctx);
- ctx.getTokens().expect("then");
- ctx.getTokens().expectType(TokenType.NEWLINE);
+ ts.expect("then");
+ ts.expectType(TokenType.NEWLINE);
- // 解析 then 分支的语句块
List thenBranch = new ArrayList<>();
- while (!ctx.getTokens().peek().getLexeme().equals("end")) {
- String keyword = ctx.getTokens().peek().getLexeme();
+ List elseBranch = new ArrayList<>();
+
+ // --- THEN 分支 ---
+ while (true) {
+ Token peek = ts.peek();
+
+ // 跳过空行
+ if (peek.getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+
+ if (peek.getType() == TokenType.KEYWORD &&
+ (peek.getLexeme().equals("else") || peek.getLexeme().equals("end"))) {
+ break;
+ }
+
+ String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : "";
StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx);
thenBranch.add(stmt);
}
- // 匹配 end if 结束语句
- ctx.getTokens().expect("end");
- ctx.getTokens().expect("if");
- ctx.getTokens().expectType(TokenType.NEWLINE);
+ // --- ELSE 分支 ---
+ if (ts.peek().getLexeme().equals("else")) {
+ ts.next(); // consume 'else'
+ ts.expectType(TokenType.NEWLINE);
- return new IfNode(cond, thenBranch);
+ while (true) {
+ Token peek = ts.peek();
+
+ if (peek.getType() == TokenType.NEWLINE) {
+ ts.next();
+ continue;
+ }
+
+ if (peek.getType() == TokenType.KEYWORD && peek.getLexeme().equals("end")) {
+ break;
+ }
+
+ String keyword = peek.getType() == TokenType.KEYWORD ? peek.getLexeme() : "";
+ StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx);
+ elseBranch.add(stmt);
+ }
+ }
+
+ ts.expect("end");
+ ts.expect("if");
+ ts.expectType(TokenType.NEWLINE);
+
+ return new IfNode(cond, thenBranch, elseBranch);
}
}
diff --git a/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java
index fc855b2..bcc69c1 100644
--- a/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java
+++ b/src/main/java/org/jcnc/snow/compiler/parser/statement/LoopStatementParser.java
@@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.parser.statement;
+import org.jcnc.snow.compiler.lexer.token.Token;
import org.jcnc.snow.compiler.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
@@ -31,12 +32,6 @@ import java.util.List;
*/
public class LoopStatementParser implements StatementParser {
- /**
- * 解析一个完整的 loop 语句块,返回 {@link LoopNode}。
- *
- * @param ctx 当前的解析上下文。
- * @return 表示 loop 结构的 AST 节点。
- */
@Override
public LoopNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens();
@@ -61,8 +56,7 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
skipNewlines(ts);
- ExpressionNode condition =
- new PrattExpressionParser().parse(ctx);
+ ExpressionNode condition = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE);
skipNewlines(ts);
@@ -71,6 +65,7 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
skipNewlines(ts);
+
String varName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
ts.expect("=");
ExpressionNode updateExpr = new PrattExpressionParser().parse(ctx);
@@ -83,9 +78,18 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":");
ts.expectType(TokenType.NEWLINE);
skipNewlines(ts);
+
List body = new ArrayList<>();
- while (!"end".equals(ts.peek().getLexeme())) {
- body.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx));
+ while (!(ts.peek().getType() == TokenType.KEYWORD &&
+ ts.peek().getLexeme().equals("end") &&
+ ts.peek(1).getLexeme().equals("body"))) {
+
+ String keyword = ts.peek().getType() == TokenType.KEYWORD
+ ? ts.peek().getLexeme()
+ : "";
+
+ StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx);
+ body.add(stmt);
skipNewlines(ts);
}
@@ -104,9 +108,7 @@ public class LoopStatementParser implements StatementParser {
}
/**
- * 跳过连续的 NEWLINE token,用于容错和格式整洁。
- *
- * @param ts 当前的 Token 流。
+ * 跳过多余的换行,用于清理语法结构和容错。
*/
private void skipNewlines(TokenStream ts) {
while (ts.peek().getType() == TokenType.NEWLINE) {
diff --git a/test b/test
index 27855d1..d2eb11e 100644
--- a/test
+++ b/test
@@ -1,11 +1,15 @@
module: CommonTasks
function: add_numbers
+
body:
return num1 + num2
end body
+
parameter:
+
declare num1: int
declare num2: int
+
return_type: int
end function
@@ -49,34 +53,60 @@ module: MainModule
end function
- function: main
+ function: main
parameter:
declare args: string
return_type: void
body:
+ declare input_number: int = BuiltinUtils.to_int(args)
+
+ if input_number <= 0 then
+ input_number = 5
+ end if
+
loop:
initializer:
- declare counter: int = 5
+ declare i: int = 0
condition:
- counter > 0
+ i < input_number
update:
- counter = counter - 1
+ i = i + 1
body:
- BuiltinUtils.print(counter)
+ if i % 2 == 0 then
+ BuiltinUtils.print("i is even: " + BuiltinUtils.to_string(i))
+ else
+ BuiltinUtils.print("i is odd: " + BuiltinUtils.to_string(i))
+
+ loop:
+ initializer:
+ declare j: int = 0
+ condition:
+ j < i
+ update:
+ j = j + 1
+ body:
+ if j == 1 then
+ BuiltinUtils.print(" first inner")
+ else
+ if j % 2 == 0 then
+ BuiltinUtils.print(" j even")
+ else
+ BuiltinUtils.print(" j odd")
+ end if
+ end if
+ end body
+ end loop
+ end if
+
+ declare sum: int = CommonTasks.add_numbers(i, 10)
+ declare squared: int = MathUtils.square_number(sum)
+ BuiltinUtils.print(" i+10 squared = " + BuiltinUtils.to_string(squared))
end body
end loop
- declare input_number: int = BuiltinUtils.to_int(args)
- if input_number == 0 then
- input_number = 999
- end if
-
- declare sum_result: int = CommonTasks.add_numbers(input_number, 20)
-
- declare squared_result: int = MathUtils.square_number(sum_result)
-
- declare final_message: string = StringUtils.concatenate("Result:",BuiltinUtils.to_string(squared_result))
+ declare final_message: string = StringUtils.concatenate("Finished with input: ", BuiltinUtils.to_string(input_number))
BuiltinUtils.print(final_message)
end body
end function
+
end module