还能这样?编程语言奇葩语法大赏!

科技   2024-11-15 13:33   上海  

最近 llamafile 的作者在实现一个高亮语法器,支持的编程语言包括 Ada、Assembly、BASIC、C、C#、C++、COBOL、CSS、D、FORTH、FORTRAN、Go、Haskell、HTML、Java、JavaScript、Julia、JSON、Kotlin、ld、LISP、Lua、m4、Make、Markdown、MATLAB、Pascal、Perl、PHP、Python、R、Ruby、Rust、Scala、Shell、SQL、Swift、Tcl、TeX、TXT、TypeScript 和 Zig ,这些语言几乎涵盖了 TIOBE 指数上的所有内容:

在实现这些语法高亮的过程中发现了非常多的奇葩语法,每个语言的设计都有自己的独到之处,下面我们一起来学习一下。

C语言

C 语言是一种通用的高级编程语言,由 Dennis Ritchie 在 1972 年为 Unix 操作系统开发。它以低级语言的效率和高级语言的灵活性著称,适用于系统编程、嵌入式开发等领域。C 语言的影响力深远,许多现代编程语言(如 C++、Java、JavaScript 等)都借鉴了其语法和概念。

三字母词(Trigraphs)

C语言一向以简单著称,但它却引入了一些令人大跌眼镜的语法特性。首先是三字母词(Trigraphs),它们是为了解决某些键盘缺少特定字符的问题。通过使用三字符的组合来替代某些符号:

  • ??= 替代 #
  • ??( 替代 [
  • ??/ 替代 \
  • ??) 替代 ]
  • ??' 替代 ^
  • ??< 替代 {
  • ??! 替代 |
  • ??> 替代 }
  • ??- 替代 ~

例如,以下代码实际上是有效的C代码:

int
main(int argc, char* argv??(??))
??<
    printf("hello world\n")
;
??>

尽管在C23标准中三字母词被移除了,但为了兼容旧代码,许多编译器仍然支持这种语法。

通用字符名(Universal Character Names)

C语言还引入了通用字符名,允许在代码中使用\u\U开头的Unicode字符。例如:

int \uFEB2 = 1;

虽然看似有助于支持多语言编程,但实际应用中,这种用法并不常见,且编译器对可用的 Unicode 范围有严格限制。

多行注释与多行字符串

在 C 中,单行注释不能跨多行,但如果在每行末尾添加反斜杠 \\,则可以实现多行单行注释:

// 这是一个\
多行注释

类似地,字符串也可以使用反斜杠延续到下一行。

Haskell 的嵌套注释

Haskell是一种函数式编程语言,以其强大的类型系统和纯函数式特性著称。Haskell 由 Haskell Curry 命名,支持高阶函数、惰性计算和不可变数据,是教学、研究和开发高度可靠的软件的重要工具。

很多语言不支持注释的嵌套,例如C语言中的多行注释。然而,Haskell允许嵌套注释,这在调试和代码说明中非常便利:

{-
    这是一个注释块
    {-
        嵌套的注释
    -}

-}

Tcl中的特殊标识符

Tcl(Tool Command Language)是一种动态类型的脚本语言,1988 年由 John Osterhout 发明。Tcl 以其简单的语法和强大的扩展能力广受欢迎,常用于嵌入式脚本、快速原型设计和应用程序脚本化。

Tcl语言的标识符可以包含引号,这意味着以下代码是有效的:

puts a"b

甚至变量名中也可以包含引号,需要通过$加花括号的方式引用:

