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