#lisp #scripting #script #scripting-language #language

impral

为LISP方言设计的命令解析和评估库,专门针对命令行输入

6个版本

0.1.6 2022年6月17日
0.1.5 2022年6月10日
0.1.4 2022年4月4日
0.1.3 2021年9月18日

#601 in 编程语言

MIT/Apache

92KB
2K SLoC

IMPRAL

IMPRAL是一个针对类似LISP方言的命令解析和评估库,旨在为大型应用、游戏和框架内的合理易用和专门的命令行输入提供支持。

注意
目前不完整/仍在开发中。预期会有破坏性变更。
不要在生产代码中使用。

介绍

功能

简要概述

  • 类似于LISP/Scheme的基本语言。
  • 大多数情况下安全且无panic的解析器。
  • 仅将AST转换一次。
  • 一组相对较小的字面类型...
    • _
    • null
    • 布尔值 (true / false)
    • 字符
    • 整数和浮点数(都是64位)
      • 整数基数:0b…0o…0d…0x…
    • 字符串(在24字节以下紧凑)
      • 包括裸词!
    • 字节数组
    • UUID的,以U为前缀。
  • 数据结构
    • 列表(例如:[ 1, 2, 3 ],逗号可选)
    • 字典(例如:{ a=1, b=2, c=3 },逗号可选)
    • 基数数字列表(0x[C0 +FF -EE]
  • 引用!
    • 结果引用:$
    • 上下文引用:$$
    • 局部引用:$my-ref
    • 全局引用:@my-ref
    • A && B:如果A成功,则仅运行B
    • A || B:如果A失败,则仅运行B
  • 运算符!
    • 算术运算(+ - * / **
    • 相等性(== != < > <= >=
    • 其他(? ! ~ ^ ++ --
  • 字段和索引!
    • 任何后跟点和名称的项目都是一个字段访问:_.name
    • 任何后跟括号表达式的项目是一个索引访问:_[index]
  • 范围!
    • 任何两个不是范围的表达式,通过两个点分隔。
    • 可以通过在点之后添加一个=来设置可选的“最后包含”标志。
    • 例如:_ .. __ ..= _
  • 管道!
    • 第一个表达式是
    • 通过|分隔的表达式
    • 过滤器:|? bar |
    • 链式操作:||?|
  • 可能失败的操作!
    • 任何后跟?的表达式都会展开到默认值。
    • 任何后跟?!的表达式将引发错误。
  • 位置参数:foo _ _
  • 命名参数:foo bar=_ baz=_
  • 子表达式,用()括起来
  • ...以及其他小东西!

语法 & 语义

字面量

字面量是一个简单的值,比如数字、字符串、布尔值等。

以下是可能的字面量列表

  • Nothing:值的缺失;写作null
  • 布尔值:有truefalse。就是这样。
  • 数字:数字可以以多种方式书写...
    • 1337
    • -1
    • 42.69
    • 1.0e-5
    • 0b101010
    • 0xC0FFEE
  • 裸词:裸词是任何完全由字母、数字、_-组成的字符序列,始终以至少一个字母开头。
  • 字符串:任何用双引号括起来的文本!"Hello, World!"
  • 列表:列表可以通过两种方式创建...
    • 通过语法:[item1, item2, … itemN](逗号是可选的!)
    • 通过命令:list item1 item2 … itemN
  • 字典:字典也可以以两种方式创建...
    • 语法规则:{ key1: val1, key2: val2,, keyN: valN}

      键值对之间必须有一个或多个,;在}之前可能有,

    • 通过命令:dict key1 val1 key2 val2 … keyN valN

参考

存在几种类型的参考

  • 全局参考:写作@NAME
  • 局部参考:写作$NAME$NUMBER
  • 结果参考:写作$
  • 上下文参考:写作$$

命令语法

该语言,像任何Lisp一样,由存储为列表的命令(函数调用)组成,其中列表的第一个项目是一个符号,表示要评估的特定命令的名称,后跟任意数量的位置参数。事物偏离之处在于IMPRAL支持命名参数,写于位置参数之后,以便于使用。

因此,一个命令由三个(半)部分组成

  1. 识别命令的符号。
    一个独特的裸词或任何内置运算符。在命令标识符之前不允许放置位置参数或命名参数。

  2. 位置参数。
    值的一个空白分隔列表。

  3. 命名参数。
    一个空白分隔的key=value对的列表;键始终是裸词。
    命名参数必须写于位置参数之后。
    唯一例外的是最后一个位置中的继续命令。
    还可以写入布尔参数,由一个+-和一个裸词作为名称组成。

  4. 继续命令。可选
    另一个命令,它是最后一个位置的一个额外位置参数,写于一个:之后。

  5. 命令分隔符。可选
    如果解析器遇到一个;,它将停止解析当前命令,无论分号之后是什么;对于序列很有用?

总结一下

  • 基本命令语法:symbol arg1 arg2 … argN
  • 命名参数:symbol … kvarg1=val1 kvarg2=val2 … kvargN=valN
  • 标志参数:symbol … +kvarg -kvarg …
  • 带有继续:symbol … …: command

子表达式

表达式可以括在括号中,用于在其他表达式内部使用,以及作为其他命令的参数:()

逻辑运算符

通过写入两个用 && 分隔的命令,后者只有在前者 成功 的情况下才会执行,结果绑定到 $foo … && bar $

通过用 || 分隔它们,后者只有在前者 失败 的情况下才会执行:foo … || bar …

这两个逻辑运算符都可以链式使用;评估将从左到右进行。

命令管道

可以将一系列表达式写成 pipe,其中每个阶段将结果($)传递给下一个阶段:players |? < $.health 50 | heal $

如果某个阶段返回可迭代的对象,将评估该迭代器并将其项目传递到管道中,而不是迭代器本身。

字段和索引访问

通过使用 _.FIELD_[INDEX] 语法,可以访问子值。

范围

通过输入两个连续的点(....= 用于右闭合),可以创建两个表达式之间的范围。

可抛性

通过使用 ? 后缀运算符,可以将给定值转换为默认值,如果它是 null 或错误。添加一个感叹号(?!)会使表达式抛出错误。


待办事项

依赖关系

~2MB
~46K SLoC