FlowG语言 是专为 FlowG框架 设计的形象化流程描述语言,尽管如此,FlowG语言仍具一定的普适性,适合绝大多数面向流程编程的框架。本文主讲 FlowG语言的设计及定义。
目录
- 1. 语言能表达的内容
- 2. 元素标识符的意义
- 3. FlowG语言的示例
- 4. FlowG语言的语法定义
- 5. 字符
- 6. 字母和数字
- 7. FlowG语言的词法定义
- 8. FlowG语言的语义定义
内容
1. 语言能表达的内容
- FFlow:包装元素
- FElement:节点 和 管道
- FNode:节点
- 端口:输入端口 和 输出端口
- FPipe:管道
- ID:元素的唯一标识
- 选项:键值对集合
- 列表:值集合
2. 元素标识符的意义
-
方案1:标识符作为实例的引用名字;具体规则如下:
- 标识符作为实例的引用名字 name
- 对 name 的查找应该是优先从 实例缓存中查找 实例id 与 name 相同的实例;
- 如果没有找到,应再找 类型名字 与 name 相同的类型,并用类型创建一个实例,并存放到 name 中;
- 如果还没找到,则报错;
-
方案2:标识符作为实例的唯一ID,而不是作为名字,理由:
- 作为名字 就会引入多更多概念,如果想查找某个实例,则可以通过 ID 查找,又可以通过 FlowG语言中的名字查找,这将使问题复杂化
- 名字作为ID 也可以节省在FlowG语言中创建实例的参数
- 名字作为ID,可以禁止 多个名字对应同一个实例的情况,这种情况虽然会使表达更灵活,但却大大降低了 FlowG 语言本身的直观性,因为人们不能从FlowG语言上直观地了解流程的图形化结构
最终决定,FlowG语言
采用 方案2
3. FlowG语言的示例
下面是 FlowG语言的示例:
node1 = NodeA[] //创建 NodeA 类型的节点 node1
node2 = NodeB[key1=2,key2=true,key3="这是字符串"] //创建 NodeB 类型的节点 node2,并给 NodeB 类传递选项(参数)
pipe1 = PipeA[] //创建 PipeA 类型的管道 pipe1
/*
定义流程:node1的默认输出端口 连接到 管道 pipe1,管道 pipe1 连接到 node2 的输入端口 A,node2的输出端口 A 连接到 NodeC 类型的匿名节点
*/
node1 -> pipe1 -> A<:node2:>A -> NodeC[] -> node3
/*
定义包装元素 packNode,其包装流程是:节点 nodeA 的默认输出端口 连接到 nodeB 的默认输入端口,nodeB 的默认输出端口连接到管道 pipeA,pipeA 又连接到 节点 nodeC 的默认输入端口
*/
flow packNode {
nodeA->nodeB->pipeA->nodeC
}
/*
定义流程:node2的输出端口 B 连接到管道 pipe2,管道 pipe2 连接到 包装元素 packNode ,包装元素 packNode 又分别连接到 节点 node3 的输入端口 B 和 节点 node4 的默认输入端口,节点 node3 的默认输出端口 和 节点 node4 的输出端口 A 都连接到了 由类型 NodeX 创建的 id 为 'node5' 的默认输入端口
*/
node2:>B -> pipe2 -> packNode -> {B<:node3,node4:>A} -> NodeX[id='node5']
这个示例描述的流程如下:

