跳到主要内容

模块化系统

Alia 的模块化系统支持代码组织和数据导入,通过 import 和 export 语句实现模块间的代码复用和数据共享。

1. 导入模块​

基本语法​

import <String modulePath> [as <Key alias>]

导入 Alia 模块​

~ 导入模块并使用别名
import 'utils.alia' as utils
print utils.format 'Hello'

~ 直接导入(模块内容合并到当前作用域)
import 'math.alia'
print add 1 and 2

导入数据文件​

Alia 支持直接导入 JSON 和 CSV 文件,自动转换为对应的内置类型。

导入 JSON 文件​

JSON 文件自动转换为 Object 类型:

~ config.json 文件内容:
~ { "name": "Alia", "version": 0.1, "features": ["type-safe", "extensible"] }

import 'config.json' as config

print Object.get config keyed 'name' ~ 输出 'Alia'
print Object.get config keyed 'version' ~ 输出 0.1

转换规则:

  • JSON 对象 → Alia Object
  • JSON 数组 → Alia Array
  • JSON 字符串 → Alia String
  • JSON 数字 → Alia Number
  • JSON 布尔值 → Alia Boolean
  • JSON null → Alia Null

导入 CSV 文件​

CSV 文件自动转换为 Sheet 类型,第一行为表头:

~ data.csv 文件内容:
~ name,age,city,score
~ Alice,30,Beijing,95
~ Bob,25,Shanghai,88

import 'data.csv' as sheet

print sheet.getCell at row 0 column 'name' ~ 输出 'Alice'
print sheet.getCell at row 1 column 'age' ~ 输出 25
print sheet.headers ~ 输出 name,age,city,score

转换规则:

  • CSV 第一行 → Sheet 表头(headers)
  • CSV 后续行 → Sheet 数据行(data)
  • 数字字符串 → Alia Number
  • 小写 true/false 或大写 TRUE/FALSE → Alia Boolean
  • 空单元格或 null/NULL → Alia Null
  • 引号包裹的字段 → 支持包含逗号的字段

导入文本文件​

任何非 .json、.csv、.alia 后缀的文件都会自动转换为 String 类型:

~ 导入 HTML 模板
import 'template.html' as template
print template

~ 导入配置文件
import 'settings.ini' as settings
print settings.includes 'debug'

~ 导入 Markdown 文件
import 'readme.md' as content
print content.split '\n'

转换规则:

  • 文件内容 → Alia String 类型
  • 保留原始换行符和空格
  • 可以使用 String 的所有方法(如 split、slice、replace 等)

模块别名​

使用 as 关键字为导入的模块指定别名:

import 'long-module-name.alia' as m
print m.doSomething

2. 导出模块​

基本语法​

export <Value exportValue>

默认导出​

导出当前模块语境的所有内容:

~ utils.alia
const format = alias <String str> -> String {
'[ ' .. str .. ' ]'
}

const add = alias <Number a> and <Number b> -> Number {
a + b
}

export this

选择性导出​

导出特定的函数或对象:

~ math.alia
const PI = 3.14159
const E = 2.71828

export *[ PI: PI, E: E ]

导出单个值​

~ constants.alia
const MAX_VALUE = 1000
export *[ MAX_VALUE: MAX_VALUE ]

export <Value> 直接以该表达式的求值结果作为模块的默认导出;导出具名绑定须使用 export *[...] 对象形式。

3. 模块路径​

标准库路径​

内置标准库名称(args、ffi、fs、io、process、web)不带目录前缀时直接加载对应的内置实现,不经过文件路径解析:

import 'fs' as fs
import 'process' as process

相对路径​

相对于当前文件的路径:

import './utils.alia' as utils
import '../config.json' as config

项目路径​

在项目中(存在 main.alia 的目录),不带目录前缀的导入会根据文件类型自动解析:

文件类型查找路径
.json项目根目录/assets/xxx.json
.csv项目根目录/assets/xxx.csv
.alia项目根目录/libs/xxx.alia
其他后缀项目根目录/assets/xxx.后缀
无后缀项目根目录/libs/xxx.alia

模组空间包解析:当 libs/ 下不存在目标模块时,系统会继续在 项目根目录/mods/<包名>@<版本>/lib.alia 中查找(版本取自 config.json 的 dependencies,缺失时回退到 mods/ 下名称匹配的目录):

~ 已安装的模组(alia add gtk-alia 安装到 mods/gtk-alia@0.1.0/)
import 'gtk-alia' as gtk
~ 在项目中,自动从 assets/ 查找 JSON 文件
import 'config.json' as config

~ 在项目中,自动从 libs/ 查找 Alia 模块
import 'utils' as utils

独立文件/代码路径​

不在项目中时,不带目录前缀的导入相对于当前执行目录解析:

~ 独立文件中,相对于当前执行目录
import 'data.json' as data

扩展名规则​

  • .alia:Alia 源代码文件
  • .json:JSON 数据文件(自动转换为 Object)
  • .csv:CSV 数据文件(自动转换为 Sheet)
  • 其他后缀名:文本文件(自动转换为 String)

如果省略扩展名,默认添加 .alia:

import 'utils' ~ 等价于 import 'utils.alia'

4. 模块缓存​

模块导入采用缓存机制,同一模块在同一运行时环境中只会被加载一次:

