使用旧Rust 2015

0.0.0 2016年6月19日

#183 in #unit

MIT 许可证

105KB
2.5K SLoC

lion

一种非常小巧、动态、弱类型的语言,用于以更直观的方式进行计算。

  • 扫描器
  • 词法分析器/标记器
  • 解析器
  • 从AST到评估树
  • 预定义
    • 数学
    • 单位
  • 评估器(β还原器)
  • 简单的命令行界面(repl)
  • 版本 1.0.0
  • 进一步增强

示例

$ 2 + 2
= 4
$ 20 cm + 2 m
= 220 cm
$ (20 cm + 2 m) m
= 2.2 m
$ add2 x = x + 2
$ add2 5
= 7
$ add2cm x = x + 2 cm
$ add2cm (5 cm)
= 7 cm

设计目标

  • 尽量在语言内部而不是在编译器级别定义大多数内容
  • 支持前缀、后缀和中缀运算符--函数、单位和运算符
  • 保持懒惰,而不是急切--但仍然提供强制严格评估的方法(仍在考虑中,因为懒惰可能还需要不可变

  • 不要编写另一种编程语言。 保持简单。

实现目标

  • 能够根据原始文本源生成解析树和抽象语法树
  • 能够从生成的树中评估原始文本源
    • 能够生成带有行号和字符号的良好错误信息
  • 能够将程序编译成字节码
  • 支持基本的范围/栈

版本 1.0.0 备注

  • 为了加快制作过程,第一个版本将非常简单,仅支持数值类型
  • 数值将由值以及单位表示
  • 向量和其他更复杂的类型将不会包含在未来的版本中
  • 仅包含初始版本中的示例中的简单表达式

可能的未来扩展

  • 函数上的模式匹配
    • 解包
    • 支持可变数量的参数
    • 匹配单位
  • 支持默认值和多个函数签名
  • 定义函数的支持输入和输出类型(在添加向量等的同时仍然保持加法运算符等)
    • 添加类型应该是全有或全无的过程,要么在所有参数和输出上都有它们,要么一个都没有
  • 语言扩展
    • 在运行时外部定义,以便使用某种插件或中间件API添加一些功能或类型
    • 向量
    • 数组,数组的操作
    • 字符串,作为字符数组操作字符串
    • I/O
    • 绘图
  • 导入和模块 - 能够添加在某个地方定义的模块中的定义(将电路分割与基本数学分开等)
  • 其他数制 + 该数制的数字字面量(十六进制等)

语法

  • 函数

    • 定义名称和一些参数的基本语法 <function_name> <param1> <param2> = <expression>
    • 匿名函数可以省略函数名称,并使用反斜杠代替(使用反斜杠时,名称后的空格不是必需的) \<param1> <param2> = <expression>
    • 函数表达式返回定义的函数
  • 表达式

    • 由符号组成,在运行时根据这些符号的定义(优先级等)进行评估
    • [高级] 可以包含在代码块中 { <expressions> } 其中表达式的结果是代码块中的最后一行
  • 字符串:(但还没有字符串运算符...)

    • 只使用双引号表示字符串,字符串中的双引号使用反斜杠转义
  • 默认运算符

    • 以下基本运算符的定义包含在序言中
      • +
      • -
      • *
      • /
      • **
      • ==
      • !=
      • >=
      • <=
      • neg - 用于否定数字(因为 - 是中缀运算符)
      • 数学函数,如 maxminsincostan

自定义运算符

所有基本运算符将在特殊的序言模块中以 lion 语法定义。

定义运算符就像调用 operator 函数并传递其三个参数一样简单

  1. 运算符的固定性,以下之一:PREFIXPOSTFIXINFIX
  2. 优先级,从 0 到 9 的数字,其中 9 是最高优先级
  3. 运算符的名称,用双引号括起来(例如:"$$"
  4. 一个函数(可以是匿名函数)表示运算符的输入和输出之间的关系
  • PREFIXPOSTFIX 运算符接受一个参数
  • INFIX 运算符接受两个参数
  • 函数参数的名称不重要

函数声明语法是定义具有高优先级的 PREFIX 运算符的语法糖

待办事项:目前所有内容都是左结合右,这可能在以后的版本中更改为可配置的

示例

$ operator INFIX 6 "$$" (\x y = x * y + x)
$ 3 $$ 2
= 9
$ f x = 2 * x
$ operator POSTFIX 6 "timestwo" (f)
$ 2.5 timestwo
= 5
$ (2.5 cm) timestwo
= 5 cm

单位

单位只是具有非常高的优先级的 POSTFIX 运算符。本节描述了一些有用的函数,这些函数被引入以简化单位的使用。

单位可以具有任何大小写,但为了避免混淆,建议尽可能始终使用正确的 SI 前缀(或您遵循的任何其他系统)。

defineUnit 函数接受一个代表单位名称的敏感大小写的字符串(使用双引号)。这定义了一个紧绑定(高优先级)的 POSTFIX 函数,当使用时,尝试根据当前定义的任何转换将它的参数转换到现在定义的单位。

$ defineUnit "kohm"
$ 3 kohm
= 3 kohm

所有没有单位的值都定义为具有一个特殊的单位,称为 units。将此单位转换为其他单位时,新单位只需将 units 作为该值的单位替换。

defineUnit 会为您定义这种简单的转换,并立即使您的单位可用于在 units 和其他单位之间进行简单转换。

现在定义的单位函数可以用作convert函数的参数。在幕后,convert函数用于将数量的当前单位转换为新的单位。

# By wrapping the `cm` function in parentheses, it is not evaluated
$ convert (2 units) (cm)

计算过程中会自动执行转换。单位会合并,保留最左侧的单位。不兼容的单位(无法进行转换的)保持不变,因为它们无法进一步简化。

$ 20 cm + 2 m
= 220 cm
$ 20 cm + 2 m + 2 kohm
= 220 cm + 2 kohm

您可以通过调用其函数显式转换为单位

$ (20 cm + 2 m) m
= 2.2 m

这相当于

$ convert (20 cm + 2 m) (m)
= 2.2 m

这仅在定义了从cmm的转换时才有效。每个程序会话中加载的prelude模块已经内置了许多有用的单位和转换。

定义转换

要定义两个单位之间的转换,请使用conversion函数。

conversion函数接受要转换的单位和用于将第一个参数转换为第二个参数的函数。这种转换定义是单向的。不会自动定义等价关系。

$ conversion cm m (\x = x / 100)

实现说明

基本上对任何给定的表达式执行beta归约。由于语言是惰性的,许多高级表达式可以在不显式要求在每个地方使用lambda的情况下得到支持(见下文的if)。一切,包括单位转换,都必须是惰性的,因为许多单位转换通常可以排除。

某些函数将在“语言级别”上定义。语言级别仅仅意味着这些是在运行时可用的不变函数。

语言级别函数的示例

  • operator - 用于定义自定义运算符(见自定义运算符)
  • defineUnit - 用于定义新的单位(见单位),如果单位已存在则不会出错,创建一个带有给定名称的POSTFIX函数,该函数尝试将给定参数解析为此单位
  • convert - 用于给某个数量赋予新的单位,执行转换
  • unitFor - 获取某个数量的单位
  • valueOf - 获取给定数量的原始值(不带单位),技术上只是将转换到单位units
  • conversion - 定义从一个单位到另一个单位的转换,覆盖任何现有的转换(见定义转换)
  • if - 评估给定的布尔表达式,然后根据结果返回其参数之一
    • if <expr> <if_true> <if_false>根据expr的结果返回一个表达式
    • 由于块的工作方式,相当灵活
    • 两个参数都是必需的

依赖关系

~14KB