Lattice 语法参考 · 第 1 章,共 4 章
源码文件与声明
本章定义线性故事和 CYOA 都能使用的公共语法,包括项目如何展开,以及建立故事身份和叙事语境的全部声明。
文档由什么组成
一个 .la 文件包含零个或多个顶层条目。完整的顶层词汇如下:
use "./relative-file.la";
story main (...);
entity character ch_mara (...);
predicate knows (...);
state group location (...);
state st_knows_signal (...): event { ... };
pool investigation (...);
module md_decode (...) { ... }
scene garden (...) { ... }predicate、state、pool 和 module 只属于 CYOA。线性项目只能使用导入、唯一 Story、实体、顶层 Scene,以及嵌套在 Scene 里的 Block 和内容。
文件与导入
项目配置指定一个入口文件,通常是 main.la。TypeApe 从这里开始,递归展开每一条 use。
# main.la
use "./story/entities.la";
use "./story/opening.la";
story main (
title: "信号花园",
language: "zh-CN",
format: novel,
entry: opening
);导入路径必须:
- 是带引号的路径,并相对于写下
use的文件解析; - 指向当前项目内的文件;
- 不能通过
..或指向外部的符号链接逃离项目; - 不能形成导入循环;
- 不能通过不同相对路径重复载入同一个文件。
被导入文件的声明会排在导入者自身声明之前。这不会创建 Block 流转,但会让声明顺序保持确定,并决定符合条件的 CYOA Module 在界面中的展示顺序。
全部展开后,整个项目必须恰好包含一个 story 声明。被导入文件应该提供实体、Scene、State 或 Module,而不是再次声明 Story。
注释、逗号与分号
只有 # 能开始行注释,// 不是 Lattice 注释。
# 这是一条 Lattice 注释。
entity item it_key (name: "信号钥匙"); # 行尾注释| 结构 | 结束方式 |
|---|---|
use、story、entity、predicate、state、state group、pool | ; |
带主体的 scene 或 module | 以 } 结束,不加 ; |
text、dialogue、foreshadowing、reveals | ; |
next、end、enter、complete、每个 option | ; |
外层 choice { ... } | 以 } 结束,不加 ; |
元数据属性和事件字段使用逗号分隔。文档语法没有定义尾随逗号,因此最后一个属性之后不要加逗号。
标识符、字符串与正文
Lattice 有意区分身份、精确文本和叙事正文。
| 形式 | 示例 | 用途 |
|---|---|---|
| 标识符 | opening、ch_mara、resolution | 声明 ID、引用和枚举值 |
| 字符串 | "zh-CN"、"黎明之前" | 明确要求带引号的精确文本 |
| 正文 | { 一束光苏醒。 } | 叙事内容或文本元数据 |
| 数字 | 3、1.5 | 被允许的事件或扩展字面量 |
| 布尔值 | true、false | 被允许的事件或扩展字面量 |
| 列表 | [opening, resolution] | 有序元数据值,也可以是空列表 [] |
身份不是标题
在下面的声明中,ch_mara 是稳定身份,"玛拉" 是展示文本:
entity character ch_mara (name: "玛拉");读者看到的名字变化时修改 name。只有实体身份本身发生变化时才修改 ch_mara,并同步更新所有引用,再做全局校验。不要给 ID 引用加引号:
# 正确
pov: ch_mara
# 错误:这是字符串,不是角色引用
pov: "ch_mara"正文花括号属于语法
正文以 { 开始、以 } 结束,内部换行和普通标点都会保留。
logline: {
一位耐心的信号猎人发现,明天正在
从她的花园地下传来回应。
}叙事段落和文本元数据使用正文。像 language 这样明确要求字符串的字段则使用带引号字符串。
元数据
元数据写在声明 ID 后面的圆括号里:
scene opening_scene (
title: "开篇",
beat: opening,
pov: ch_mara
) {
# Blocks
}同一个声明里,每个键最多出现一次。字段会根据所有者检查类型:某个字段即使对 Scene 有效,也可能不能用于 Story 或 Block。
未知键会产生错误,除非它以 x_ 开始:
scene opening_scene (
title: "开篇",
x_editor_color: "blue"
) { ... }x_ 元数据会被保留,但没有内置语义。除非某个具名产品能力明确支持,否则它不能创建路径、改变预览、修改 State、绕过诊断或影响导出。
Story 声明
导入展开后,项目中只能有一个 Story。
story main (
title: "信号花园",
language: "zh-CN",
format: novel,
entry: opening,
logline: { 一个埋藏的信号把猎人召回家。 },
audience: "青少年",
content_rating: "Teen",
genres: [mystery, science_fiction],
themes: [memory, belonging],
tags: [draft],
prose_unit: non_whitespace_graphemes,
prose_scope: authored_body,
prose_minimum: 4500,
prose_target: 5000,
prose_maximum: 5500,
beat_order: [opening, midpoint, resolution]
);| 字段 | 必填 | 接受的值 | 含义 |
|---|---|---|---|
title | 是 | 字符串或正文 | 面向读者的故事标题 |
language | 是 | 带引号的 BCP 47 字符串 | 故事正文语言 |
format | 是 | novel 或 screenplay | 展示/导出格式 |
entry | 是 | Block ID | 阅读开始的主故事 Block |
logline、audience、content_rating | 否 | 文本 | 描述性元数据 |
genres、themes、tags | 否 | 文本或列表 | 分类元数据 |
prose_unit | 有目标时 | 计数单位标识符 | word_like 或 non_whitespace_graphemes |
prose_scope | 有目标时 | authored_body | 面向读者的已创作正文 |
prose_target | 有目标时 | 正整数 | 预期正文总长度 |
prose_minimum、prose_maximum | 否 | 正整数 | 可选的交付边界 |
beat_order | 否 | 标识符列表 | Scene 节拍应遵循的全局顺序 |
initial_state | 仅 CYOA | State ID 列表 | 运行开始时激活的 State |
入口必须存在于主故事中。属于 Module 的 Block 不能成为 Story 入口。线性故事不能声明 initial_state,即使它只是空列表。
prose_unit、prose_scope 和 prose_target 组成一个可选目标,必须一起声明。存在边界时须满足 minimum <= target <= maximum。word_like 统计类单词单元;中文“字数”请求使用 non_whitespace_graphemes。只有 TypeApe 内置字段会进入创作契约,x_* 元数据保持惰性。
实体声明
实体声明的形态是 entity <kind> <id> (...)。
entity character ch_mara (
name: "玛拉",
role: protagonist,
description: { 一位耐心的信号猎人。 },
aliases: ["玛拉·文", "倾听者"],
traits: [observant, stubborn],
tags: [cast, point_of_view]
);
entity location loc_garden (
name: "信号花园",
description: { 湿润土壤下埋藏着天线阵列。 },
tags: [exterior]
);
entity item it_key (
name: "信号钥匙",
description: { 一枚会在午夜搏动的温热碎片。 }
);支持的 kind 是 character、location 和 item。所有实体都必须有文本 name。
| Kind | 可选字段 |
|---|---|
character | role 标识符;description 文本;aliases、traits、tags 文本或列表 |
location | description 文本;tags 文本或列表 |
item | description 文本;tags 文本或列表 |
引用会检查实体类型。Dialogue 说话者和 Scene pov 必须是角色;Scene location 必须是地点。CYOA Predicate 元组也会确认每个实体符合声明的字段类型。
Scene 声明
Scene 把多个 Block 组织在共同叙事语境下。
scene garden (
title: "花园",
summary: { 玛拉听见了埋藏的信号。 },
beat: opening,
pov: ch_mara,
location: loc_garden,
time: "黎明之前",
tone: [quiet, uneasy],
prose_target: 1200,
tags: [chapter_one]
) {
block opening (...) { ... }
}| 字段 | 必填 | 接受的值 |
|---|---|---|
title | 是 | 文本 |
summary、time | 否 | 文本 |
beat、pov、location | 否 | 标识符/引用 |
tone、tags | 否 | 文本或列表 |
prose_target | 否 | 正整数 |
顶层 Scene 属于主故事。嵌套在 Module 里的 Scene 只属于该 Module。每个 Block 都继承 Scene 的所有者,流转目标不能从主故事跳入 Module、从一个 Module 跳到另一个 Module,也不能从 Module 跳回主故事。
Scene 的 prose_target 是可以重新分配的创作预算,不是独立的有效性阈值。它要求 Story 已经声明完整的正文目标。
安全的多文件布局
project/
├── main.la
└── story/
├── entities.la
├── opening.la
└── ending.la# main.la
use "./story/entities.la";
use "./story/opening.la";
use "./story/ending.la";
story main (
title: "信号花园",
language: "zh-CN",
format: novel,
entry: opening
);导入只负责组织声明,绝不会暗示叙事顺序。下一章介绍 Block、内容、批注与线性流转。
