开发 julia-intellij 过程找 Parser 的 Bug 发现的一些稍微奇怪或是大家没注意到的语法……


#1

(不定期更新)

正则表达式

r"RegExp"m
r"RegExp"ms
r"RegExp"sm 
#……

后来发现这里其实是有五个选项的。见官方文档 r_str 的部分 https://docs.julialang.org/en/stable/base/strings/#Base.@r_str

构造一个正则表达式,例如 r"^[a-z]*$" ,没有插值和反转义(除了 " 仍然必须转义的引号)。正则表达式还接受在结束引用后列出的一个或多个标志来更改其行为:

  • i 启用不区分大小写的匹配
  • m 将标记 ^$ 标记视为匹配单个行的开始和结束,而不是整个字符串。
  • s 允许 . 修饰符匹配换行符。
  • x 启用“注释模式”:启用空格,除非转义为 \ ,并被 # 视为开始注释。
  • a 禁用 UCP 模式(Unicode character properties)(启用ASCII模式)。在默认情况下匹配 \B\b\D\d\S\s\W\w 基于Unicode字符属性。使用此选项,这些序列仅匹配ASCII字符。

#2

数组

a = Int[1,2,3]
b = String["asd","a"]

会产生什么问题呢?Roger 说 Julia 是没有 Lexer 的,对于单纯的词法分析,如果有

Int[1] # 一个Int类型的,只有一个值为1的元素的数组
int[1] # 名为 int 的数组取1的下标

同理还有 这样的……

a[1,2]

表示第一行第二个元素


julia> r=[1 2 3;4 5 6]
2×3 Array{Int64,2}:
 1  2  3
 4  5  6

julia> r[1,2]
2

julia> Int[1,2]
2-element Array{Int64,1}:
 1
 2

就不能简单分辨到底是指的 某个类型的数组 还是 某个变量的数组下标。必须根据上下文来判断。(Julia以及绝大部分的语言都没有什么所谓 类型强制要求大写 的……)

另外还有冒号
冒号有表示 quote 语句,在数组中又可以表示范围

于是在解析语法过程这就很糟心了……由于运算符优先级的问题

a = [:A :B]

这个 a 被当作了 [: (A:B)]

总之 Array 的语法解析比较复杂,咱又得 fix bug

支持的语法

还有这个

1[1]

好神奇……

然后经过这些天的努力……Fix了好多Array,我怀疑自己要精通 Julia Array 的构造了……
可以作为被赋值的表达式:

[1]
[1,2,3]
[1;2;3]

[[1,2,3];[4,5,6]]
6-element Array{Int64,1}:
 1
 2
 3
 4
 5
 6

二维数组

# 空格分割表示创建二维数组……
[1 2 3]

# 错误用法
# [1,5; 3,3 ; 4,4;]
# 应为:
julia> [1 5; 3 3 ; 4 4;]
3×2 Array{Int64,2}:
 1  5
 3  3
 4  4

# 错误用法2
# [1,3;4]
# [1,3;4;]

范围符号 (冒号)

[1:5;]  # 表示[1,2,3,4,5]

julia> [1:5]
1-element Array{UnitRange{Int64},1}:  
    1:5

[1 2; 3 4]
2×2 Array{Int64,2}:
 1  2
 3  4

有关 end 下标

由于julia索引是1开始的,所以 end 其实也就等于数组的长度的值

a=[1, 2, 3,4]
a[1:end]
a[end:1]
a[end-1]
a[Int(0.5end)]

(最后一个谁真要这么写迟早被打死)


#3

global 语法

为了保证兼容性,以下语法在 julia-intellij 插件中 均不会报错,但是我们之后可能会根据当前使用的 Julia 目标版本在对应语句上添加 error 的 “报错” (不影响整个文件的解析。如果是一般的语法报错会破坏整个 Julia 文件的解析过程)

global 赋值语句一

global a,b=1,2

v0.6:
D655F9C083BBFD3841D79D88F1640B39

v1.0:
07D3A1C180E7EA459FBA2332D8CD65E6

global 赋值语句二

global a,b=1

v0.6:
B55E1301641A2924647A5E3DE555591C

v1.0:
EFC54ED104A0B2D55E969CB80FE95758


#4

某些自动格式化插件碰到矩阵语法容易狗带,仅供参考……

f(x) = 100(x[1]^2 - x[2])^2 + (x[1] - 1)^2
g(x) = [400x[1]*(x[1]^2 - x[2])+2(x[1] - 1);-200(x[1]^2 - x[2])]
H(x) = [1200x[1]^2-400x[2]+2 -400x[1];-400x[1] 200]

这是我写过的某函数,梯度和Hessian矩阵(捂脸)


#5

a[end÷2]


#6

竟然能这么搞。。真心长见识了


#7

其实不强制的原因有很多呀,比如在Ruby,Python这种语言之中,类名可以用变量赋值(这种面向对象语言的强制类型大小写就只能是对类名用了,类型可以类似值一样使用)所以强制不了,同理Julia也因此不可能强制(我没看懂你为什么提Julia没有Lexer,其实不管有没有,不也没有可能推导出来吗?)。其实我知道唯一强制类型大小写的语言就是Haskell了(不知道有没有别的,有没有补充?),主要原因之一(我猜测是这样)是一开始Haskell约定类型变量都要小写(那个时候没有where这种引入类型变量的东西)所以必须把类型全部大写,另外一些高级特性也要避免这个大小写问题。不过确实没有必要做成强制的,除非迫不得已,不然留一个选择也不是什么问题,这是个编码约定。
另外,1[1]是个什么玩意。。。还有这个数组我也是醉了,我当时看到Array的各种构造函数什么hvat之类的就觉得写这个玩意也正是一门艺术。


#8

因为我们是做IDE的……如果能不根据上下文来判断是什么语法那肯定要来的方便,毕竟我们不可能还用其他语言描述一遍一整天Julia的语言规则……

至于缺德语法我们必须都得支持……


#9

import 和 using 居然是表达式。。。也就是说
甚至你可以这么写

a = import Base.+
b = using JSON

但是毫无作用,a 和 b 照样是 nothing
另外 importall 语句在 1.0 已经废弃了~