凹语言中文语法探讨(一)

说明

凹语言的中文版初步设计和实现之后,暂时进入了瓶颈期,沉寂了一段时间。

最近凹发布了0.7版,语言特性基本完善,因此我觉得应当再次开启中文语法的探讨与实现了。

我有如下计划:

  1. 重新梳理一遍现有的语法
  • 通过论坛和其他途径与大家一起探讨每一个语法点是否有更好的形式
  • 根据讨论的结果修改整理语法
  • 修改现有前端
  1. 重新编写中文前端
  • 现在的中文前端是直接用英文版前端移植修改的,还存有大量英文解析的逻辑,应当清理
  • 由于中文语法的特殊性,解析流程需要重新设计

由于完整的语法涉及面太广,我打算把整套语法拆分成2个层面:

  1. 语句层面:与变量定义、表达式、控制流语句等
  2. 模块层面:如模块导入、函数定义、代码块、类型定义等

我们先从更细碎的语句层面开始讨论,再上升到模块层面。
每个层面分成不同的话题,方便大家探讨和投票。

每个话题,我会列出英文语法设计、现有中文语法设计,以及我能想到的其他备选方案。
欢迎大家提供自己的方案,并参与投票。

这个帖子先记录第一个话题:变量定义。

------- 我是分割线 ---------

变量定义

凹语言英文版的变量定义语法是这样的:


// 指定类型:

var x: int = 1

// 不指定类型

x := 1

方案1. 中文版的原始设计如下:


设x = 1

这个语法的问题在于,关键字和变量名x之间没有天然的空隙,因此在没有语法高亮的情况下,有信息粘连的问题,反而会增加阅读难度。

我们可以添加符号或空格来解决这个问题。

方案2. 添加分隔符号:


设:x = 1

方案3. 直接用空格:


设 x = 1

如果不考虑关键字的话,还可以有三个方案:

方案4. 直接复用英文版的:=符号:


x := 1

注意由于:=没有对应的全角符号,仍然需要手动添加空格。

如果使用全角冒号,会天然和后面的=分开,并不理想:


x:= 1

方案5. 利用其他的包裹性符号:


「x」= 1

打印(x)

这个可读性不错,但缺点是「」符号不好输入,需要输入法支持。

如果函数调用不采用()形式,而是采用的形式,倒是可以考虑用括号来表示变量定义:


(x)= 1

打印:x

方案6. 前缀符号:

例如@之类:


@x = 1

打印(x)

前缀符号可以和变量名看做一个整体,因此不需要与变量名分隔。

小结:

变量定义有6种方案:

  1. 中文关键字:设x = 1

  2. 中文关键字+分隔符:设:x = 1

  3. 中文关键字+空格:设 x = 1

  4. :=x := 1

  5. 包裹性符号「」「x」= 1()(x)= 1

  6. 前缀符号:@x = 1¥x = 1

注:关键字和符号本身也可以讨论换成其他的。比如我最早设计的中文关键字是


令x = 1

其实就是let的直译。

1 个赞

喜欢 打印:x 这种风格的函数调用,但是也喜欢 x := 1 这种风格的变量定义……

之前 对此语法点有过一些讨论。除了将反馈信息纳入考虑,不妨像 @Peefy 那样尽量列出所有相关语法点统盘考虑。比如还有 函数声明 里的 a: i32、增量运算 a += 2 等。

即在挑选具体语法方案之前,先将相关用例 (use case)列出,以避免顾此失彼。

1 个赞

关于冒号赋值的额外语义,即 a:=1a=1 的区别,以及var的语义,是否可能用更显式的中文语法表达?

另外,在查函数默认值时,看到 此答 提到:“ Go语言追求显式的表达,避免隐含”。不知有无出处?

我之前的设计就是显示的中文关键字“设”:

// 带类型声明
设a之数=5

// 类型推导
设a=5

这里都是关键字。是类型名称。

