支持嵌套

This commit is contained in:
Luke 2025-04-25 13:49:52 +08:00
parent 9662e722a4
commit f2f30b8d7b
9 changed files with 189 additions and 57 deletions

View File

@ -28,7 +28,7 @@ public class OperatorTokenScanner extends AbstractTokenScanner {
*/ */
@Override @Override
public boolean canHandle(char c, LexerContext ctx) { 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; type = TokenType.LESS_THAN;
} }
break; break;
case '%':
lexeme = "%";
type = TokenType.MODULO;
break;
case '&': case '&':
if (ctx.match('&')) { if (ctx.match('&')) {
lexeme = "&&"; lexeme = "&&";

View File

@ -34,6 +34,9 @@ public enum TokenType {
/** 赋值符号 "=" */ /** 赋值符号 "=" */
EQUALS, EQUALS,
MODULO, // %
/** 加号 "+" */ /** 加号 "+" */
PLUS, PLUS,

View File

@ -4,11 +4,24 @@ import java.util.List;
/** /**
* 表示 if 语句的 AST 节点 * 表示 if 语句的 AST 节点
* 包含一个条件表达式和一个 then 分支的语句列表不包含 else 分支 * 包含条件表达式then 分支语句列表以及可选的 else 分支
* 示例{@code if (x > 0) { print(x); }} * 示例
* <pre>{@code
* if (x > 0) {
* print("Positive");
* } else {
* print("Negative");
* }
* }
* }</pre>
* *
* @param condition 条件表达式控制是否执行 then 分支 * @param condition 条件表达式控制是否进入 thenBranch
* @param thenBranch 条件为真时执行的语句列表 * @param thenBranch 条件为真时执行的语句列表
* @param elseBranch 条件为假时执行的语句列表可为空
*/ */
public record IfNode(ExpressionNode condition, List<StatementNode> thenBranch) implements StatementNode { public record IfNode(
ExpressionNode condition,
List<StatementNode> thenBranch,
List<StatementNode> elseBranch
) implements StatementNode {
} }

View File

@ -29,6 +29,7 @@ public class PrattExpressionParser implements ExpressionParser {
infixes.put("-", new BinaryOperatorParselet(Precedence.SUM, true)); 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.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)); infixes.put("<", new BinaryOperatorParselet(Precedence.SUM, true));
infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true)); infixes.put("==", new BinaryOperatorParselet(Precedence.SUM, true));

View File

@ -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<StatementNode> parse(ParserContext ctx, String endKeyword, String endSubKeyword) {
List<StatementNode> 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);
}
}

View File

@ -30,20 +30,26 @@ public class ExpressionStatementParser implements StatementParser {
public StatementNode parse(ParserContext ctx) { public StatementNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens(); 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 if (ts.peek().getType() == TokenType.IDENTIFIER
&& ts.peek(1).getLexeme().equals("=")) { && ts.peek(1).getLexeme().equals("=")) {
String varName = ts.next().getLexeme(); // 消费 identifier String varName = ts.next().getLexeme(); // consume identifier
ts.expect("="); // 消费 '=' ts.expect("="); // consume '='
ExpressionNode value = new PrattExpressionParser().parse(ctx); ExpressionNode value = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
return new AssignmentNode(varName, value); return new AssignmentNode(varName, value);
} }
// 否则解析为普通表达式语句 // 普通表达式语句如函数调用
ExpressionNode expr = new PrattExpressionParser().parse(ctx); ExpressionNode expr = new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
return new ExpressionStatementNode(expr); return new ExpressionStatementNode(expr);
} }
} }

View File

@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.parser.statement; 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.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.IfNode; import org.jcnc.snow.compiler.parser.ast.IfNode;
import org.jcnc.snow.compiler.parser.ast.StatementNode; import org.jcnc.snow.compiler.parser.ast.StatementNode;
@ -11,45 +12,79 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* if 语句解析器支持基本格式 * if 语句解析器支持
* <pre>{@code * <pre>{@code
* if condition then * if condition then
* statements... * statements...
* else
* statements...
* end if * end if
* }</pre> * }</pre>
* 不支持 else 分支仅支持 then 分支
*/ */
public class IfStatementParser implements StatementParser { public class IfStatementParser implements StatementParser {
/**
* 解析 if 表达式并构造 {@link IfNode}
*
* @param ctx 当前解析上下文
* @return 表示 if 语句的 AST 节点
*/
@Override @Override
public IfNode parse(ParserContext ctx) { public IfNode parse(ParserContext ctx) {
ctx.getTokens().expect("if"); var ts = ctx.getTokens();
ts.expect("if");
// 解析条件表达式 // 解析条件表达式
var cond = new PrattExpressionParser().parse(ctx); var cond = new PrattExpressionParser().parse(ctx);
ctx.getTokens().expect("then"); ts.expect("then");
ctx.getTokens().expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
// 解析 then 分支的语句块
List<StatementNode> thenBranch = new ArrayList<>(); List<StatementNode> thenBranch = new ArrayList<>();
while (!ctx.getTokens().peek().getLexeme().equals("end")) { List<StatementNode> elseBranch = new ArrayList<>();
String keyword = ctx.getTokens().peek().getLexeme();
// --- 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); StatementNode stmt = StatementParserFactory.get(keyword).parse(ctx);
thenBranch.add(stmt); thenBranch.add(stmt);
} }
// 匹配 end if 结束语句 // --- ELSE 分支 ---
ctx.getTokens().expect("end"); if (ts.peek().getLexeme().equals("else")) {
ctx.getTokens().expect("if"); ts.next(); // consume 'else'
ctx.getTokens().expectType(TokenType.NEWLINE); 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);
} }
} }

