语法设计,在不使用分号的情况下如何避免下面这种二义性?

句龙胤 发布于 2015/07/01 14:27
阅读 235
收藏 0

用一个例子来表示。

# dof是一个列表
# 第一个元素为一个函数,后面的为参数
# 需要调用第一个元素的函数

dof(0)(dof(1), dof(2), dof(3)) # 获取第一个元素,是函数,调用。

# 以上是最直接的编写方式。
# 但这样有二义性。

# 由于我没有使用分号,不像C/C++可以用;来明确一条语句。
# 所以上面的例子最终会被解析成两条语句:
# dof(0) 函数调用,获取dof的第一个元素
# (dof(1), dof(2), dof(3)) 一个临时列表
#


我暂时想出了两个解决方式:

1,在dof(0)后面加个.,变成dof(0).(dof(1), dof(2), dof(3)),这样就会被解析成一条语句,在把.()定义为调用函数的另一种形式。或者是.call,这个更明确一点:dof(0).call(dof(1), dof(2), dof(3))


2. 应用一个“不可分”概念,使用[]之类的开合符号作为不可分的单位,例如:[dof(0)(dof(1), dof(2), dof(3))]。这样解析器遇到[],会将里面的所有表达式视为一个整体的语句,于是不会有二义性了。

但是这两个办法,我认为都不妥,前者会导致混乱,后者会导致语法繁杂。

或许你认为用dof[0]就行了,不需要将获取元素的操作作为函数,但是这没有解决问题。因为总会遇到一个函数返回一个函数,然后需要调用这个函数的时候:

define getf lambda ()
    lambda (a b) a + b * 2 end
end

...

getf()(10 20)

...


所以,到底该如何修改设计,以消除这个二义性呢。

加载中
1
L5_Railgun
L5_Railgun

引用来自“艾米”的评论

这个应该是不存在二义性的,dof(0)本身就是个函数调用定义,(dof(1), dof(2), dof(3))本身就是一组参数。

你这个语法是没问题的,你应该在语义分析器里来确认,dof(0)是否返回的是一个 参数数量为3,且类型匹配的函数。

按照你的描述,

你所谓的list定义差不多应该是 id=(p1,p2...)

 函数调用差不多应该是 func(p1,p2)

你的语法推导器不可能会把dof(0)(dof(1), dof(2), dof(3)) 推导成两个语义的,所以根本不存在二义性的问题

引用来自“句龙胤”的评论

@艾米

主要是这样的,解析器可能在遇到dof(0)时直接就分析为一次函数调用,然后后面的(dof(1), dof(2), dof(3))就变成了另外的语句。然而这其实是一条语句,就是先调用dof(0)得到函数,然后传入(dof(1), ...)这些参数来调用这个函数。

可能是我描述上的问题,似乎应该用“非正确推导”来说明。

也就是说,本来dof(0)(dof(1), dof(2), dof(3))要做的是调用dof(0),这次调用返回的是函数,编码者也知道,所有后面带有(dof(1), dof(2), dof(3)),用来指明接下来还是一次函数调用。所以这应该被分析成一条语句。

但是在我目前的语法设计下,解析器可能会产生错误的分析!会将dof(0)(dof(1), dof(2), dof(3))分析成两条语句,dof(0)分析成一次函数调用,然后(dof(1), dof(2), dof(3))分析成一个临时列表,变成了两条语句。这样的话执行根本就错了,就只执行dof(0),因为这条语句只有dof(0),后面的(dof(1), dof(2), dof(3))就纯粹的变成了临时列表,而不是函数调用了。

根源是我不想加入;,因为我认为现代语言确实不需要;了。

而我对一次函数的定义为:目标(参数exp1, 参数exp2, ...)

所以只有这样的形式符合:foo(1, 2, 3)、mod(100*70, foo(5, 5 - 1, 4))。

大部分情况下没问题。

但就是在函数返回函数,并需要调用返回的函数时,出现了问题。

getf()(1, 2),就产生了两条语句:1.调用函数getf,2.一个临时列表(1, 2)。而不是调用函数getf,然后以(1, 2)为参数调用getf返回的函数。

=============

所以我不知道如何解决,比如这样:

define getlist lambda ()
    println ("getlist()")
    (1, 2, 3)
end

getlist() # result (1, 2, 3)



这个代码写的很明确,getlist函数显示一行文字:getlist(),然后返回一个列表(1 2 3),没问题。但是如果我为了解决dof那个问题,用手段比如把一次函数后面紧跟的列表并入到到这次函数调用来解决问题,那么上面这种正常的例子就不正常了。

而用类型推导来确认,依然行不通:

define getf lambda (v)
    case v
    0    lambda (a, b) a + b end
    1    "1"
    2    (2)
    end
end

x = ...
...
getf(x)
(1, 2)



比如这样的情况,getf有三种返回情况:1.参数为0,返回一个函数;2.参数为1,返回字符串"1";3.参数为2,返回有一个元素的列表(2)。

那么分析器中,该如何推导呢。

=========

所以我觉得,我需要修改对函数调用的定义,需要修改一些设计。

原本我还以为我的语法设计没什么大问题,直到我开始用这语法写一个工具时,才发现函数调用存在这么大一个问题。希望能早日得以解决。

我是这个意思,首先,你理应能有个机制能确定一行,无论是C family的 分号,还是python这种\r\n来分割的。

