!4 release: 合并 v0.1.1 版本至 main 分支
Merge pull request !4 from Luke/release/v0.1.1
This commit is contained in:
commit
11037da7ec
24
README.md
24
README.md
@ -3,27 +3,27 @@
|
|||||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Snow编程语言</h1>
|
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Snow编程语言</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href='https://gitee.com/jcnc-org/snow/stargazers'><img src='https://gitee.com/jcnc-org/snow/badge/star.svg?theme=dark' alt='star'></img></a>
|
<a href='https://gitee.com/jcnc-org/snow/stargazers'><img src='https://gitee.com/jcnc-org/snow/badge/star.svg?theme=dark' alt='star'></a>
|
||||||
<a href='https://gitee.com/jcnc-org/snow/members'><img src='https://gitee.com/jcnc-org/snow/badge/fork.svg?theme=dark' alt='fork'></img></a>
|
<a href='https://gitee.com/jcnc-org/snow/members'><img src='https://gitee.com/jcnc-org/snow/badge/fork.svg?theme=dark' alt='fork'></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://gitee.com/jcnc-org/JNotepad/blob/master/LICENSE">
|
<a href="https://gitee.com/jcnc-org/snow/blob/main/LICENSE">
|
||||||
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
|
<img src="https://img.shields.io/badge/%20license-Apache--2.0%20-blue" alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://gitee.com/jcnc-org/JNotepad/blob/master/LICENSE">
|
<a href="https://gitee.com/jcnc-org/snow/tree/v0.1/">
|
||||||
<img src="https://img.shields.io/badge/version-v0.1-blue" alt="">
|
<img src="https://img.shields.io/badge/version-v0.1.1-blue" alt="">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
|
<a href="https://gitee.com/jcnc-org/snow/releases">
|
||||||
<img src="https://img.shields.io/badge/Windows-Passing-49%2C198%2C84.svg?style=falt&logo=Windows" alt="">
|
<img src="https://img.shields.io/badge/Windows-Passing-49%2C198%2C84.svg?style=falt&logo=Windows" alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
|
<a href="https://gitee.com/jcnc-org/snow/releases">
|
||||||
<img src="https://img.shields.io/badge/Ubuntu-Passing-49%2C198%2C84.svg?style=falt&logo=Ubuntu" alt="">
|
<img src="https://img.shields.io/badge/Ubuntu-Passing-49%2C198%2C84.svg?style=falt&logo=Ubuntu" alt="">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://gitee.com/jcnc-org/JNotepad/releases">
|
<a href="https://gitee.com/jcnc-org/snow/releases">
|
||||||
<img src="https://img.shields.io/badge/MacOS-Passing-49%2C198%2C84.svg?style=falt&logo=Apple" alt="">
|
<img src="https://img.shields.io/badge/MacOS-Passing-49%2C198%2C84.svg?style=falt&logo=Apple" alt="">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@ -68,9 +68,9 @@ Snow 语言受到 LLM 驱动代码生成趋势的启发,强调简单而清晰的
|
|||||||
|
|
||||||
3. **运行项目**
|
3. **运行项目**
|
||||||
|
|
||||||
使用IDEA配置好的运行配置SnowCompiler
|
使用IDEA配置好的运行配置SnowCompiler
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
4. **运行成功**
|
4. **运行成功**
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ Process has ended
|
|||||||
3: 0
|
3: 0
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 编译Snow源代码
|
## 编译Snow源代码
|
||||||
|
|
||||||
@ -329,8 +329,6 @@ end module
|
|||||||
|
|
||||||
[Git 管理规范](doc/Git-Management/Git-Management.md)
|
[Git 管理规范](doc/Git-Management/Git-Management.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 开发计划 / TODO
|
## 开发计划 / TODO
|
||||||
|
|
||||||
* 扩展标准库支持和更多内置模块,如字符串,文件操作等常用功能。
|
* 扩展标准库支持和更多内置模块,如字符串,文件操作等常用功能。
|
||||||
|
|||||||
@ -59,7 +59,7 @@ public class SnowCompiler {
|
|||||||
System.out.println(code);
|
System.out.println(code);
|
||||||
|
|
||||||
LexerEngine lexer = new LexerEngine(code, p.toString());
|
LexerEngine lexer = new LexerEngine(code, p.toString());
|
||||||
ParserContext ctx = new ParserContext(lexer.getAllTokens());
|
ParserContext ctx = new ParserContext(lexer.getAllTokens(), p.toString());
|
||||||
allAst.addAll(new ParserEngine(ctx).parse());
|
allAst.addAll(new ParserEngine(ctx).parse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public class LexerEngine {
|
|||||||
* 构造时立即进行全量扫描。
|
* 构造时立即进行全量扫描。
|
||||||
*
|
*
|
||||||
* @param source 源代码文本
|
* @param source 源代码文本
|
||||||
* @param sourceName 文件名或来源描述(如"main.snow")
|
* @param sourceName 文件名或来源描述(如"Main.snow")
|
||||||
*/
|
*/
|
||||||
public LexerEngine(String source, String sourceName) {
|
public LexerEngine(String source, String sourceName) {
|
||||||
this.context = new LexerContext(source);
|
this.context = new LexerContext(source);
|
||||||
|
|||||||
@ -7,33 +7,64 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* {@code CallExpressionNode} 表示抽象语法树(AST)中的函数调用表达式节点。
|
* {@code CallExpressionNode} 表示抽象语法树(AST)中的函数调用表达式节点。
|
||||||
* <p>
|
* <p>
|
||||||
* 函数调用表达式用于表示函数或过程的调用操作,
|
* 函数调用表达式用于表示函数或过程的调用操作,包含被调用对象(callee)以及一组参数表达式(arguments)。
|
||||||
* 包括被调用对象(callee)以及一组参数表达式(arguments)。
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param callee 被调用的表达式节点,通常为函数标识符或成员访问表达式。
|
* @param callee 被调用的表达式节点,通常为函数标识符或成员访问表达式,表示函数名或方法名等。
|
||||||
* @param arguments 参数表达式列表,依照调用顺序排列。
|
* @param arguments 参数表达式列表,表示函数调用中传递给函数的实际参数。参数的顺序与调用顺序一致。
|
||||||
|
* @param line 当前表达式所在的行号,方便调试和错误定位。
|
||||||
|
* @param column 当前表达式所在的列号,用于精确定位错误位置。
|
||||||
*/
|
*/
|
||||||
public record CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments) implements ExpressionNode {
|
public record CallExpressionNode(
|
||||||
|
ExpressionNode callee, // 被调用的表达式节点,表示函数或方法名
|
||||||
|
List<ExpressionNode> arguments, // 函数调用的参数表达式列表
|
||||||
|
int line, // 当前节点所在的行号
|
||||||
|
int column, // 当前节点所在的列号
|
||||||
|
String file // 当前节点所在的文件
|
||||||
|
) implements ExpressionNode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回函数调用表达式的字符串形式。
|
* 返回函数调用表达式的字符串形式,便于调试与语法树可视化。
|
||||||
* <p>
|
* <p>
|
||||||
* 该格式将输出为类似 {@code foo(a, b, c)} 的形式,
|
* 该方法将表达式节点转化为类似 {@code foo(a, b, c)} 的格式,便于查看和理解抽象语法树的结构。
|
||||||
* 便于调试与语法树可视化。
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @return 表示函数调用的字符串表示
|
* @return 表示函数调用的字符串表示,格式为 {@code callee(arguments)}。
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(callee).append("(");
|
sb.append(callee).append("("); // 拼接函数名和左括号
|
||||||
for (int i = 0; i < arguments.size(); i++) {
|
for (int i = 0; i < arguments.size(); i++) {
|
||||||
sb.append(arguments.get(i));
|
sb.append(arguments.get(i)); // 拼接每个参数
|
||||||
if (i + 1 < arguments.size()) sb.append(", ");
|
if (i + 1 < arguments.size()) sb.append(", "); // 如果不是最后一个参数,添加逗号和空格
|
||||||
}
|
}
|
||||||
sb.append(")");
|
sb.append(")"); // 拼接右括号
|
||||||
return sb.toString();
|
return sb.toString(); // 返回拼接好的字符串
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前表达式所在的行号。
|
||||||
|
*
|
||||||
|
* @return 当前表达式的行号。
|
||||||
|
*/
|
||||||
|
public int line() {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前表达式所在的列号。
|
||||||
|
*
|
||||||
|
* @return 当前表达式的列号。
|
||||||
|
*/
|
||||||
|
public int column() {
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前表达式所在的文件名。
|
||||||
|
*
|
||||||
|
* @return 当前表达式所在的文件名。
|
||||||
|
*/
|
||||||
|
public String file() { return file; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package org.jcnc.snow.compiler.parser.context;
|
package org.jcnc.snow.compiler.parser.context;
|
||||||
|
|
||||||
import org.jcnc.snow.compiler.lexer.token.Token;
|
import org.jcnc.snow.compiler.lexer.token.Token;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,15 +18,27 @@ public class ParserContext {
|
|||||||
/** 当前语法分析所使用的 Token 流 */
|
/** 当前语法分析所使用的 Token 流 */
|
||||||
private final TokenStream tokens;
|
private final TokenStream tokens;
|
||||||
|
|
||||||
|
/** 当前语法分析所使用的资源文件名 */
|
||||||
|
private final String sourceName;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用词法分析得到的 Token 列表构造上下文。
|
* 构造一个新的 {@code ParserContext} 实例,用于在语法分析阶段传递上下文信息。
|
||||||
|
* <p>
|
||||||
|
* 本构造方法接收词法分析得到的 {@link Token} 列表以及当前源文件名,<br>
|
||||||
|
* 并将 Token 列表包装为 {@link TokenStream} 以便后续遍历与分析。<br>
|
||||||
|
* 源文件名通常用于错误定位、调试和报错信息中指明具体文件。
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param tokens 词法分析器生成的 Token 集合
|
* @param tokens 词法分析器生成的 Token 集合,表示待解析的完整源代码流
|
||||||
|
* @param sourceName 当前正在解析的源文件名(或文件路径),用于错误报告和调试定位
|
||||||
*/
|
*/
|
||||||
public ParserContext(List<Token> tokens) {
|
public ParserContext(List<Token> tokens, String sourceName) {
|
||||||
this.tokens = new TokenStream(tokens);
|
this.tokens = new TokenStream(tokens);
|
||||||
|
this.sourceName = Paths.get(sourceName).toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取封装的 Token 流,用于驱动语法分析过程。
|
* 获取封装的 Token 流,用于驱动语法分析过程。
|
||||||
*
|
*
|
||||||
@ -33,4 +47,13 @@ public class ParserContext {
|
|||||||
public TokenStream getTokens() {
|
public TokenStream getTokens() {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取资源文件名,用于发现错误后展示文件名。
|
||||||
|
*
|
||||||
|
* @return 当前语法分析所使用的资源文件名
|
||||||
|
*/
|
||||||
|
public String getSourceName() {
|
||||||
|
return sourceName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ public record ParserEngine(ParserContext ctx) {
|
|||||||
List<String> errs = new ArrayList<>();
|
List<String> errs = new ArrayList<>();
|
||||||
TokenStream ts = ctx.getTokens();
|
TokenStream ts = ctx.getTokens();
|
||||||
|
|
||||||
while (ts.isAtEnd()) { // ← 取反
|
while (ts.isAtEnd()) {
|
||||||
// 跳过空行
|
// 跳过空行
|
||||||
if (ts.peek().getType() == TokenType.NEWLINE) {
|
if (ts.peek().getType() == TokenType.NEWLINE) {
|
||||||
ts.next();
|
ts.next();
|
||||||
|
|||||||
@ -12,8 +12,7 @@ import java.util.List;
|
|||||||
* {@code CallParselet} 表示函数调用语法的中缀解析器。
|
* {@code CallParselet} 表示函数调用语法的中缀解析器。
|
||||||
* <p>
|
* <p>
|
||||||
* 用于处理形如 {@code foo(arg1, arg2)} 的函数调用结构。
|
* 用于处理形如 {@code foo(arg1, arg2)} 的函数调用结构。
|
||||||
* 在 Pratt 解析器架构中,该解析器在函数名之后接收括号开始的调用参数,
|
* 在 Pratt 解析器架构中,该解析器在函数名之后接收括号开始的调用参数,构建 {@link CallExpressionNode} 抽象语法树节点。
|
||||||
* 构建 {@link CallExpressionNode} 抽象语法树节点。
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class CallParselet implements InfixParselet {
|
public class CallParselet implements InfixParselet {
|
||||||
@ -31,6 +30,11 @@ public class CallParselet implements InfixParselet {
|
|||||||
|
|
||||||
List<ExpressionNode> args = new ArrayList<>();
|
List<ExpressionNode> args = new ArrayList<>();
|
||||||
|
|
||||||
|
// 获取当前 token 的行号和列号
|
||||||
|
int line = ctx.getTokens().peek().getLine();
|
||||||
|
int column = ctx.getTokens().peek().getCol();
|
||||||
|
|
||||||
|
// 解析函数调用参数
|
||||||
if (!ctx.getTokens().peek().getLexeme().equals(")")) {
|
if (!ctx.getTokens().peek().getLexeme().equals(")")) {
|
||||||
do {
|
do {
|
||||||
args.add(new PrattExpressionParser().parse(ctx));
|
args.add(new PrattExpressionParser().parse(ctx));
|
||||||
@ -38,7 +42,10 @@ public class CallParselet implements InfixParselet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.getTokens().expect(")"); // 消费并验证 ")"
|
ctx.getTokens().expect(")"); // 消费并验证 ")"
|
||||||
return new CallExpressionNode(left, args);
|
|
||||||
|
// 创建 CallExpressionNode 并传递位置信息,文件名称
|
||||||
|
String file = ctx.getSourceName();
|
||||||
|
return new CallExpressionNode(left, args, line, column, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -88,9 +88,8 @@ public class ModuleParser implements TopLevelParser {
|
|||||||
// 确保模块体以 "end module" 结束
|
// 确保模块体以 "end module" 结束
|
||||||
ts.expect("end");
|
ts.expect("end");
|
||||||
ts.expect("module");
|
ts.expect("module");
|
||||||
ts.expectType(TokenType.NEWLINE);
|
|
||||||
|
|
||||||
// 构建并返回完整的模块语法树节点
|
// 构建并返回完整的模块语法树节点
|
||||||
return new ModuleNode(name, imports, functions);
|
return new ModuleNode(name, imports, functions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ public class ASTJsonSerializer {
|
|||||||
case IdentifierNode(String name) -> exprMap("Identifier", "name", name);
|
case IdentifierNode(String name) -> exprMap("Identifier", "name", name);
|
||||||
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
|
case NumberLiteralNode(String value) -> exprMap("NumberLiteral", "value", value);
|
||||||
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
|
case StringLiteralNode(String value) -> exprMap("StringLiteral", "value", value);
|
||||||
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments) -> {
|
case CallExpressionNode(ExpressionNode callee, List<ExpressionNode> arguments, int line, int column, String file) -> {
|
||||||
List<Object> args = new ArrayList<>(arguments.size());
|
List<Object> args = new ArrayList<>(arguments.size());
|
||||||
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
|
for (ExpressionNode arg : arguments) args.add(exprToMap(arg));
|
||||||
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
|
yield exprMap("CallExpression", "callee", exprToMap(callee), "arguments", args);
|
||||||
@ -195,4 +195,4 @@ public class ASTJsonSerializer {
|
|||||||
default -> Map.of("type", expr.getClass().getSimpleName());
|
default -> Map.of("type", expr.getClass().getSimpleName());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,32 +1,65 @@
|
|||||||
package org.jcnc.snow.compiler.semantic.error;
|
package org.jcnc.snow.compiler.semantic.error;
|
||||||
|
|
||||||
|
|
||||||
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
import org.jcnc.snow.compiler.parser.ast.base.Node;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表示一次语义错误。<br/>
|
* 表示一次语义错误(Semantic Error)。
|
||||||
|
* <p>
|
||||||
|
* 本类用于在语义分析阶段记录出错的 AST 节点及对应的错误信息,<br>
|
||||||
|
* 便于后续错误报告、调试和 IDE 集成等多种用途。
|
||||||
|
* </p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>记录对应 {@link Node} 及出错信息;</li>
|
* <li>通过关联的 {@link Node} 提供出错的具体位置(文件、行号、列号等)信息;</li>
|
||||||
* <li>重写 {@link #toString()},以 <code>行 X, 列 Y: message</code> 格式输出,</li>
|
* <li>支持格式化错误输出,友好展示错误发生的上下文;</li>
|
||||||
* <li>避免默认的 <code>Node@hash</code> 形式。</li>
|
* <li>避免直接输出 AST 节点的默认 <code>toString()</code> 形式。</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p><b>示例输出:</b></p>
|
||||||
|
* <pre>
|
||||||
|
* D:\Devs\IdeaProjects\Snow\playground\Main.snow: 行 7, 列 28: 参数类型不匹配 (位置 1): 期望 int, 实际 long
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param node 指向发生语义错误的 AST 节点,可用于获取详细的位置信息(文件名、行号、列号等)
|
||||||
|
* @param message 描述该语义错误的详细信息,通常为人类可读的解释或修正建议
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public record SemanticError(Node node, String message) {
|
public record SemanticError(Node node, String message) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回该语义错误的字符串描述,格式如下:
|
||||||
|
* <pre>
|
||||||
|
* [文件绝对路径: ]行 X, 列 Y: [错误信息]
|
||||||
|
* </pre>
|
||||||
|
* 若节点未能提供有效位置,则输出“未知位置”。
|
||||||
|
*
|
||||||
|
* @return 适合用户阅读的语义错误描述字符串
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
// Node 假定提供 line() / column() 方法;如无则返回 -1
|
// Node 假定提供 line() / column() 方法;如无则返回 -1
|
||||||
int line = -1;
|
int line = -1;
|
||||||
int col = -1;
|
int col = -1;
|
||||||
|
String file = null;
|
||||||
|
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
try {
|
try {
|
||||||
line = (int) node.getClass().getMethod("line").invoke(node);
|
line = (int) node.getClass().getMethod("line").invoke(node);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
col = (int) node.getClass().getMethod("column").invoke(node);
|
col = (int) node.getClass().getMethod("column").invoke(node);
|
||||||
} catch (ReflectiveOperationException ignored) {
|
} catch (Exception ignored) {
|
||||||
// 若 Node 未提供 line/column 方法则保持 -1
|
}
|
||||||
|
try {
|
||||||
|
file = (String) node.getClass().getMethod("file").invoke(node);
|
||||||
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String pos = (line >= 0 && col >= 0) ? ("行 " + line + ", 列 " + col) : "未知位置";
|
|
||||||
return pos + ": " + message;
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (file != null && !file.isBlank()) sb.append(file).append(": ");
|
||||||
|
sb.append((line >= 0 && col >= 0) ? "行 " + line + ", 列 " + col : "未知位置");
|
||||||
|
sb.append(": ").append(message);
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user