5个版本

0.1.4 2023年12月18日
0.1.3 2023年12月18日
0.1.2 2023年12月18日
0.1.1 2023年12月17日
0.1.0 2023年12月17日

#133 in 编程语言

每月21次下载

MIT/Apache

290KB
7K SLoC

nope脚本

nope是一种脚本编程语言,针对小型和有趣的程序进行了优化

nope是

  • 有趣的
  • 基于表达式的
  • 垃圾回收
  • 命令式,带有一些函数式元素
  • 专注于解决实际问题
  • 将数据与逻辑分离
  • 可以原生表示JSON和XML
  • 单个二进制文件
  • 用Rust构建
  • 将包含一个完整的stdlib
  • 将具有事件循环并发模型

a screenshot of nope, see samples/fizzbuzz2.nope

运行和安装

安装rust / cargo然后

cargo install nope-lang

您可以使用以下命令启动交互式repl

nope

或运行脚本

nope myscript.nope

如果您使用vim,可以使用以下命令安装语法支持

nope --install-vim-plugin

路线图

该项目目前处于一个非常非常早期的阶段

  • 评估表达式
  • 全局、局部变量和作用域
  • 常量与运算符
  • 数学stdlib
  • 循环
  • 函数
  • 字典和数组
  • 垃圾回收
  • 错误,try catch
  • 字符串stdlib
  • 解析和序列化
  • utils stdlib
  • 资源类型
  • os stdlib
  • 单元测试
  • v1.0
  • 异步
  • 异步stdlib
  • v2.0
  • 网络服务器
  • v3.0
  • 模块与包
  • v4.0
  • 性能

类型

以下表格应该能给您一个基本类型和允许的数据模型的概念。请注意,目前尚未实现数组和对象。

[
    null: null,
    void: [void _ ()]
    bool: [true false]
    num:  [0 1 99 3.14 -1_000_000 0xdead 0b10101 NaN Inf PI]
    string: ['foo' "bar" ~hello]
    arrays: [1 2 true null []]
    dicts:  {key:32 val:99}
    mixed:  ['foo' key:'value']
]

nope中的所有数据都是上述类型和结构的组合。

注意,数字是64位浮点数,字符串是Unicode。

代码结构

让我们看看基本的nope程序。

let x = d6() + d6()
var winning = false

if x >= 10 (
    set winning = true
    print 'haha, yes! :)'
) else (
    print 'oh no :('
)

在第一行,我们定义了一个名为x的变量,其初始值由一个调用两个函数'd6'并添加结果的运算符定义。变量'x'在当前作用域中的后续表达式中可用。由于它是用let定义的,因此不能用set来更改它。

在第二行中,我们声明了另一个变量,但这次它可以被修改。

我们接下来评估一个经典的if-else条件语句。请注意,与其他语言不同,代码块不是由花括号{/}定义的,而是由括号(/)定义的。原因是nope是一种基于表达式的语言,因此每个nope构造都是一个产生值的表达式。

以下示例将这些原则付诸实践。

let res = if flip_coin(), "heads" else "tails"

if返回一个值,该值将取决于flip_coin()的结果,可能是字符串“heads”或“tails”。

您可以用括号包围表达式,也可以用括号包围多个表达式。在后一种情况下,所有表达式都将按顺序评估,但只有最后一个表达式会产生值。

这就是我们使用set winning = true表达式来改变变量winning,并打印消息的原因。

请注意,print表达式print "hey"省略了其参数周围的括号。这并不是print函数的特殊情况。在nope中,所有函数参数的括号和逗号都是可选的。

let clamped_0_100 = max 0 min value 100

这也适用于没有参数的函数。

let score = d6 + d6 + d6

请注意,函数应用具有最高的优先级,因此print 1 + 1打印的是1,因为加法是在之后执行的。这并不总是您想要的。一种方法是用括号print(1 + 1),注意函数名和第一个括号之间的空格是有意义的。没有空格时,期望有完整的参数列表,而有空格时,只是单个参数的括号;max(1,2) / max (1) (2)

另一种方法是使用左箭头<-运算符。此运算符简单地评估右侧的完整表达式。因此,您可以像这样打印print <- 1 + 1

按照惯例,nope中的所有函数都将它们操作的数据作为最后一个参数。

let txt = read_text 'file.txt'
let txt2 = upper txt
write_txt 'file2.txt' txt2

借助左箭头<-运算符,您可以编写以下代码:

write_txt 'file2.txt' <- upper <- read_text 'file.txt'

右箭头->运算符(尚未实现)执行相同的操作,但方向相反。

read_text 'file.txt' -> upper -> write_txt 'file2.txt'

运算符

相等==!=

检查操作数是否为同一类型,如果是这样,则比较它们的值或引用

比较 <><=>=

首先将数字转换为它们的数值等价物(字符串和数组为NaN),然后进行比较