其次,你如果允许 (dof(1), dof(2), dof(3)) 这种临时列表,又不想让程序员明确指示一条表达式结束,而且还允许临时列表和函数调用紧接着写在一起的话,而且还是弱类型语言的话…… 坑爹啊

C语言里,允许

typedef int(*test_fn)(int a, int b);

test_fn ret_fn()
{
    return (test_fn)1;
}

int c = ret_fn()(1, 2);

这种, 但是你的设计里,问题在于你是弱类型语言,无法在编译时确定 getf()返回的是个function。

解决办法就是, list定义不允许和函数调用同义。直接用 [] 或者 {}定义list。

dof(0)(dof(1), dof(2), dof(3)) 为一行时,你应该认为dof(0)返回的是个函数,如果运行时得到的返回不是个函数,你应该报错

完毕



句龙胤
句龙胤
你说的对,()确实只应该表示函数参数表。用[]之类的来表示列表数据,就解决问题了。
0
L5_Railgun
L5_Railgun

这个应该是不存在二义性的,dof(0)本身就是个函数调用定义,(dof(1), dof(2), dof(3))本身就是一组参数。

你这个语法是没问题的,你应该在语义分析器里来确认,dof(0)是否返回的是一个 参数数量为3,且类型匹配的函数。

按照你的描述,

你所谓的list定义差不多应该是 id=(p1,p2...)

 函数调用差不多应该是 func(p1,p2)

你的语法推导器不可能会把dof(0)(dof(1), dof(2), dof(3)) 推导成两个语义的,所以根本不存在二义性的问题

0
句龙胤
句龙胤

引用来自“艾米”的评论

这个应该是不存在二义性的,dof(0)本身就是个函数调用定义,(dof(1), dof(2), dof(3))本身就是一组参数。

你这个语法是没问题的,你应该在语义分析器里来确认,dof(0)是否返回的是一个 参数数量为3,且类型匹配的函数。

按照你的描述,

你所谓的list定义差不多应该是 id=(p1,p2...)

 函数调用差不多应该是 func(p1,p2)

你的语法推导器不可能会把dof(0)(dof(1), dof(2), dof(3)) 推导成两个语义的,所以根本不存在二义性的问题

@艾米

主要是这样的,解析器可能在遇到dof(0)时直接就分析为一次函数调用,然后后面的(dof(1), dof(2), dof(3))就变成了另外的语句。然而这其实是一条语句,就是先调用dof(0)得到函数,然后传入(dof(1), ...)这些参数来调用这个函数。

可能是我描述上的问题,似乎应该用“非正确推导”来说明。

也就是说,本来dof(0)(dof(1), dof(2), dof(3))要做的是调用dof(0),这次调用返回的是函数,编码者也知道,所有后面带有(dof(1), dof(2), dof(3)),用来指明接下来还是一次函数调用。所以这应该被分析成一条语句。

但是在我目前的语法设计下,解析器可能会产生错误的分析!会将dof(0)(dof(1), dof(2), dof(3))分析成两条语句,dof(0)分析成一次函数调用,然后(dof(1), dof(2), dof(3))分析成一个临时列表,变成了两条语句。这样的话执行根本就错了,就只执行dof(0),因为这条语句只有dof(0),后面的(dof(1), dof(2), dof(3))就纯粹的变成了临时列表,而不是函数调用了。

根源是我不想加入;,因为我认为现代语言确实不需要;了。

而我对一次函数的定义为:目标(参数exp1, 参数exp2, ...)

所以只有这样的形式符合:foo(1, 2, 3)、mod(100*70, foo(5, 5 - 1, 4))。

大部分情况下没问题。

但就是在函数返回函数,并需要调用返回的函数时,出现了问题。

getf()(1, 2),就产生了两条语句:1.调用函数getf,2.一个临时列表(1, 2)。而不是调用函数getf,然后以(1, 2)为参数调用getf返回的函数。

=============

所以我不知道如何解决,比如这样:

define getlist lambda ()
    println ("getlist()")
    (1, 2, 3)
end

getlist() # result (1, 2, 3)



这个代码写的很明确,getlist函数显示一行文字:getlist(),然后返回一个列表(1 2 3),没问题。但是如果我为了解决dof那个问题,用手段比如把一次函数后面紧跟的列表并入到到这次函数调用来解决问题,那么上面这种正常的例子就不正常了。

而用类型推导来确认,依然行不通:

define getf lambda (v)
    case v
    0    lambda (a, b) a + b end
    1    "1"
    2    (2)
    end
end

x = ...
...
getf(x)
(1, 2)



比如这样的情况,getf有三种返回情况:1.参数为0,返回一个函数;2.参数为1,返回字符串"1";3.参数为2,返回有一个元素的列表(2)。

那么分析器中,该如何推导呢。

=========

所以我觉得,我需要修改对函数调用的定义,需要修改一些设计。

原本我还以为我的语法设计没什么大问题,直到我开始用这语法写一个工具时,才发现函数调用存在这么大一个问题。希望能早日得以解决。

0
万里谁能驯
万里谁能驯
不用分号可以用空白。

写成
dof(0)(dof(1), dof(2), dof(3))



这样就直接认为dof(0)是一个函数,后面是参数,不想歧义可以写在两行。

这两种情况看似相近,其实没什么关联,不如干脆将它们分开,区别对待。
返回顶部
顶部