/
主要包:
core.function
在由语法解析 阶段生成的节点中,类型为 FunctionCallNode(context, name, arguments) 的节点代表了函数调用 。
虽然所有其他节点在创建之后就再也不会改变,但这是唯一一种可变节点,这意味着它的内部数据预期会在 AST 完全生成之后发生改变。它的变更会影响其子节点——子节点最初是一个空的集合,随后会由一个名为*函数调用展开器(function call expander)*的组件来填充。
在讲解展开本身之前,我们需要先理解函数之间是如何交换数据的。Quarkdown 的函数,无论显式还是隐式,总是返回一个 Value,即一个经过类型检查的对象包装器。
在 Quarkdown 中,并非所有类型都能被返回,也并非所有类型都能用作参数。因此,函数的参数应当是 InputValue 类型,并返回一个 OutputValue。完整的类型集合如下图所示(Venn-UML 图):
每当某个函数返回一个 OutputValue 时,它都必须被转换为某种可以渲染到屏幕上的 Node。例如,StringValue 会变成文本,OrderedCollectionValue 会变成有序列表,BooleanValue 会变成复选框,DictionaryValue 会变成表格,依此类推。这一操作由一个*值-节点映射器(value-node mapper)*来处理。
对于解析器入队的每一个函数调用节点,系统会在已加载的库中查找对应的函数1,建立参数与形参之间的绑定2,然后执行该函数以获取其输出。在从映射器取回输出节点之后,展开器就可以将它推入该次函数调用的子节点中,为下一阶段做好准备。
1用户自定义的函数也会被动态地存储到一个易失性库中。而原生库(例如stdlib)则是通过反射来加载其函数的。2Quarkdown 是弱类型 的,而原生库是用静态类型语言(Kotlin)编写的。当建立参数-形参绑定时,如果参数的类型是动态的,则会通过 ValueFactory 将其转换为形参的静态类型。如果转换失败,就会抛出一个错误。