1 个不稳定版本
新 0.1.0 | 2024 年 8 月 21 日 |
---|
127 在 编程语言 中排名
74 每月下载量
140KB
3.5K SLoC
qalam
受伊斯兰/阿拉伯术语启发,简单易懂,解释型编程语言
目录
简介
作为一个学习练习,为了真正磨练我的技能,我想自己编写一种编程语言。为了实现这一目标,我正在遵循名为《 Crafting Interpreters by Robert Nystrom》的精彩教程。
罗伯特会逐步介绍创建名为 Lox 的编程语言的全过程,而我并不想简单地遵循教程并复制粘贴代码。我发现,当你稍微改变教程时,真正的学习才会发生,这样你可以有更深入的理解。
为了做到这一点,我决定为该语言创建自己的语法,其功能与 Lox 相同。我还决定使用 Rust 而不是 Java 来创建语言,这样我就不可能复制粘贴代码。
使用 qalam
- 安装
qalam
解释器
cargo install qalam
- 创建一个
.qlm
文件,其中包含你的源代码
echo "qul \"hello world!\"" > main.qlm
- 运行
qalam
代码
qalam main.qlm
>> hello world!
语法
关键字
语法 | 含义/灵感 | 用法 |
---|---|---|
niyya |
意图。表示存储值的意图。 | 变量声明 |
amal |
善行/行动。函数执行某些操作(行动/善行) | 函数声明 |
radd |
返回(阿拉伯语) | 返回语句 |
qul |
说(阿拉伯语) | 打印语句 |
ghaib |
未见,隐藏。表示值不存在 | 空值 |
niyya a = 1;
niyya b = 2;
amal add(a, b) {
radd a + b;
}
qul add(a, b);
// prints 3
条件语句
语法 | 含义/灵感 | 用法 |
---|---|---|
shart |
条件。如果条件得到满足,则执行某些操作。 | 如果语句 |
illa |
否则。不言而喻 | 否则语句 |
ilshart |
shart 和 illa 的组合 | else-if 语句 |
haqq |
真理。布尔 true 是真理的顶峰。 |
布尔 true 值 |
batil |
虚假。布尔 false 是虚假的顶峰。 |
布尔 false 值 |
wa |
和(阿拉伯语)。不言而喻。 | 与运算符。也可以使用 && |
aw |
或(阿拉伯语)。顾名思义。 | 或运算符。也可以使用 || |
la |
不(阿拉伯语)。顾名思义。 | 非运算符。也可以使用 ! |
niyya a = haqq;
niyya b = batil;
shart(a wa b) {
// do something
} ilshart(la a) {
// do something
} illa {
// do something
}
循环
语法 | 含义/灵感 | 用法 |
---|---|---|
tawaf |
环绕。环绕克尔白(Ka'bah)的名称,用于朝觐。表示环绕。 | for 循环 |
baynama |
While(阿拉伯语)。顾名思义 | While 循环 |
iftar |
破戒。穆斯林破戒的时间称为 iftar 。 |
break 语句 |
safar |
旅行或旅行。表示循环将继续其旅程。 | continue 语句 |
tawaf(niyya i = 0; i < 10; i = i + 1) {
qul i;
// prints 0 to 9
}
niyya a = haqq;
niyya i = 0;
baynama(a) {
shart (i < 10) {
qul i;
// prints 0
i = i + 1;
safar;
} illa {
iftar;
}
}
对象(类)
语法 | 含义/灵感 | 用法 |
---|---|---|
kitab |
章节(字面:书籍)(阿拉伯语)。在伊斯兰书籍中,章节被称为 kitab。类与章节类似,它们将相关的数据分组在一起。 | 类定义 |
khalaq |
创建(阿拉伯语)。构造函数创建类对象。 | 类构造函数 |
nafs |
自我/灵魂。在伊斯兰教中,nafs 用于描述自我的欲望。表示实例(自我)的状态。 | 实例访问器(JavaScript 中的 this ,Python/Rust 中的 self ) |
ulya |
最高,优越(阿拉伯语)。超类优于子类。 | 超类访问器(JavaScript 中的 super ) |
ibn |
儿子/孩子(阿拉伯语)。子类从超类继承,就像孩子从父母那里继承一样。 | 类继承运算符 |
kitab Animal {
khalaq(name, sound) {
nafs.name = name;
nafs.sound = sound;
}
speak() {
qul nafs.sound;
}
}
kitab Feline ibn Animal {
khalaq(name, sound) {
ulya.khalaq(name, sound);
}
purr() {
qul "purr"
}
}
niyya cat = Feline("Hurayra", "Meow");
cat.speak();
// prints "meow"
cat.purr()
// prints "purr"
类型
以下是在 qalam
中支持的内置类型
类型 | 描述 | 初始化示例 |
---|---|---|
number |
数值。所有数字都存储为浮点值。没有小数部分的数字被视为整数。 | niyya num= 1.0; |
string |
字符集合。字符可以用整数索引。用双引号初始化。 | niyya name= "阿马尔"; |
bool |
布尔值真或假。 haqq = true,batil = false。 |
niyya is_foo=haqq; |
array |
任何值的集合。值可以用整数索引和设置。用方括号初始化。 | niyya arr= [1, "one",haqq]; |
原生函数
我向程序中实现了一些原生函数
函数名称 | 参数 | 返回类型 | 描述 |
---|---|---|---|
clock |
number |
返回自纪元以来经过的时间(秒) | |
typeof |
arg:any |
string |
将参数的类型作为字符串返回 |
str |
arg:any |
string |
将参数转换为字符串 |
str2num |
arg:string |
number |
将参数转换为数字。如果不可能,则抛出错误。 |
substr |
arg:string,start:number(正整数),length:number(正整数) |
string |
返回从 start 开始,长度为 length 的子字符串 |
index_of |
arg:string,substring:string |
number |
返回子字符串在参数中的起始索引。如果未找到,则返回 -1。 |
replace |
arg:string,old_substr:string,new_substr:string |
string |
将参数中所有 old_substr 替换为 new_substr 。 |
len |
arg:string|array |
number |
返回字符串或数组的长度。 |
max |
a:number,b:number |
number |
返回输入的最大值 |
min |
a:number,b:number |
number |
返回输入的最小值 |
pow |
base:number,exp:number |
number |
将基数提升为指数的幂 |
random |
min:number,max:number |
number |
返回范围在 min 到 max 之间的随机数 |
random_int |
min:number(int),max:number(int) |
number |
返回一个在 min 到 max 范围内的随机整数 |
push |
arr:array,val:any |
ghaib |
将一个值推送到数组的末尾 |
pop |
arr:array |
any |
从数组的末尾弹出一个值并返回它。如果不存在,则返回 ghaib |
数组 |
大小:number(正整数),值:any |
array |
创建一个大小为 size 的数组,所有值都初始化为 value 。 |
代码 |
字符:string |
number |
返回一个单字符字符串的字符码。 |
floor |
数字:number |
number |
返回小于或等于 num 的最接近整数 |
ceil |
数字:number |
number |
返回大于或等于 num 的最接近整数 |
round |
数字:number |
number |
返回最接近 num 的整数。如果 num 在两个整数之间,则返回远离 0 的整数。 |
完整示例
为了展示 qalam
的功能,以下是一个反转链表的示例
kitab ListNode {
khalaq(value, next) {
nafs.value = value;
nafs.next = next;
}
}
amal list_to_string(head) {
niyya string = "";
niyya curr = head;
baynama(curr != ghaib) {
string += str(curr.value);
shart(curr.next) {
string += " -> ";
}
curr = curr.next;
}
radd string;
}
niyya list = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5, ghaib)))));
amal reverse_list(head) {
niyya prev = ghaib;
niyya curr = head;
niyya next = ghaib;
baynama(curr != ghaib) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
radd prev;
}
qul "Original list:";
qul list_to_string(list);
qul "Reversed list:";
qul list_to_string(reverse_list(list));
输出
Original list:
1 -> 2 -> 3 -> 4 -> 5
Reversed list:
5 -> 4 -> 3 -> 2 -> 1
更多示例
我提供了更多的示例,在 示例目录 中。您可以使用 cargo run --example <name>
运行它们,或者在示例子目录中通过使用 qalam <file_path>
运行 main.qlm
文件。
速度
qalam
是一种解释型语言,具有动态类型。然而,由于实际上没有进行任何优化,它非常慢。在 Rust 编程方面,我也是一名极端的业余爱好者,因此,它可能比 Robert 在 Java 中的实现还要慢,因为我肯定在使用 Rust 时犯了成千上万次错误。
为了展示它实际上有多慢,我将将其速度与 JavaScript 和 Python(解释型、动态类型语言)进行比较。对于比较,我将使用递归算法计算第 30 个斐波那契数的计算来使用。
以下是每种语言的脚本
Python
import time
def fib(n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)
start = time.time()
result = fib(30)
end = time.time()
print(f"{end - start}")
JavaScript
function fib(n) {
if (n <= 1) {
return n;
} else {
return fib(n - 1) + fib(n - 2);
}
}
let start = Date.now() / 1000;
let result = fib(30);
let end = Date.now() / 1000;
console.log(`${end - start}`);
Qalam
amal fib(n) {
shart(n <= 1) {
radd n;
} illa {
radd fib(n - 1) + fib(n - 2);
}
}
niyya start = clock();
niyya result = fib(30);
niyya end = clock();
qul(str(end - start));
每个测试都运行了 10 次,平均运行时间如下表所示
语言 | 平均运行时间(秒) |
---|---|
JavaScript | 0.0185 |
Python | 0.2238 |
Qalam | 78.2590 |
JavaScript 和 Python 每次运行都不到一秒。Qalam 需要超过一分钟。尽管如此,我仍然为它能工作而感到自豪。
依赖项
~685KB
~12K SLoC