其中 n1
表示 node1
,p1
表示 pipe1
。
4. FlowG语言的语法定义
为了规范、表达清晰 和 将来更方便地开发语法解析器,FlowG语言采用 EBNF范式 描述 FlowG语言的文法。
关于 EBNF 的详细信息,请看语法格式描述规范BNF、EBNF、ABNF
语句列表 = [ 语句 [ ';' ] 语句列表 ] ;(*语句以分号结束*)
语句 = 元素定义 | 关系语句 | 流程定义
流程定义 = 'flow' [名字] '{' 语句列表 '}' ;(*定义流程;Name 是流程的名字*)
元素定义 = 名字 [ (赋值符 | 默认值赋值符) 元素创建 ] ;(*有两种赋值方式*)
赋值符 = '=' ;(*将右值直接赋给左值*)
默认值赋值符 = '?=' ;(*只有当 左值 不存在时,才将右值接赋给左值*)
元素创建 = 类型 , 哈唏表 ;(*通过在类型后面加 [key=value] 的方式来调用类型的构建函数并给其传参数 来创建类型的实例 *)
哈唏表 = '[' [ 键值对列表 ] ']' ;(*带有一系列键值对的数据结构*)
键值对列表 = 键 '=' 值 [ ',' 键值对列表 ]
键 = 标识符
值 = 名字 | 字符串 | 数字 | 布尔 | 哈唏表
关系语句 = [ 元素|元素列表 ] 关系右值 {关系右值} ;(*关系语句会将关系分别映射到元素 或 元素列表中的每一个元素上*)
关系右值 = '->' (元素|元素列表) ;(*关系会分别映射到元素 或 元素列表中的每一个元素上*)
元素 = { 输入端口 } 名字 { 输出端口 } ;(*元素可以指定输入端口 和 输出端口,输入端口写在节点的前端,输出端口写在节点的后面*)
输入端口 = 端口 '<:' ;(*输入端口和节点的分隔符是 <: *)
输出端口 = ':>' 端口 ;(*节点和输出端口的分隔符是 :> *)
端口 = 名字
元素列表 = ’{' [元素序列] '}' ;(*元素列表表示一组元素,元素与元素之间用 逗号 分隔*)
元素序列 = 元素 {',' 元素 } ;(*元素与元素之间用 逗号 分隔*)
类型 = 名字
名字 = 标识符
注释 = 行注释 | 注释块
行注释 = '//' {Unicode字符} ;(*单行注释*)
注释块 = '/*' { Unicode字符|换行符 } '*/' ;(*多行注释*)
标识符 = 字母 { 字母 | 数字 }
字母 = Unicode字母 | "_"
数字 = "0" … "9"
Unicode字母 = (* 类型为“字母”的Unicode解码点 *)
字符串 = 双引号字符串 | 单引号字符串
双引号字符串 = '"'...'"'
单引号字符串 = '''...'''
数字 = Unicode数字...
布尔 = 'true' | 'false'
5. 字符
具体的Unicode字符类别由以下术语表示:
换行符 = (* 即Unicode码点U+000A *)
Unicode字符 = (* 除换行符之外的任意Unicode编码点 *)
Unicode字母 = (* 类型为“字母”的Unicode解码点 *)
Unicode数字 = (* 类型为“十二进制数字”的Unicode解码点 *)
空白符 = 空格U+0020 | 跨制表符U+0009 | 回车符U+000D | 换行符U+000A
6. 字母和数字
下划线字符_(U + 005F)被视为一个字母。
字母 = Unicode字母 | "_"
数字 = "0" … "9"
7. FlowG语言的词法定义
7.1. 注释
注释有两种形式:
- 行注释以
//
开始,至行尾结束。一条行注释注意到一个换行符。 - 注释块以
/*
开始,至*/
结束。块注释在包含多行时视为一个换行符,否则视为一个空格。
注释不可嵌入。
注释 = 行注释 | 注释块
行注释 = '//' {Unicode字符}
注释块 = '/*' { Unicode字符|换行符 } '*/'
7.2. 标记
标记是构成语言的词汇。它有多种类型:标识符、关键字、运算符 与 分隔符 以及 字面量。
空白符 包括 空格 U+0020
、跨制表符 U+0009
、回车符 U+000D
和 换行符 U+000A
,除非用它们来分隔会结合成一体的标记,否则将被忽略。从而,换行符 或 EOF(文件结束符)会触发分号的插入。把输入分解为标记时,可形成有效标记的最大化字符序列将作为下一个标记。
标记 = 标识符 | 关键字 | 运算符 | 分隔符 | 字面量
7.3. 分号
分号 ;
作为一条语句的终结符,但在没有歧义的情况下可省略;
7.4. 标识符
标识符命名程序实体。标识符是一个或多个字母和数字的序列。标识符中的第一个字符必须是字母。
标识符 = 字母 { 字母 | 数字 }
示例:
g
_g3
GuoBinYong
g_by
αβ
7.5. 关键字
以下关键字是保留关键字,不能用作标识符。
关键字 = 'flow'
7.6. 运算符
运算符 = '{' | '}' | '->' | '[' | ']' | ':' | '=' '?='
7.7. 字符串
由 单引号 '...'
或 双引号 "..."
包围的标识符称为字符串
字符串 = 双引号字符串 | 单引号字符串
双引号字符串 = '"'...'"'
单引号字符串 = '''...'''
7.8. 数字
以 Unicode数字 开头的标识符 都称为数字;
数字 = Unicode数字...
7.9. 布尔
布尔 = 'true' | 'false'
8. FlowG语言的语义定义
词法定义、语法定义都有了,为了接下来的语义分析并生成目标语言,还需要定义FlowG语言的属性文法,但由于时间关系,日后再来完善这块。
网友评论