View File

@ -1,5 +1,6 @@
package org.jcnc.snow.compiler.parser.statement; 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.lexer.token.TokenType;
import org.jcnc.snow.compiler.parser.ast.AssignmentNode; import org.jcnc.snow.compiler.parser.ast.AssignmentNode;
import org.jcnc.snow.compiler.parser.ast.ExpressionNode; import org.jcnc.snow.compiler.parser.ast.ExpressionNode;
@ -31,12 +32,6 @@ import java.util.List;
*/ */
public class LoopStatementParser implements StatementParser { public class LoopStatementParser implements StatementParser {
/**
* 解析一个完整的 loop 语句块返回 {@link LoopNode}
*
* @param ctx 当前的解析上下文
* @return 表示 loop 结构的 AST 节点
*/
@Override @Override
public LoopNode parse(ParserContext ctx) { public LoopNode parse(ParserContext ctx) {
TokenStream ts = ctx.getTokens(); TokenStream ts = ctx.getTokens();
@ -61,8 +56,7 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":"); ts.expect(":");
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
skipNewlines(ts); skipNewlines(ts);
ExpressionNode condition = ExpressionNode condition = new PrattExpressionParser().parse(ctx);
new PrattExpressionParser().parse(ctx);
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
skipNewlines(ts); skipNewlines(ts);
@ -71,6 +65,7 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":"); ts.expect(":");
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
skipNewlines(ts); skipNewlines(ts);
String varName = ts.expectType(TokenType.IDENTIFIER).getLexeme(); String varName = ts.expectType(TokenType.IDENTIFIER).getLexeme();
ts.expect("="); ts.expect("=");
ExpressionNode updateExpr = new PrattExpressionParser().parse(ctx); ExpressionNode updateExpr = new PrattExpressionParser().parse(ctx);
@ -83,9 +78,18 @@ public class LoopStatementParser implements StatementParser {
ts.expect(":"); ts.expect(":");
ts.expectType(TokenType.NEWLINE); ts.expectType(TokenType.NEWLINE);
skipNewlines(ts); skipNewlines(ts);
List<StatementNode> body = new ArrayList<>(); List<StatementNode> body = new ArrayList<>();
while (!"end".equals(ts.peek().getLexeme())) { while (!(ts.peek().getType() == TokenType.KEYWORD &&
body.add(StatementParserFactory.get(ts.peek().getLexeme()).parse(ctx)); 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); skipNewlines(ts);
} }
@ -104,9 +108,7 @@ public class LoopStatementParser implements StatementParser {
} }
/** /**
* 跳过连续的 NEWLINE token用于容错和格式整洁 * 跳过多余的换行用于清理语法结构和容错
*
* @param ts 当前的 Token
*/ */
private void skipNewlines(TokenStream ts) { private void skipNewlines(TokenStream ts) {
while (ts.peek().getType() == TokenType.NEWLINE) { while (ts.peek().getType() == TokenType.NEWLINE) {

58
test
View File

@ -1,11 +1,15 @@
module: CommonTasks module: CommonTasks
function: add_numbers function: add_numbers
body: body:
return num1 + num2 return num1 + num2
end body end body
parameter: parameter:
declare num1: int declare num1: int
declare num2: int declare num2: int
return_type: int return_type: int
end function end function
@ -54,29 +58,55 @@ module: MainModule
declare args: string declare args: string
return_type: void return_type: void
body: body:
declare input_number: int = BuiltinUtils.to_int(args)
if input_number <= 0 then
input_number = 5
end if
loop: loop:
initializer: initializer:
declare counter: int = 5 declare i: int = 0
condition: condition:
counter > 0 i < input_number
update: update:
counter = counter - 1 i = i + 1
body: 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 body
end loop end loop
declare input_number: int = BuiltinUtils.to_int(args) declare final_message: string = StringUtils.concatenate("Finished with input: ", BuiltinUtils.to_string(input_number))
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))
BuiltinUtils.print(final_message) BuiltinUtils.print(final_message)
end body end body
end function end function
end module end module