set a"b "Hello, World!"
puts ${a"b}

JavaScript的正则表达式陷阱

JavaScript是一种动态、弱类型的脚本语言,主要用于网页开发。由 Netscape 在 1995 年推出,其语法受 C 语言影响,支持面向对象、函数式和命令式编程。JavaScript 是前端开发的必备语言,广泛用于客户端和服务器端脚本开发。

JavaScript中的正则表达式语法有时会让人困惑。例如,以下正则表达式是有效的:

var regex = /[/]/g;

在字符集[]内部的斜杠/不需要转义,这与在正则表达式外部的用法不同。

此外,JavaScript还支持一些不可见的Unicode字符作为行终止符,如LINE SEPARATOR (\u2028)PARAGRAPH SEPARATOR (\u2029),这可能导致代码在不同环境下的行为不一致。

Shell的Here Document奇技

Shell脚本语言是 Unix/Linux 系统中的命令解释器和脚本编程语言。常见的 Shell 有 Bash、sh 等。Shell 脚本用于自动化任务、系统管理和批处理工作,是系统管理员和开发者的得力工具。

在Shell脚本中,Here Document允许定义多行字符串。通常的用法是:

cat <<EOF
这是一个
多行字符串
EOF

但你可以使用一个空的界定符,这会使Here Document在遇到空行时结束:

cat <<''
Hello

echo "这将被执行,而不是字符串的一部分"

以上代码中,字符串在第一次空行时结束,后续的echo命令将被执行。

字符串插值的奇异用法

许多现代编程语言支持字符串插值,例如TypeScript、Kotlin、Scala等,它们允许在字符串中嵌入变量甚至表达式。

Kotlin的字符串插值

Kotlin是一种现代编程语言,由 JetBrains 在 2011 年发布,旨在改善 Java 语言的不足。Kotlin 兼容 Java,并且具有更简洁的语法、空安全、扩展函数等特点。它被广泛应用于 Android 开发、Web 开发和企业级应用。

val s = "${name.toUpperCase()}, 欢迎您!"

但当嵌套的花括号数量增加时,解析和高亮这些字符串变得复杂。

Swift的自定义分隔符

Swift是 Apple 公司为 macOS、iOS、watchOS 和 tvOS 开发的编程语言,发布于 2014 年。Swift 以其现代语法、安全性和高性能著称,并逐渐取代了 Objective-C 在苹果生态中的地位。

Swift允许在字符串的起始位置添加任意数量的#号,使得字符串中的特殊字符无需转义:

let rawString = #"""
这是一段包含 " 引号和 \ 反斜杠的字符串
"""
#

C#的多重引号字符串

C# 是一种面向对象编程语言,由微软在 2000 年发布,作为其.NET 框架的一部分。C# 结合了 C++ 的强大功能和 Java 的简单性,广泛用于 Windows 应用程序、Web 开发、游戏开发(通过 Unity)等领域。

C#解决字符串中包含引号的问题是使用多重引号,字符串的起始和结束引号数量相同:

Console.WriteLine(@"""这是一句话,包含引号""");
Console.WriteLine(@"""""""多重引号字符串""""""");

这种方式使得在字符串中包含任意数量的引号变得简单直观。

Lua的多层次字符串

Lua是一种轻量级、多范式的编程语言,1993 年由巴西里约热内卢天主教大学开发。Lua 以其简单、高效、嵌入式设计和灵活的元表机制受到游戏开发和嵌入式系统开发的青睐。

Lua使用等号=来定义不同层次的长字符串或注释,这使得在字符串中包含任意的[]字符成为可能:

-- 长字符串
local str = [==[
这是一个包含特殊符号的字符串 [[ ]] ]==]

]==]

-- 多层注释
--[==[
这是一个注释
--[=[
    嵌套的注释
]=]
]==]

Assembly的注释与语法

Assembly(汇编语言)是一种低级编程语言,直接与计算机硬件交互。每条汇编指令通常对应一条机器语言指令。汇编语言用于嵌入式系统、驱动程序开发和系统级编程,需要深入了解硬件结构。

汇编语言的语法因汇编器不同而有较大差异,如NASMAT&T等。注释符号可能是;#//等,甚至在一些老式汇编器中,/用于注释。此外,汇编代码通常与预处理器如cppm4结合使用,增加了语法的复杂性。

section .data
message db 'Hello, World!', 0xA ; 定义字符串,包含换行符

section .text
global _start

_start:
; syscall 写操作
mov rax, 1 ; 系统调用编号:write
mov rdi, 1 ; 文件描述符:stdout
mov rsi, message ; 指向字符串的指针
mov rdx, 13 ; 字符串长度
syscall

; syscall 退出程序
mov rax, 60 ; 系统调用编号:exit
xor rdi, rdi ; 退出状态码:0
syscall

Perl的正则表达式与文档块

Assembly(汇编语言)是一种低级编程语言,直接与计算机硬件交互。每条汇编指令通常对应一条机器语言指令。汇编语言用于嵌入式系统、驱动程序开发和系统级编程,需要深入了解硬件结构。

Perl以其强大的正则表达式支持著称,其语法也相当灵活。例如,Perl允许使用不同的定界符来定义正则表达式,以避免转义斜杠:

$string =~ s!hello!Perl!i;

此外,Perl的文档块使用了独特的=pod=cut语法,可以在代码中嵌入大段说明性文字。

=pod

=head1 NAME

MyScript - 演示Perl的文档块

=head1 SYNOPSIS

 perl MyScript.pl

=head1 DESCRIPTION

这是一个示例脚本,展示了Perl的文档块用法。

=cut


print "Hello, World!\n";

Ruby的语法迷宫

Ruby是一种面向对象的动态编程语言,由 Yukihiro "Matz" Matsumoto 在 1995 年设计。Ruby 以其简洁、优雅的语法和强大的元编程能力著称。Ruby on Rails 是其最著名的 Web 应用开发框架。

Ruby的语法灵活性极高,甚至让解析器都难以完全理解。例如,反引号既可以用于执行命令,也可以作为方法名:

def `(cmd)
  "执行命令:#{cmd}"
end

此外,Ruby的字符串、正则表达式、符号等语法经常让人迷惑:

puts "这是#{<<-HERE.strip}太神奇了"
  多行字符串
HERE

Ruby允许在字符串插值中嵌入Here Document,这种语法在其他语言中极为罕见。

Ada:单引号的多重用途

Ada是一种结构化、静态类型的编程语言,由 Jean Ichbiah 在 1983 年为美国国防部开发。Ada 以其高可靠性和安全性著称,广泛用于航空、航天和国防领域的高要求系统开发。

字符字面量

像C语言一样,Ada使用单引号来表示字符字面量:

C := 'A';

属性访问

单引号还用于访问类型或对象的属性。例如:

Length := Array'Length;

这里,Array'Length表示数组的长度。

泛型和函数调用

更有趣的是,单引号可以用于调用泛型函数或属性函数。例如:

with Ada.Text_IO;

procedure Main is
S : String := Character'(')')'Image;
begin
Ada.Text_IO.Put_Line("变量 S 的值是:" & S);
end Main;

运行上述代码,会输出:

变量 S 的值是:')'

在这个示例中,我们:

  1. 定义了一个字符字面量')'
  2. 调用了属性函数Image,将字符转换为字符串表示。
  3. 将结果赋值给字符串变量S,并输出。

这种单引号的多重用途,使Ada的代码在解析和阅读时需要格外小心。

BASIC:古早语法的奇妙之处

BASIC(Beginner's All-purpose Symbolic Instruction Code)是一种易于学习的编程语言,1964 年由 John Kemeny 和 Thomas Kurtz 开发。BASIC 为初学者设计,通过简洁的语法帮助用户快速编程。虽然早期 BASIC 版本较为简陋,但后来发展出了更加现代和强大的变种,如 Visual BASIC。

代码示例

以下是一个Commodore BASIC的经典代码片段,可以看到它打破了许多我们惯常的语法假设:

10 rem cbm basic v2 示例
20 rem 包含关键字的注释:for, data
30 dim a$(20)
35 rem 缺少空格的节省空间写法:
40 fort=0to15:poke646,t:print"{revers on} ";:next
50 geta$:ifa$=chr$(0):goto40
55 rem 行末的字符串引号可以省略
60 print"{white}":print"再见...
70 end

解析特点

  • 省略空格:第40行中,FOR、变量T、赋值符号之间没有空格。这在早期是为了节省内存和存储空间。
  • 模糊变量与关键字:关键词和变量之间没有明确的分隔符,例如goto会被解析成关键字,即使它是标识符的一部分。
  • 行末可省略引号:字符串末尾的引号可以省略,并不会导致语法错误,例如第60行。

这样的语法设计在今天看来充满了挑战,但它确实展示了早期计算机编程的独特风貌。

Visual BASIC的日期字面量

Visual BASIC 还引入了奇特的日期字面量语法:

Dim v As Variant    ' 声明一个Variant变量
v = #1/1/2024# ' 持有一个日期值

同时,VB还支持条件编译指令,这对语法分析提出了更高的要求:

#If DEBUG Then
<WebMethod()>
Public Function SomeFunction() As String
#Else
<WebMethod(CacheDuration:=86400)>
Public Function SomeFunction() As String
#End If

FORTH:简单到极致的语言

FORTH是一种极其简洁的编程语言,它将所有内容都作为空格分隔的标记处理。虽然简洁,但人类理解起来却非常复杂。这是一种典型的低级语言,直接操作硬件,语法非常紧凑。

代码示例

c" hello world"

以上代码在FORTH中表示字符串hello world,类似其他语言的字符串定义,但方式独特。

**FORTRAN与COBOL

FORTRANCOBOL是两种古老但仍然在大企业和金融系统中广泛使用的编程语言。它们的语法规则严格,且具有固定的列宽要求。

FORTRAN代码示例

*
* 快速返回
*
IF ((M.EQ.0) .OR. (N.EQ.0) .OR.
+ (((ALPHA.EQ.ZERO).OR. (K.EQ.0)).AND. (BETA.EQ.ONE))) RETURN
*
* 如果 ALPHA 等于零
*
IF (ALPHA.EQ.ZERO) THEN
IF (BETA.EQ.ZERO) THEN
DO 20 J = 1,N
DO 10 I = 1,M
C(I,J) = ZERO
10 CONTINUE
20 CONTINUE
ELSE
DO 40 J = 1,N
DO 30 I = 1,M
C(I,J) = BETA*C(I,J)
30 CONTINUE
40 CONTINUE
END IF
RETURN
END IF

FORTRAN的列规则

  • 在第1列放置*cC表示注释。
  • 在第6列放置非空字符可以延长行的字符数超过80个。
  • 标号放在第1-5列。

COBOL代码示例

000100*Hello World in COBOL
000200 IDENTIFICATION DIVISION.
000300 PROGRAM-ID. HELLO-WORLD.
000400
000500 PROCEDURE DIVISION.
000600 DISPLAY 'Hello, world!'.
000700 STOP RUN.

COBOL的列规则

  • 在第7列放置*表示注释。
  • 在第7列放置-可以延续行的字符数超过80个。
  • 行号放在第1-6列。

Zig:独特的多行字符串

Zig是一种现代编程语言,设计简洁,注重安全和性能。它的多行字符串语法非常独特,使用双反斜杠\\作为前缀。

代码示例

const copyright = \\
\\ Copyright (c) 2024, Zig Incorporated
\\ All rights reserved.
;

解析特点

这种语法避免了像Python的三重引号那样需要调用textwrap.dedent()来处理缩进的问题,但需要在结尾使用难看的分号。这种设计非常适合不需要分号的语言,如Go、Scala、Python等。

最后

大家觉得哪个语法最奇葩?你最喜欢的又是什么语言?欢迎在评论区留言!

参考:https://justine.lol/lex/


👇🏻 点击下方阅读原文,获取鱼皮的编程学习路线、原创项目教程、求职面试宝典、编程交流圈子。

往期推荐

要是工作前就知道这个,该多好。

24 年最新项目,手把手教程

突击面试,看这个就够了!

我的新书,冲上了京东榜一!

不敢相信,Nginx 还能这么玩?

我做了个闯关游戏,竟难倒了无数程序员。。

看了我的简历指南,面试率翻倍

程序员鱼皮
一手科技热点和编程干货 | 免费编程学习网 codefather.cn
 最新文章