# [FVV 语言 - 多元化&自由](https://fvvlang.sbs/) > 迈向自由的语言 —— 配置文件还能进化成脚本?! **FVV** 全称 **Fresh View for Viewer**,名字显而易见,是个 *~~废物~~* **清新** 的语言 > 虽然 FVV 的名字是取自 “废物”,但它绝不是一种废物语言哦\~ FVV 目前大量应用于 [*回忆溢出工作组*](https://oom-wg.dev/) 相关项目与 [*Latest File*](https://latestfile.zip/) 相关项目等项目 上至**用户使用**,中至**开发配置**,下至**底层实现**,FVV 均有涉及,已经在开发实践中已经有着**相当重要**的地位 FVV 在后续也会不断拓展应用范围,贯彻**简单**、**易用**、**好用**等宗旨,在各个项目中发光发热 特别致谢 [#特别致谢] FVV 由 [Linso](https://linso.top/) 冠名发布,Linso 牛逼!!! 快速开始 [#快速开始] FVV 的开发方向由以下部分组成: * **FVV 配置格式**(主要后缀为 `.fvv`) * **FVV 图形化**(主要后缀为 `.fww`) * **FVV 脚本化**(主要后缀为 `.fws`) 一切开发方向均基于 FVV 基本语法实现,只是**使用用途**不相同 各个方向的使用文档参照以下内容: * [FVV](spec) * [FWW (FVV Widgets)](widgets) * [FWS (FVV Script)](script) 相关仓库 [#相关仓库] *** 支持信息: C++ `11` \+ Dart `3` \+ Go `1.18` \+ JavaScript/TypeScript ( *仍在开发中* ) Kotlin **多平台** *** # [FWS](https://fvvlang.sbs/script) > FWS (FVV Script) 的基本用法 FWW 是基于 FVV 语法为脚本化所立的标准, 是在**不变更任何 FVV 原有功能**的情况下,为编写脚本而仅在**语法规则**上所立的标准 FWS 是计划在 FVV **3** 推出的,目前仍在开发中 # [FVV](https://fvvlang.sbs/spec) > FVV 的基本用法 对于 FVV 的用法示例,请看如下内容 ```plaintext title="示例" <基本类型> BoolVal = true <布尔值,解析时会忽略大小写> IntVal1 = 1 <十进制整数(整数、浮点数均不支持正号)> IntVal2 = 1000'0000 <有引号的十进制整数> IntVal3 = 0x5 <十六进制整数> IntVal4 = 0o2 <八进制整数,也可写作“02”> IntVal5 = 0b0 <二进制整数> FloatVal = -1.0 <浮点数> StrVal = "字符串" <字符串> <列表> StrList = ["1", "2", "3"] <字符串列表> <不再多写...> <组> Group = { SubVal = "子值" <子值> SubGroup = { SubVal = "子值" <子组子值> } } <普通组> GroupList = [ { SubVal = "组一子值" } <组一> { SubVal = "组二子值" } <组二> ] <组列表> <赋值> Value1 = StrVal <常规赋值> Value2 = "字符串" <这里把注释给赋值了,注释的赋值只会生效于目标是字符串类型的情况,不会隐式转换> List1 = [Value1, Value2] <常规列表赋值> List2 = [List1] <列表赋值列表,会自动展开> Group1.SubVal1 = Value1 <通过点连接组名赋值到子值> Group2.SubVal = Group1.SubVal1 <用同样的方式赋值到另一个子值> Group1 = { SubGroup = { SubVal2 = SubVal1 <展开赋值> } } <赋值遵守就近原则,并且赋值的字符串将按原样存储> <仅有常规赋值会保留原样,列表或注释中的赋值不会保留> <拼接与类型提升> StrVal1 = "字符串" + "字符串" <常规拼接> StrVal2 = StrVal1 + true + 1 + 1.0 <所有非字符串基本类型进行拼接都会提升为字符串> <解析时不会记录拼接的原样,所有拼接中的赋值都会丢失> StrList1 = ["字符串" + "字符串"] <拼接在列表中也可用> StrList2 = [StrVal1, StrList1, true, 1, 1.0] <在列表中也会提升类型为优先级最高的字符串> FloatList1 = [true, 1, 1.0] <字符串之下是浮点数> IntList1 = [true, 1] <浮点数之下是整数> BoolList1 = [true] <整数之下只有布尔值了> <组不支持拼接,列表也不支持拼接(因为已经支持在列表中展开其他列表了)> <并且列表仅允许单一类型,组与基本类型不可以混合存储> <分隔符> Val1 = 1 <用换行省略了分号> Val2 = 2 <显式的分号声明>; ValList1 = [ 1 2 3 ] <用换行省略了逗号> ValList2 = [ 1, 2, 3, ] <显式的逗号声明(末尾是否有逗号不影响解析)> <其他字符> Key1: "Value" <冒号与等号等价> Key2:"Value" <全角冒号同样可用> IntVal = 1000’0000 <有全角引号的整数> Say1 = "\"Hello\" “Hello”" <常规字符串> Say2 = “"Hello" “Hello\”” <用全角引号包裹的字符串> <可以看到转译也会因此改变> Say3 = ` "Hello" “Hello” ` <用反引号包裹的原始字符串,解析时会去掉开头与结尾的空白,并且去掉最小缩进> Grp ={Lst1 =[Key1, Key2]}<全角括号(用花括号省略了应该写在“Lst1”定义后的分号)> Lst2 = ["Value",];<全角分隔符> <除引号外,其他全角字符均可与半角字符混用> ``` 显而易见,支持 `字符串`、`浮点数`、`整数`、`布尔值` 以及它们的 `列表` 的定义,还有 `组` 本身的 `列表` **命名定义** ``` 组 = { 文本 = "" 文本列表 = [""] 组列表 = [{}] } ``` * `组` 为 *组名称*,`文本`/`文本列表`/`组列表` 为 *值名称* * `""`/`[""]`/`[{}]` 为值,`{}` 称为组 故基本类型名称为 `字符串`/`文本`、`浮点数`、`整数`、`布尔值`,`组` 为特殊类型 列表类型即为 `字符串列表`/`文本列表`、`浮点数列表`、`整数列表`、`布尔值列表` 以及 `组列表` 值的命名也是没有什么忌口的,但需要注意的是, 如果一个值被命名为**纯数字**(仅 `整数`,因为 `浮点数` 会被分割为双层组), 那么这个值将**无法用于赋值**,因为没法判断到底给的是值还是值的名称 同样的,还需要注意正常命名不要带 `.` 啊喂,会被认为是多层组的定义的 值的定义使用 `;` 或 `换行` 进行, 所有的值都可以置于根路径的 `{}` 里面(放不放都没区别,只不过加一个 `{}` 看起来类 *JSON* 一点?), 块注释使用 `<>`,没有行注释 `换行` 可以省略掉以下字符: * `;`: 当定义值时有 `换行` 则无需在行尾添加 `;` * `,`: 当定义组时有 `换行` 则无需在两个值之间添加 `,` `}` 也可省略分隔符 注释是 FVV 一个比较特色的功能,它可以放到任何地方,请看示例: ``` <注释>{<注释>a<注释>=<注释>1<注释>;<注释>}<注释> ``` 可以非常直接地看出来,注释完全是想怎么写就怎么写,但是它的作用不止于此 介于 `=` 与 `;`(或 `换行`)之间的**最后一个**注释,将会被认定为是该值的 `描述`,在解析时将会被留存为该值的附属值 因为 `换行` 可以替代 `;`,`描述` 又是和定义一样通过 `换行`/`;` 来解析的, 所以 `描述` 不可以写到定义的**下一行**! 为了让 **FWW** 有更好的开发体验, `组列表` 及其子值的 `描述` 是放在前面的 `描述` 是支持**赋值**的,也就是说任意字符串都有可能赋值到 `描述` 上,只要 `描述` 与该字符串的值名称相同 所以在实践中,建议在编写 `描述` 时,在开头加一个 `- `,从而实现类似于 `<- 这是注释>` 的效果, 如果字体支持,`<-` 会变成一个好看的箭头,这样既不会与赋值机制有冲突,还可以提升观感 代码层使用方法 [#代码层使用方法] 仅支持 `UTF-8` 文本(或 `UTF-8 with BOM` ), `\r` / `\n` / `\r\n` 文本均可正常解析 引入依赖 [#引入依赖] 在项目中添加文件后,直接 `#include` 即可: ```cpp #include "fvv.hh" ``` > [Pub 发布页面](https://pub-web.flutter-io.cn/packages/fvv) > > [Dart API 文档](https://pub-web.flutter-io.cn/documentation/fvv/latest/fvv/) 先添加依赖: ```shell dart pub add fvv ``` ```shell flutter pub add fvv ``` 然后直接 `import` 即可: ```dart import 'package:fvv/fvv.dart'; ``` 先添加依赖 在 `go.mod` 中通过 `replace` 指向本地路径,并通过 `require` 添加依赖 ```plaintext title='示例' replace fvvlang.sbs/fvv => path/fvv/go require fvvlang.sbs/fvv v0.0.0 ``` 通过命令行添加依赖: ```shell go get -u fvvlang.sbs/fvv@latest ``` 然后 `import` 即可: ```go import "fvvlang.sbs/fvv" ``` 先依赖源中有 [PkgPub](https://app.niggergo.work/purejoy/pkgpub/use/) 的依赖源 然后添加依赖: ```groovy dependencies { implementation("ren.shiror.fvv:core:2.+") } ``` 再直接 `import` 即可: ```kotlin import ren.shiror.fvv.FVVV ``` 基本功能 [#基本功能] 示例代码都是瞎写的,不要依此为参照 对于开发体验着重在 `C++` 与 `Go` 上以及 `Dart` 与 `Kotlin` 上,在一起的语言开发体验较为相似 解析 [#解析] 由于 `Go` 的语言特性,其函数名称不得不大写 <> 由于 `Dart` 与 `Go` 不支持函数重载,为了保证长期维护的可能性,额外添加了 `String` 后缀 ```cpp FVV::FVVV fvv; fvv.parse(txt); ``` ```dart final fvv = FVVV()..parseString(txt); ``` ```go fvv := NewFVVV() fvv.ParseString(txt) ``` ```kotlin val fvv = FVVV().apply { parse(txt) } ``` 格式化 [#格式化] 当传入以下内容时,会有不同的格式化效果: * `Common`: 默认 * `UseWrapper`: 最外面包裹一层 *花括号* * `Minify`: 最小化,移除所有 *缩进*、*空格*、*换行* * `UseCRLF`: 使用 `\r\n` 换行,默认 `\n` * `UseCR`: 使用 `\r` 换行,默认同上 * `UseSpace2`: 使用 `2` 个 **空格** 作为 **缩进** ,默认是 `\t` * `UseSpace4`: 使用 `4` 个 **空格** 作为 **缩进** * `IntBinary`: 输出整数为 *二进制* 格式,默认是 *十进制* * `IntOctal`: 输出整数为 *八进制* 格式,默认同上 * `IntHex`: 输出整数为 *十六进制* 格式,默认同上 * `DigitSep3`: 输出 *十进制整数* 或 *浮点数的整数部分* 时每 `3` 个数字插入一个分隔符 * `DigitSep4`: 同上,每 `4` 个数字插入一个分隔符 * `UseColon`: 使用 *冒号* 而非默认的 *等号* 作为定义符 * `FullWidth`: 将部分符号改为 **全角** * `KeepListSingle`: 强制 *列表* 保持为一行(默认长度达到 `16` 的内容达到 `6` 则一行一个) * `ForceUseSeparator`: 强制添加 *分隔符*(最小化时无效) * `RawMultilineString`: 当 *字符串* 有多行时改为 *原始多行缩进字符串*(最小化时无效) * `NoDescs`: 移除所有 `描述`(在 *FWW 风格* 下对 `组列表` 与 `组` 无效) * `NoLinks`: 移除所有 `链接` * `FlattenPaths`: 递归展平只有 `1` 个值的 `组` * `FWWStyle`: FWW 风格,前置 `组` 的 `描述` 各个语言的格式化选项位置如下: * `C++`: `FVV::FormatOpt` * `Dart`: `FormatOpt` * `Go`: 以 `FmtOpt` 开头的值 * `Kotlin`: `FVVV.FormatOpt` 不同的语言在命名上略有不同,但实际行为一致 传入均支持通过 `|`/`or` 传入为一个参数或通过 `,` 传入为多个参数 当使用 `Dart` / `Kotlin` 默认的 `toString` 时( `"$fvv"` ),将使用默认的格式化效果 一切去除 `描述`/`链接` 的行为均仅为在输出的字符串中去除,不会影响到代码中的实际内容 ```cpp fvv.to_string(opts...); ``` ```dart fvv.toString(opts...); ``` ```go fvv.ToString(opts...) ``` ```kotlin fvv.toString(opts...) ``` 使用值 [#使用值] 由于 `JVM` 机制,在 `Kotlin` 上 *判断*/*获取* 列表时请使用对应的 `isList()`/`list()` 方法,否则会类型出错 首先是对值的基本判断 由于 `Go` 的语法可用性太低了,便又封装了 `IsNodesEmpty` 和 `IsNodesNotEmpty` 来判断子项是否为空, 但使用前最好还是先判断一下值本身是否为 `nil` ```cpp bool is_empty = fvv.empty(); // 判断值是否为空 bool is_type = fvv.is(); // 判断类型 is_type = fvv.is_list(); // 判断列表类型(相当于 fvv.is>()) ``` ```dart final isEmpty = fvv.isEmpty && !fvv.isNotEmpty; // 判断值是否为空 var isType = fvv.isType(); // 判断类型 isType = fvv.isList(); // 判断列表类型(相当于 fvv.isType>()) ``` ```go is_empty := fvv.IsEmpty() && !fvv.IsNotEmpty() // 判断值是否为空 is_empty = fvv.IsNodesEmpty() && !fvv.IsNodesNotEmpty() // 判断子值是否为空 is_type := fvv.IsBool() // 判断类型(也可使用 fvv.Is[T](fwv),此处的 fvv 是 fvv 包) is_type = fvv.IsBoolList() // 判断列表类型(也可使用 fvv.IsList[T](fwv),此处的 fvv 是 fvv 包) // 其他依此类推,不再示范... ``` ```kotlin val isEmpty = fvv.isEmpty() && !fvv.isNotEmpty() // 判断值是否为空 var isType = fvv.`is`() || fvv.isType() // 判断类型(不适用于列表) isType = fvv.isList() // 判断列表类型 ``` 由于 FVV 2 统一采用 `int64` 为整数类型,对于各语言的兼容性如下: * `C++`: 使用 `long long`/`vector` 类型, 存储时有兼容其他整数类型,但判断时需要使用 `long long`/`vector` * `Dart`: 由于没有细分类型,无变化 * `Go`: 由于 `Go` 没有重载,在判断中对 `int`/`[]int` 做了兼容,使用时尽量用 `int64`/`[]int64` * `Kotlin`: 在存储与判断时均有 `Int`/`List` 转 `Long`/`List` 与 `Float`/`List` 转 `Double`/`List` *** 然后是读取值 由于 `Dart` 机制,常规获取 `List` 类型得到的是引用,拷贝请再调用 `toList()` <> <>在 `Kotlin` 获取 `List` 类型时由于 `JVM` 机制无法确认类型,总会创建一个新的固定类型的 `List`,不会获取到引用 ```cpp T value = fvv.value(/*默认值*/); // 指定类型获取值 vector list = fvv.list(/*默认值*/); // 指定类型获取列表 ``` ```dart final value = fvv.as(/*默认值*/) ?: fvv.asType(/*默认值*/) ?: fvv.get(); // 指定类型获取值 //(当不确定类型时,使用 as/asType,返回 T?,确定时则可使用 get,返回 T) final list = fvv.list(/*默认值*/); // 指定类型获取列表 final boolValue = fvv.boolean; // 获取布尔值 final boolList = fvv.bools; // 获取布尔值列表 // 其他依此类推,不再示范... ``` ```go value := fvv.Value[T](fwv); // 指定类型获取值(此处的 fvv 是 fvv 包) list := fvv.List[T](); // 指定类型获取列表(此处的 fvv 是 fvv 包) bool_value := fvv.Bool(/*默认值*/); // 获取布尔值 bool_list := fvv.BoolList(/*默认值*/); // 获取布尔值列表 // 其他依此类推,不再示范... ``` ```kotlin val value = fvv.`as`(/*默认值*/) ?: fvv.asType(/*默认值*/) ?: fvv.get(); // 指定类型获取值 //(当不确定类型时,使用 as/asType,返回 T?,确定时则可使用 get,返回 T) val list = fvv.list(/*默认值*/); // 指定类型获取列表 val boolValue = fvv.bool; // 获取布尔值 val boolList = fvv.bools; // 获取布尔值列表 // 其他依此类推,不再示范... ``` 由于 `Dart` 的基本类型名称会冲突,但为了尽可能与 `Kotlin` 一致,目前的 getter 如下: * `bool`(`Kotlin`)、`boolean`(`Dart`&`Kotlin`) * `int`(`Kotlin`)、`integer`(`Dart`&`Kotlin`) * `double`(`Kotlin`)、`float`(`Dart`&`Kotlin`) * `string`、`bools`、`ints`、`doubles`、`strings`、`fvvvs`(`Dart`&`Kotlin`) 赋值只需要在有重写运算符功能的语言上直接赋值即可,没有则需要手动调用内部的值进行赋值 序列化/反序列化 [#序列化反序列化] *序列化* 时会去除所有链接 *序列化*/*反序列化* 在各语言上均可使用,但差异较大 `C++` 可以直接通过 `to`/`from` 传入值引用,或在类中定义 `fvv_values` 函数以绑定名称与引用 <> `parse` 支持传入第二个参数以相当于执行 `parse`+\`to `Dart` 需要定义继承 `FVVStruct` 的类,在 `fvvValues` 函数中绑定名称并设置 `getter`/`setter`, 当需要处理嵌套的 `FVVStruct` 列表时,需要传入 `factory` 以创建该 `FVVStruct` <> 调用只需要通过 `to`/`from` 传入即可 <> `parseString` 支持传入第二个参数以相当于执行 `parseString`+`to` `Go` 可在 `struct` 使用 `` `fvv:"值名称"` ``,支持 `omitempty` <> 调用只需要通过 `Unmarshal`/`Marshal` 传入即可 <> `ParseString` 支持传入多个参数以相当于执行 `parseString`+`Unmarshal` `Kotlin` 基于 `serialization` 实现 <> `parse` 支持传入类型以相当于执行 `parse`+`Unmarshal` # [FWW](https://fvvlang.sbs/widgets) > FWW (FVV Widgets) 的基本用法 FWW 是基于 FVV 语法为图形化所立的标准, 是在**不变更任何 FVV 原有功能**的情况下,为编写界面而仅在**语法规则**上所立的标准 尽管 FVV 本身并不是独立语言,但是 FWW 通过其他语言解析 FWW 的配置文件从而生成界面 因为只是在语法规则上所立的标准,所以大部分都只是在值名称等地方有所规定, 如果已经了解如何编写 FVV,那么编写 FWW 也是十分容易的 基本规则 [#基本规则] FWW 的所有页面都是基于 `组列表` 实现的(类似于 `Column`),例如如下示例 ```plaintext title="示例" Page = <这是页面标题呢> [ { Txt = "这是第一个文本呢" } { Txt = "这是第二个文本呢" } ] ``` 所有页面都是基于如上的 `组列表` 实现的,`组列表` 可以读取外部 FVV 的定义,因此在实现多语言等方面也是比较方便的 具体的规范如下: * `组列表` 本身的 `描述` 是其 `页面标题` * `组列表` 内的每个 `组` 的 `描述` 是其 `组件类型` * `组列表` 内的每个 `组` 的具体定义则因具体 `组件类型` 而异 * `页面标题` 应当写在 `=` 与 `[]` 之间 * `组件类型` 应当写在 `{}` 前面(但应与 `{` 在同一行,否则语法错误) # [FVV 插件](https://fvvlang.sbs/extensions) > FVV 语言的各个编译器插件 FVV 拓展仓库 中有 [*MT 管理器*](https://mt2.cn) 的拓展 [*VS Code*](https://code.visualstudio.com) 的拓展则可以直接在应用商店中搜索到 # [FVV 文件后缀](https://fvvlang.sbs/formats) > FVV 语言的各个文件后缀 FVV 目前有如下文件后缀: 首选, typeDescription: '建议使用', required: true }, '.fw': { type: 主要, typeDescription: '可选使用', required: true }, '.fyl': { type: 主要, typeDescription: '可选使用', required: true }, '.rpp': { type: 历史遗留, typeDescription: '不建议使用', required: true }, '.rxx': { type: 历史遗留, typeDescription: '不建议使用', required: true }, '.r++': { type: 历史遗留, typeDescription: '不建议使用', required: true }, '.rr': { type: 历史遗留, typeDescription: '不建议使用', required: true } }} /> *** FWW 目前有如下文件后缀: 首选, typeDescription: '建议使用', required: true }, '.fvvvv': { type: 次要, typeDescription: '不建议使用', required: true } }} /> *** FWS 目前有如下文件后缀: 首选, typeDescription: '建议使用', required: true }, '.fvvs': { type: 次要, typeDescription: '不建议使用', required: true } }} /> # [FVV 1](https://fvvlang.sbs/v1) > FVV 1 的语法标准 FVV 1 指的不是最初的 FVV 语法标准,而是移除掉复杂语法后的 “***纯净版本***” <> 自 FVV 3 起的 FVV 大版本,均会以 *FVV 1 解析器* + *对应的大版本解析器* 组成,依此实现复杂内容的插件化 <> FVV 1 的语法标准可能会因为大版本更新需要而进行变化 语法标准 [#语法标准] ```plaintext title="FVV 1 语法标准" <基本类型> BoolVal = true <布尔值,解析时会忽略大小写> IntVal1 = 1 <十进制整数(整数、浮点数均不支持正号)> IntVal2 = 1000'0000 <有引号的十进制整数> IntVal3 = 0x5 <十六进制整数> IntVal4 = 0o2 <八进制整数,也可写作“02”> IntVal5 = 0b0 <二进制整数> FloatVal = -1.0 <浮点数> StrVal = "字符串" <字符串> <列表> StrList = ["1", "2", "3"] <字符串列表> <不再多写...> <组> Group = { SubVal = "子值" <子值> SubGroup = { SubVal = "子值" <子组子值> } } <普通组> GroupList = [ { SubVal = "组一子值" } <组一> { SubVal = "组二子值" } <组二> ] <组列表> <赋值> Value1 = StrVal <常规赋值> Value2 = "字符串" <这里把注释给赋值了,注释的赋值只会生效于目标是字符串类型的情况,不会隐式转换> List1 = [Value1, Value2] <常规列表赋值> List2 = [List1] <列表赋值列表,会自动展开> Group1.SubVal1 = Value1 <通过点连接组名赋值到子值> Group2.SubVal = Group1.SubVal1 <用同样的方式赋值到另一个子值> Group1 = { SubGroup = { SubVal2 = SubVal1 <展开赋值> } } <赋值遵守就近原则,并且赋值的字符串将按原样存储> <仅有常规赋值会保留原样,列表或注释中的赋值不会保留> <拼接与类型提升> StrVal1 = "字符串" + "字符串" <常规拼接> StrVal2 = StrVal1 + true + 1 + 1.0 <所有非字符串基本类型进行拼接都会提升为字符串> <解析时不会记录拼接的原样,所有拼接中的赋值都会丢失> StrList1 = ["字符串" + "字符串"] <拼接在列表中也可用> StrList2 = [StrVal1, StrList1, true, 1, 1.0] <在列表中也会提升类型为优先级最高的字符串> FloatList1 = [true, 1, 1.0] <字符串之下是浮点数> IntList1 = [true, 1] <浮点数之下是整数> BoolList1 = [true] <整数之下只有布尔值了> <组不支持拼接,列表也不支持拼接(因为已经支持在列表中展开其他列表了)> <并且列表仅允许单一类型,组与基本类型不可以混合存储> <分隔符> Val1 = 1 <用换行省略了分号> Val2 = 2 <显式的分号声明>; ValList1 = [ 1 2 3 ] <用换行省略了逗号> ValList2 = [ 1, 2, 3, ] <显式的逗号声明(末尾是否有逗号不影响解析)> <其他字符> Key1: "Value" <冒号与等号等价> Key2:"Value" <全角冒号同样可用> IntVal = 1000’0000 <有全角引号的整数> Say1 = "\"Hello\" “Hello”" <常规字符串> Say2 = “"Hello" “Hello\”” <用全角引号包裹的字符串> <可以看到转译也会因此改变> Say3 = ` "Hello" “Hello” ` <用反引号包裹的原始字符串,解析时会去掉开头与结尾的空白,并且去掉最小缩进> Grp ={Lst1 =[Key1, Key2]}<全角括号(用花括号省略了应该写在“Lst1”定义后的分号)> Lst2 = ["Value",];<全角分隔符> <除引号外,其他全角字符均可与半角字符混用> ``` # [FVV 2](https://fvvlang.sbs/v2) > FVV 1 至 2 的巨大改进 FVV 作为已经投入生产使用超过一年的语言,在它诞生的第二年,第二个大版本已经完全完成 FVV 2 是 FVV 1 的完全重构版本,在原有语法不变的基础上新增了更多语法,也完善了很多机制 具体变动内容如下: 代码优化 [#代码优化] `C++`/`Dart`/`Go`/`Kotlin` 版本全部完整实现 **解析**/**格式化** 以及 **序列化**/**反序列化** 由于 `Go` 的特性, `map` 是无序的,在格式化时会 `sort` 这里的格式化指的是把 FVV 对象转为字符串,并非直接将原始 FVV 文本重新格式化 解析由原本的扁平循环判断改为词法分析与语法分析分离的递归下降解析器,代码质量大幅提高 FVV 1 在语法出错时只会硬解析,不会报错, FVV 2 引入了 *异常机制*,并且会有具体的行号提示,各语言实现如下: * `C++`: 解析函数会返回字符串,若非空,则为报错内容 * `Dart`: 在解析出错时会直接 `throw` * `Go`: 在解析出错时会返回非 `nil` 的 `error` * `Kotlin`: 在解析出错时会直接 `throw` FVV 2 在代码使用上新增了 *序列化*/*反序列化* (`from`/`to` 或 `Marshal`/`Unmarshal`),使用更加方便 对于 *序列化*/*反序列化*,各语言实现如下: * `C++`: 直接的基本值引用传入或在类中定义绑定函数以传入类 * `Dart`: 继承基本类以定义绑定函数 * `Go`: 基于 `tag` 的反射 * `Kotlin`: 基于 `kotlinx.serialization` 的直接转换 语法优化 [#语法优化] 底层规则 [#底层规则] FVV 2 在解析时能够正常处理 `\r`/`\n`/`\r\n` 文本,不会再强制转为 `\n` 并且 FVV 2 能够正常处理 *转义*,支持 `\b`/`\f`/`\n`/`\r`/`\t`/`\\` 的转义 在 *文本* / *注释* 情况下会依情况额外支持 `\"` / `\”` 或 `\>` 在 FVV 2 中,整数均以 `int64` 存储,而非原本的 `int` 值解析 [#值解析] FVV 2 还支持了更完善的数字机制,支持 *二进制*/*八进制*/*十六进制* 整数解析, 支持在十进制数字中添加 `'`/`’` 作为分隔符 *八进制* 支持包括 `0` / `0o` 开头 在解析布尔值时,可以忽略大小写解析 组闭合的花括号可以用来省略分隔符了(`{ k = v; }` => `{ k = v }`) 字符串与拼接 [#字符串与拼接] 早日列在 *TODO* 的字符串拼接也支持了,可通过基本值或值名称以 `+` 拼接 仅支持基本值拼接!列表由于有赋值展平机制,故不支持直接拼接 非字符串基本值在拼接时为自动转为字符串(而非数学运算), 在列表中也有这样的类似规则,一个混合类型的基本类型列表会类型提升为最高优先级的类型,优先级是这样的: > `字符串` > `浮点数` > `整数` > `布尔值` 不支持基本值与组列表混合 类型提升时是直接提升为最高类型(`布尔值`(`true`) => `字符串`(`"true"`)), **而非**逐级提升(`布尔值`(`true`) => `整数`(`1`) => `浮点数`(`1.0`) => `字符串`(`"true"`)) 拼接时会以原始字符串拼接,而不会解析后再拼接 (原始值 `1'0000`/`TRUE` 拼接为字符串时仍为 `1'0000`/`TRUE`,而非 `10000`/`true`) <> 但列表混合类型由于是解析完成后统一类型提升,是以解析后的值提升的 对于字符串本身,支持了 *原始多行字符串*(使用 ` `` ` 包裹), 解析时不会触发任何转义,并且解析后自动去除最小缩进与头尾空白 字符 [#字符] FVV 2 支持使用 `:` 而非 `=` 来定义值(更 *YAML* 一点?) 全角字符 [#全角字符] 部分常见的全角字符也可在 FVV 2 使用,目前支持如下: * `{}` 与 `{}` * `[]` 与 `[]` * `""` 与 `“”` * `'` 与 `’` * `:` 与 `:` * `;` 与 `;` * `,` 与 `,` 除 `""`/`“”` 外,其他均支持混用(因为当引号混用时,转义将更复杂) # [FVV 3](https://fvvlang.sbs/v3) > FVV 3 FVV 3 目前仍在开发中,将移除部分语法,新增部分语法,实现插件化、脚本化等内容