开发组有同学提出这样的语法太过紧凑,超过了阅读密度的阈值。

我考虑了一下,感觉确实如此,所以才提出了几种备选方案。

其中a := 1这种直接借用英文版语法的方案我认为也没有问题,因为它也符合“多利用符号来清晰地表达意思”的原则。要说缺点的话,那就可能是:=是中序表达式,和其他的语句在语序上不一致吧。

是的,函数参数的声明本质上和语句中的变量声明是一条路线上的,可以一起讨论,这一点我欠考虑了。

不过下一个话题马上就到了函数相关了,所以干脆在那里一起讨论吧。明天我发一下函数相关的帖子。

自增语句我暂时没有考虑,我认为如果要保留自增语句,那么最简单的办法还是沿用+=这种符号的形式。自增语句会放在另外一个话题“运算表达式”里讨论。

补充一下:

  1. 中文版的代码,变量名也是可以用中文的,不过也不排斥i,j,k这类单字母字符。这里用字母是为了凸显关键字的作用。

这是现在已实现的示例:

引于 “书”
设甲=5
书·曰:甲
  1. 关于全角半角下相似的符号,如’()‘、’,’和引号。考虑到括号这类字符在全角和半角下很难区分,就干脆把它们当做相同的字符了即‘()’和’()'都是括号的意思。未来会添加自动格式话的功能,将半角括号自动处理成统一的全角符号。
1 个赞

嗯嗯,广度优先是一种不错的方式,避免顾此失彼 :eyes:

先主要根据 贪吃蛇 例程列一下变量定义相关用例,请看看有无错漏:

  • 定义新变量,同时指定类型 var ca: *canvas.Canvas

  • 定义多个新变量,同时指定初值 var sin30, cos30 = math.Sin(angle), math.Cos(angle) 源自此例程

  • 定义新变量,同时指定类型和初值 【未找到实例】

  • 在结构中定义新变量,同时指定类型 scale :i32

  • 在结构中定义多个新变量,同时指定相同类型 x, y :i32

  • 在函数体中定义新变量,同时指定值 x := xyrange * (f64(i)/cells - 0.5) 源自此例程

  • 在函数体中定义多个新变量,同时指定值 ax, ay := corner(i+1, j)

  • 在函数定义中定义参数,同时指定相同类型 scale: i32

  • 在函数定义中定义多个参数,同时指定相同类型 w, h: i32

顺便列出常量定义(之后不可修改)相关用例:

  • 定义新常量 GridBody

  • 定义新常量,同时指定值 const DefaultColor = 0x00000000

  • 在常量体中定义新常量,同时指定值 GridNull: i8 = iota

一个问题:var sin30, cos30 后面为何是 = 而不是 := 呢?冒号是可选的吗?

这里很多例子都不符合最新提议(var 将只用于全局变量的声明)。如果按照最新提议,变量定义只有以下三种形式:

局部变量、结构体成员、参数:

x: i32 //0值初始化
y: i32 = 42  //非0值初始化

局部变量快捷定义(类型由右侧表达式推断,类似于 C++ 的 auto):

x := 13  //x类型为i32
y := genFloat()

全局变量:

var G1: i32  //0值初始化
var G2: f32 = 42.0

请问全局变量部分的例子是否缺少了冒号?

var G1 i32  //0值初始化
var G2 f32 = 42.0

另外,在上面看到一些情况下未用冒号,如 var sin30, cos30 = math.Sin(angle), math.Cos(angle),不知与带冒号的区别是?

我回头再整理一下用例,理想情况是用例分析与具体语法设计解耦。

已改。凑字凑字凑字

如果现在 var 仅用于全局变量的定义,改名为 global 等是否更符合语义?

这是之前有讨论过的方案,在正式版之前一切问题都是可以再讨论的

也不是不可以,凑字数

@wuxuan global 提案已经通过,详情请看 8号提案

1 个赞