~ 第一次导入,执行并缓存
import 'cache.alia' as c1

~ 第二次导入,直接返回缓存
import 'cache.alia' as c2

~ c1 和 c2 指向同一模块实例

5. 使用示例​

示例 1:配置管理​

~ config.json
~ {
~ "server": "localhost",
~ "port": 8080,
~ "debug": true
~ }

import 'config.json' as config

print ('Server: ' .. (Object.get config keyed 'server'))
print ('Port: ' .. (Object.get config keyed 'port'))

if (Object.get config keyed 'debug').valueOf {
print 'Debug mode enabled'
}

示例 2:数据处理​

~ sales.csv
~ product,region,amount
~ Laptop,North,1000
~ Phone,South,800
~ Laptop,South,1200

import 'sales.csv' as salesData

~ 读取整列
const products = salesData.getColumn 'product'
print products

~ 读取单元格
print salesData.getCell at row 0 column 'amount'
print salesData.getCell at row 2 column 'region'

示例 3:模块组合​

~ constants.alia
const PI = 3.14159
export *[ PI: PI ]

~ calculator.alia
import 'constants.alia'

const circleArea = alias <Number radius> -> Number {
PI * radius * radius
}

export *[ circleArea: circleArea ]

~ main.alia
import 'calculator.alia' as calc
print 'Circle area: ' .. (calc.circleArea 5)

示例 4:文本文件导入​

~ template.html 文件内容:
~ <div class="container">
~ <h1>Hello, {{name}}!</h1>
~ <p>Welcome to Alia.</p>
~ </div>

import 'template.html' as template

~ 获取模板内容
print template

~ 按行处理
const lines = template.split '\n'
print 'Template has ' .. lines.length .. ' lines'

~ 查找占位符
if template.includes '{{name}}' {
print 'Template contains placeholder'
}

6. 导入的编译过程​

在编译模式下,import 语句的处理由后端(如 js.alia)中的 @compileNode "ImportStatement" 覆写完成。

编译时的 importData 结构​

编译器为每个 ImportStatement 节点构建 importData 对象,包含以下字段:

字段类型说明
sourceString原始导入路径
aliasString导入别名
sourceDirString源文件目录
extString文件扩展名(如 .alia、.json、.csv)
resolvedPathString解析后的绝对文件路径
contentEncodedStringJSON 编码后的文件内容
contentStrString原始文件内容
exportsObjectnull

自定义导入编译​

可在后端覆写 ImportStatement 节点来自定义导入行为:

@compileNode "ImportStatement" -> js {
let data = _bound.importData
let safeAlias = safeObjectName data.alias

if data.ext == '.alia' {
~ 使用 @compile 递归编译 Alia 模块
let content = readFile data.resolvedPath
let compiled = @compile "js" content (dirname data.resolvedPath)
~ 包装导出结果
let exportNames = []
if data.exports & data.exports.names {
exportNames = data.exports.names
}
if exportNames.length > 0 {
let names = ''
let i = 0
while i < exportNames.length {
if i > 0 { names = names .. ', ' }
names = names .. exportNames[i]
i = i + 1
}
compiled = '(function() {\n' .. compiled .. '\nreturn { ' .. names .. ' };\n})()'
} else {
compiled = 'wrapObject(' .. compiled .. ')'
}
return 'const ' .. safeAlias .. ' = ' .. compiled
}

~ JSON/CSV/文本文件使用预处理内容
return 'const ' .. safeAlias .. ' = ' .. data.contentEncoded
}

@compile 递归编译​

@compile 指令用于在编译入口或覆写中编译任意 Alia 代码片段:

@compile <Expression langName> <Expression code> [<Expression sourceDir>]
  • 共享当前编译器的 aliasRegistry 和覆写规则
  • 拥有独立的变量作用域
  • 返回编译后的目标语言代码字符串

7. 库运行时与依赖归属声明​

库(标准库或第三方模组)通过 @compileWith <lang> 把自己注册进目标语言的编译入口。基础后端不内置任何库名,一切归属关系由库自身声明,因此新增库无需改动编译器或标准库:

@compileWith nodejs {
import './runtime.js' as myRuntime
import './web-runtime.js' as myWebRuntime
registLib 'mylib' with myRuntime as '__aliaMyLib'
registDep 'my-native-dep' at '^1.0.0'
~ 同一运行时的其他 import 名统一归属到宿主库
declareLibAlias 'mylib-web' with 'mylib'
~ 该 npm 依赖是否收录,取决于宿主库是否被使用
declareLibDep 'my-native-dep' with 'mylib'
~ 该运行时的文件需在无包管理器的目标下随产物落盘
declareLibInline 'mylib' with 'web'
}
声明语义
registLib登记该语言的库运行时产物;仅当该库(或其别名)在入口中被 import 时才会写入产物
registDep登记第三方依赖
declareLibAlias声明某个 import 名由宿主库提供,避免后端按库名硬编码
declareLibDep声明依赖是否收录取决于宿主库是否被使用
declareLibInline声明库运行时文件在指定目标下需随产物落盘(如目标无包管理器、无法安装第三方运行时时的内联场景)

这些辅助方法由各后端在 @compileEntry 中定义(如 Base/Core/src/backends/nodejs.alia),语义见编译器文档。

下一步​