基本用法
FVV 全称 Fresh View for Viewer,是一个较为简单易上手的语言,名字显而易见,是个 废物 清新 的文本格式,那么有多清新呢,请看示例
Val1 = "FVV\"" <字符串转义与注释转义\>>
Val2 = true <布尔值>
Val3 = 2006 <整数>
Val4 = 2006.06 <浮点数>
Val5 = ["FVV", "FW"] <字符串组>
Val6 = [2, 0, 0, 6,] <整数组(这个“,”是故意多打的)>
Val7 = [Val5, Val6] <字符串组(值组赋值默认跟随第一个值的类型)>
Val8 = <FVV值组嵌套> [
{
Val9 = "FVV"
Val10 = <FVV值组> [
<FVV值组子值> {
Val11 = "FW"
}
{
Val12 = "FW"
}
]
}
]
Group = {
Sub = {
a = Val3 <跨组赋值>
} <子组>
c = Sub.a <跨组赋值>
} <组>
Group.Sub.b = a <同组赋值,支持用“.”连接组名>
Group.Sub.b = Group.Sub.a <同组赋值,用“.”表示了完整名称>
Desc = "这是描述"
Value = "这是值" <Desc>
<Value的描述是通过赋值Desc实现的,这样Value的描述将会是“这是描述”>
显而易见,支持字符串
、布尔值
、整数
、浮点数
以及它们的组
的定义,还有 FVV 本身的组
,还支持值组
以及赋值
FVV 本身在实现方面有部分缺陷,对转义的判断仅为当当前符号是特定符号时(例如 "
和 >
),
判断其上一个字符是否为 \
,并没有很完善的转义判断,故仅支持转义 "
和 >
(即 "\""
和 <\>>
),
不支持 \n
等转义,直接使用 \
时不需要 \\
,直接 \
即可(因此字符串或注释的末尾不可以是 \
)
值的命名也是没有什么忌口的,但需要注意的是,
如果一个值被命名为纯数字(仅 整数
,因为 浮点数
会被分割为双层组),
那么这个值将无法用于赋值,因为没法判断到底给的是值还是值的名称
同样的,还需要注意正常命名不要带 .
啊喂,会被认为是多层组的定义的
值的定义使用 ;
或 换行
进行,
所有的值都可以置于根路径的 {}
里面(放不放都没区别,只不过加一个 {}
看起来类 JSON 一点),
块注释使用 <>
,没有行注释
换行
可以省略掉以下字符:
;
: 当定义值时有换行
则无需在行尾添加;
,
: 当定义组时有换行
则无需在两个值之间添加,
注释是 FVV 一个比较特色的功能,它可以放到任何地方,请看示例:
<注释>{<注释>a<注释>=<注释>1<注释>;<注释>}<注释>
可以非常直接地看出来,注释完全是想怎么写就怎么写,但是它的作用不止于此
介于 =
与 ;
(或 换行
)之间的最后一个注释,将会被认定为是该值的 描述
,在解析时将会被留存为该值的附属值
因为 换行
可以替代 ;
,描述
又是和定义一样通过 换行
/;
来解析的,
所以 描述
不可以写到定义的下一行!
为了让 FWW 有更好的开发体验,FVV 值组
及其子值的 描述
是放在前面的
描述
是支持赋值的,也就是说任意字符串都有可能赋值到 描述
上,只要 描述
与该字符串的值名称相同
所以在实践中,建议在编写 描述
时,在开头加一个 -
,从而实现类似于 <- 这是注释>
的效果,
如果字体支持,<-
会变成一个好看的箭头,这样既不会与赋值机制有冲突,还可以提升观感
代码层使用方法
仅支持 UTF-8
文本(或 UTF-8 with BOM
),由于各种语言实现的差异,不确保所有语言均可正常解析所有文本
解析时会把所有 \r\n
替换为 \n
,把所有 \r
替换为 \n
(即强制将文本转换为 LF
文本)
引入依赖
- C++
- Dart
- Go
- Kotlin
在项目中添加文件后,直接 #include
即可:
#include "fvv.hh"
先添加依赖:
- Dart
- Flutter
dart pub add fvv
flutter pub add fvv
然后直接 import
即可:
import 'package:fvv/fvv.dart';
在 go.mod
中通过 replace
指向本地路径,并通过 require
添加依赖
replace app.niggergo.work/fvv => path/fvv/go
require app.niggergo.work/fvv v0.0.0
通过命令行添加依赖:
go get -u app.niggergo.work/fvv@latest
远程依赖仅可在 Go 1.25
+ 可用!
直接 import
即可:
import "app.niggergo.work/fvv"
先确保 repositories
内有 https://jitpack.io
:
- Groovy
- Kotlin
dependencyResolutionManagement {
repositories {
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositories {
maven("https://jitpack.io")
}
}
然后添加依赖:
dependencies {
implementation("dev.oom-wg.FVV:FVV:-SNAPSHOT")
}
再直接 import
即可:
import ren.shiror.fvv.FVVV
基本功能
首先当然是解析
由于 Go
的语言特性,其函数名称不得不大写
由于 Dart
与 Go
不支持函数重载,为了保证长期维护的可能性,额外添加了 String
后缀
- C++
- Dart
- Go
- Kotlin
FVV::FVVV fvv;
fvv.parse(txt);
final fvv = FVVV()..parseString(txt);
fvv := NewFVVV()
fvv.ParseString(txt)
val fvv = FVVV().apply { parse(txt) }
然后是格式化成字符串,当传入以下内容时,会有不同的格式化效果:
Common
: 默认Min
: 格式化后的文本将去掉任何符号间的空格、缩进、换行(替换成;
)、描述BigList
: 格式化后的文本中的任何组都会被展开为一行一值NoDesc
: 格式化后的文本不再有任何描述
各个语言的格式化选项位置如下:
C++
:FVV::FormatOpt
Dart
:FVVFormatOpt
Go
: 以FmtOpt
开头的值,调用时需要通过PrintOpt
传入Kotlin
:FVVV.Companion.FormatOpt
不同的语言在命名上略有不同,但实际行为一致
FVV 值组
的 描述
在 FWW 中是页面标题,在 Min
与 NoDesc
中不会被去除
其内部的 FVV 值
的 描述
则是其组件类型,同样也不会被去除
一切去除描述的行为均仅为在输出的字符串中去除,不会影响到代码中的描述
- C++
- Dart
- Go
- Kotlin
string fvv_str = fvv.print();
final fvvStr = fvv.print();
fvv_str := fvv.Print(PrintOpt{})
val fvvStr = fvv.print()
然后是对值的基本判断
由于 Go
的语法可用性太低了,便又封装了 SubIsEmpty
和 SubIsNotEmpty
来判断子项是否为空,
但使用前最好还是先判断一下值本身是否为 nil
由于写 Kotlin
时语法基本上照抄 Dart
,所以 isEmpty
和 isNotEmpty
是通过 getter 获取的
- C++
- Dart
- Go
- Kotlin
bool is_empty = fvv.isEmpty() && !fvv.isNotEmpty(); // 判断是否为空
bool is_type = fvv.isType<T>(); // 判断类型
final isEmpty = fvv.isEmpty && !fvv.isNotEmpty; // 判断是否为空
final isType = fvv.isType<T>(); // 判断类型
is_empty := fvv.IsEmpty() && !fvv.SubIsNotEmpty() // 判断是否为空
_, is_type := fvv.Value.(T) // 判断类型
val isEmpty = fvv.isEmpty && !fvv.isNotEmpty // 判断是否为空
val isType = fvv.isType<T>() // 判断类型
然后是读取值
由于 Go
的语法实现这些有点麻烦,所以请自行使用 .(T)
来获取值吧
由于 Dart
机制,常规获取 List
类型得到的是拷贝,有 Ref
后缀的函数获取到的才是引用
由于 JVM
机制,在 Kotlin
获取 List
类型的方法有点抽象...,在 Kotlin
获取到的是拷贝
- C++
- Dart
- Go
- Kotlin
auto value = fvv.as<T>(); // 指定类型获取
string str = fvv.asString(); // 获取字符串
vector<string> strs = fvv.asStrings(); // 获取字符串组
// 其他依此类推,不再示范
final value = fvv.as<T>(); // 指定类型获取
final str = fvv.asString(); // 获取字符串
final strs = fvv.asStrings(); // 获取字符串组
// 其他依此类推,不再示范
value, ok := fvv.Value.(T) // 指定类型获取,通过返回的布尔值来判断是否获取成功
val value = fvv.as<T>() // 指定类型获取
val str = fvv.asString() // 常规获取字符串
val str = fvv.string // getter获取字符串
val strs = fvv.asStrings() // 常规获取字符串组
val strs = fvv.strings // getter获取字符串组
// 其他依此类推,不再示范
由于 Dart
的基本类型名称会冲突,所以没在它上面实现 getter 获取...
赋值只需要在有重写运算符功能的语言上直接赋值即可,没有则需要手动调用内部的值进行赋值