运算符 +-=!+-= 比较数字的相等性,精度达到 EPSILON

算术运算符 +-*/%**,

通常的数学运算。操作数转换为它们的数值等价物(字符串为NaN)

字符串的加法 + 将执行字符串连接,并将另一个操作数转换为字符串

整数 & 位运算

所有以 ~ 开头的算术运算符首先将操作数转换为 int32,然后执行整数运算。

例如 10 ~/ 42

位运算符 ~!~|~&~^~<<~>>~>>> 将操作数转换为 int32,然后执行位运算

布尔值 !||&&

操作数的布尔等价值是

  • false 如果是 nullvoid0
  • true 否则

运算符

  • ! 取消布尔等价值的符号
  • && 如果第一个操作数为假,则返回第一个操作数,否则返回第二个
  • || 如果第一个操作数为真,则返回第一个操作数,否则返回第二个

默认 ??

如果第一个操作数是 nullvoid,则返回第二个操作数,否则返回第一个

重复 *:

重复运算符将计算并求和第二个操作数 N 次,其中 N 是第一个操作数。

例如,这将掷骰子 3 次

print(3 *: d6)

请注意,首先将第一个操作数转换为数字。如果数字为零或负数,则返回 void

循环

不支持两种循环机制,loop 无限重复后面的表达式,和 while,它评估一个条件,只要条件保持为真,就重复下一个表达式。

var i = 0, while i < 10 (
    set i = print i + 1
)

continue 将直接跳转到下一次迭代,而 break 将停止迭代

var i = 0, loop (
    if i < 10 (
        set i = i + 1
        continue
    )
    break
)

循环也是表达式,并返回它们的表达式的最后一个值。 break 返回空值,break_as 将中断并返回下一个表达式

var i = 0, let res = loop (
    if i < 10 (
        set i = i + 1
    ) else (
        break_as 'done!'
    )
)

打印到终端

printecho 是类似的功能,它们接受一个参数,将值打印到终端,并返回它。区别在于 print 打印原始字符串转换后的值,而 echo 打印值的彩色内部表示。

在交互式解释器中,每次提交执行后都会回显。

请注意,由于 printecho 都返回它们的参数,因此可以在表达式中使用它们来显示临时值。

var i = 0, while i < 10 (
    set x = print x + 1
)

类型检查与转换

以下函数将转换为其类型: to_numto_boolto_str

以下函数测试类型: is_voidis_nullis_boolis_numis_intis_nanis_oddis_evenis_str

算术函数和常量

Nope 支持以下算术函数

  • floorceilabsacosacoshsinsinhasinasinhcoscoshtantanhatanatanhinv log2log10lnln1pexpexpm1sqrtcbrtroundfroundtruncsignmaxminpowatan2

以下算术常量

  • NaNInfPIESQRT_2SQRT_2PILN_2LN_10LOG2_10LOG2_ELOG10_2LOG10_EPHITAUEPISLONMAX|MIN_INT MAX|MIN_F64MAX|MIN_I32|16|8MAX_U32|16|8

字符串

  • len 返回字符串的长度(扫描整个字符串)
  • upperlower 用于转换字符串的大小写
  • trim 移除字符串开头和结尾的空白字符
  • char_at(idx, text) 获取字符串中指定索引的字符
  • substr(from_idx, to_idx, text),根据字符索引返回子字符串。其中 to_idx 不包含在内。负索引从字符串末尾开始。
  • replace(from, to, text)text 中的 from 替换为 to
  • find(substr, str),返回 substrstr 中的位置或 -1
  • contains(substr, str) 如果 substrstr 的子字符串,则返回 true
  • bitstr 将数字转换为它的位表示形式的字符串

随机性

Nope 支持以下随机数生成器函数

  • randomflip_coinrand100d4d6d8d10d12d20d100

如果您想返回三次骰子投掷的总和,最好使用重复操作符 3*:d6

单位

Nope 允许您使用单位后缀注释数字字面量。

let distance = cos 45deg * (10Km + 3miles + 5yd + 3in + 5cm)

单位将数字转换为它们的国际单位制等效值。例如,所有距离都转换为米。

运行时不会保留数字的单位信息。单位不过是方便的语法技巧。

以下单位受到支持

pitauphiGTMTkTTkggmgugngTiGiMikidhminsmsusnsmoondegradinkmmdmcmmmumnmlbozmilemilesftydFCKm3ldm3dlclmlcm3barrelcuftft3galpintcuinin3cuydyd3m2dm2cm2mm2ahakm2mile2yd2sqydft2sqftin2sqinbelgiumfootballfieldTiBTBGiBGBMiBMBKiBKBmillionbilliontrillionquadrillonmillithousandthmicronanopico

也可以使用from_unitto_unit函数在运行时将一个单位转换为另一个单位。

let area_sqin = to_unit 'sqin' <- from_unit 'ha' <- 50

依赖项

~6-17MB
~182K SLoC