支持嵌套

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
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 = "&&";

View File

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

View File

@ -4,11 +4,24 @@ import java.util.List;
/**
* 表示 if 语句的 AST 节点
* 包含一个条件表达式和一个 then 分支的语句列表不包含 else 分支
* 示例{@code if (x > 0) { print(x); }}
* 包含条件表达式then 分支语句列表以及可选的 else 分支
* 示例
* <pre>{@code
* if (x > 0) {
* print("Positive");
* } else {
* print("Negative");
* }
* }
* }</pre>
*
* @param condition 条件表达式控制是否执行 then 分支
* @param condition 条件表达式控制是否进入 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.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));

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) {
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);
}
}

View File

@ -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 语句解析器支持
* <pre>{@code
* if condition then
* statements...
* else
* statements...
* end if
* }</pre>
* 不支持 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<StatementNode> thenBranch = new ArrayList<>();
while (!ctx.getTokens().peek().getLexeme().equals("end")) {
String keyword = ctx.getTokens().peek().getLexeme();
List<StatementNode> 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);
}
}

View File

@ -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<StatementNode> 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) {

58
test
View File

@ -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
@ -54,29 +58,55 @@ module: MainModule
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