工具链

REPL 交互模式

Aria 提供了基于 JLine3 的交互式命令行(REPL),支持 Tab 补全、历史导航和多行编辑。

启动方式

java -cp aria.jar priv.seventeen.artist.aria.AriaREPL

启动后显示:

Aria REPL v1.0 (JLine3)
Type 'exit' to quit, 'reset' to clear context.

>>>

快捷键

快捷键说明
Tab补全关键字、命名空间、内置函数
/ 历史命令导航
Ctrl+C取消当前行输入
Ctrl+D退出 REPL

历史记录

REPL 自动将输入历史持久化到 ~/.aria_history,下次启动时自动加载,支持跨会话的历史导航。

基本使用

输入表达式后回车即可执行,结果以 = 前缀显示:

>>> var.x = 42
>>> var.y = x + 8
= 50.0

多行输入

当输入行以 { 结尾时,REPL 自动进入多行模式(提示符变为 ...),直到花括号匹配完成:

>>> if (x > 10) {
...     return 'big'
... } else {
...     return 'small'
... }
= big

REPL 通过计算 {} 的数量来判断多行输入是否完成。

内置命令

命令说明
exit退出 REPL
quit退出 REPL(同 exit)
reset重置上下文,清除所有已定义的变量和函数

执行 reset 后会创建一个全新的 Context,之前定义的所有变量和函数都会被清除。

错误处理

执行出错时,REPL 会打印错误信息并重置多行输入状态,不会退出:

>>> var.x = 1 / 0
Error: Division by zero
>>>

LSP 语言服务器

Aria 提供了基于 LSP4J 的语言服务器(aria-lsp 模块),支持在编辑器中获得语法检查、代码补全等功能。

支持的功能

功能说明
实时诊断文档打开、修改、保存时自动进行语法分析并报告错误
自动补全支持 .:@ 触发字符的上下文补全
悬停提示鼠标悬停在标识符上显示类型和定义信息
跳转到定义跳转到变量、函数、类的定义位置
文档符号显示文档中的变量、函数、类、字段、方法、导入等符号大纲

文档同步采用全量更新模式(TextDocumentSyncKind.Full),每次变更发送完整文档内容。

启动方式

LSP 服务器通过 stdio 与客户端通信:

java -cp aria-lsp.jar priv.seventeen.artist.aria.lsp.AriaLspLauncher

服务器启动后通过标准输入/输出与编辑器交换 JSON-RPC 消息。

VSCode 集成配置

在 VSCode 扩展的 package.json 中配置语言客户端:

{
  "contributes": {
    "languages": [{
      "id": "aria",
      "extensions": [".aria"],
      "aliases": ["Aria"]
    }]
  }
}

在扩展的 extension.ts 中启动语言客户端:

import { LanguageClient, ServerOptions, TransportKind } from 'vscode-languageclient/node';

const serverOptions: ServerOptions = {
  command: 'java',
  args: ['-cp', 'aria-lsp.jar', 'priv.seventeen.artist.aria.lsp.AriaLspLauncher'],
  transport: TransportKind.stdio
};

const clientOptions = {
  documentSelector: [{ scheme: 'file', language: 'aria' }]
};

const client = new LanguageClient('aria', 'Aria Language Server', serverOptions, clientOptions);
client.start();

符号类型映射

LSP 服务器将 Aria 内部符号类型映射为标准 LSP 符号类型:

Aria 符号LSP SymbolKind
VARIABLEVariable
FUNCTIONFunction
CLASSClass
PARAMETERVariable
FIELDField
METHODMethod
IMPORTModule

静态编译

Aria 支持将编译后的 IR 程序序列化为 .aria 二进制文件,实现预编译和快速加载。

.aria 二进制格式

文件结构:

[Magic: 'A' 'R' 0x00 0x01]  (4 bytes)
[Flags]                       (2 bytes, short)
[Name]                        (UTF string)
[RegisterCount]               (4 bytes, int)
[Constants]                   (常量池)
[VariableKeys]                (变量键表)
[Instructions]                (指令序列)
[SubPrograms]                 (子程序,递归结构)
[SourceMap]                   (可选,由 FLAG_HAS_SOURCE_MAP 控制)

常量池类型标记:

标记类型
0None
1Number (double)
2Boolean
3String (UTF)

标志位:

标志说明
FLAG_HAS_SOURCE_MAP0x01包含源码映射信息
FLAG_HAS_CLASSES0x02包含类定义

编译 API

将 IR 程序写入 .aria 文件:

import priv.seventeen.artist.aria.staticcompile.AriaFileWriter;

// 编译源码
AriaCompiledRoutine routine = Aria.compile("myScript", code);

// 写入二进制文件(不含源码映射)
AriaFileWriter.write(routine.getProgram(), Path.of("myScript.aria"));

// 写入二进制文件(含源码映射,用于调试)
AriaFileWriter.write(routine.getProgram(), Path.of("myScript.aria"), true);

加载 API

.aria 文件加载 IR 程序:

import priv.seventeen.artist.aria.staticcompile.AriaFileReader;

IRProgram program = AriaFileReader.read(Path.of("myScript.aria"));

加载时会验证文件头的 Magic 字节(AR\x00\x01),格式不匹配时抛出 IOException

模块打包

Aria 支持将多个编译后的模块打包为 .ariapkg 包文件,基于 ZIP 格式。

.ariapkg 包结构

myPackage.ariapkg (ZIP)
├── META-INF/
│   └── MANIFEST.ARIA          (属性文件,包元数据)
├── modules/
│   ├── main.aria               (编译后的 .aria 二进制模块)
│   ├── utils.aria
│   └── ...
└── resources/
    ├── config.json            (资源文件)
    └── ...

打包 API

import priv.seventeen.artist.aria.staticcompile.AriaPackager;

AriaPackager packager = new AriaPackager();

// 设置清单信息
packager.setManifestEntry("Main-Module", "main");
packager.setManifestEntry("Version", "1.0.0");

// 添加编译后的模块
packager.addModule("main", mainProgram);
packager.addModule("utils", utilsProgram);

// 添加资源文件
packager.addResource("config.json", configBytes);

// 写入包文件
packager.writeTo(Path.of("myPackage.ariapkg"));

读取 API

import priv.seventeen.artist.aria.staticcompile.AriaPackageReader;

AriaPackageReader reader = AriaPackageReader.read(Path.of("myPackage.ariapkg"));

// 读取清单
Properties manifest = reader.getManifest();
String mainModule = manifest.getProperty("Main-Module");

// 列出所有模块
Set<String> modules = reader.getModuleNames();

// 加载指定模块
IRProgram main = reader.getModule("main");

// 读取资源
byte[] config = reader.getResource("config.json");

getModule() 方法会自动补全 .aria 后缀,内部通过临时文件调用 AriaFileReader 反序列化 IR 程序。