#programming-language #esolang #control-flow #compiler #flow-control #goto #esoteric

bin+lib penne

Penne 是一种以意大利面为导向的编程语言,它偏好使用goto语句进行流程控制

6 个版本

0.3.4 2023年3月7日
0.3.3 2023年2月27日
0.3.0 2023年1月8日
0.2.1 2022年10月12日

248编程语言

MIT 许可证

450KB
18K SLoC

包含 (WOFF 字体, 28KB) iosevka-fixed-regular-stripped.woff, (WOFF 字体, 28KB) iosevka-fixed-bold-stripped.woff

Penne logo

Penne 编程语言

Latest Crate License Stars GitHub repo size Lines of code

Build Release Tests Coverage Issues

Penne 是一种 怪异编程语言,想象一个世界,在这个世界中,goto 语句没有被排斥,反而成为控制流的主要方法,超过了 for 循环和 switch 语句,最终消除了对 RAII 和面向对象编程的发明需求。通过将现代感应用于goto语句的使用,而不是完全摒弃它,Penne 旨在带来面向意大利面的编程的复兴。

快速尝鲜

Penne 的一般美学灵感来自现代编程语言(特别是 Rust),唯一的例外是标签和goto语句,这些(至少在语法上)来自C语言,以及loop语句。

// Calculate the number of Collatz steps needed to reach 1.
// The Collatz conjecture states that this function always terminates.
fn determine_collatz_number(start: i32) -> i32
{
	var x = start;
	var steps = 0;
	{
		if x == 1
			goto return;
		do_collatz_step(&x);
		steps = steps + 1;
		loop;
	}

	return: steps
}

// If x is even, divide it by 2. Otherwise calculate 3 * x + 1.
// Do this without division or modulo operators (for demonstrative purposes).
fn do_collatz_step(x: &i32)
{
	var y = x;
	{
		if y == 0
		{
			if y + y == x
			{
				x = y;
				goto end;
			}
			y = y + 1;
			loop;
		}
		else if y == 1
		{
			x = 3 * x + 1;
			goto end;
		}
		y = y - 2;
		loop;
	}
	end:
}

用法

编译器使用LLVM作为其后端。它需要安装LLVM版本6.0或更高版本。

# Install it (requires Rust 1.60.0 or newer):
cargo install penne

# Compile a source file:
penne examples/addition.pn

# Or run it directly (using lli):
penne run examples/addition.pn
# Output: 10

有适用于Ubuntu 20.04的预编译的二进制文件

语言特性

Penne 更多独特语言特性的简要概述

作用域goto语句

在 Penne 中,goto 是一个局部单向前跳。这是通过给标签赋予反向作用域来实现的:类似于变量在声明之前不能被引用,标签在声明后不能被跳转。

fn foo() -> i32
{
	var x = 0;
	goto end;
	x = 10; // This line is not executed.
	end:
	x = x + 1;
	return: x
}

作用域循环语句

返回的唯一方式是使用loop语句。

fn foo() -> i32
{
	var x = 0;

	{
		x = x + 1;
		loop;
	}

	// This line is never reached.
	return: x
}

视图

函数参数,如数组和结构体,以视图形式传递。对于数组而言,这意味着创建了一个数组视图(或“切片”)并将其传递到函数中。数组视图会记住它们数组的长度,可以使用长度操作来访问,如下所示:|x|

fn foo()
{
	var data: [4]i32 = [1, 2, 3, 4];
	var total = sum(data);
}

fn sum(x: []i32) -> i32
{
	var total = 0;
	var i = 0;
	{
		if i == |x|
			goto return;
		total = total + x[i];
		i = i + 1;
		loop;
	}
	return: total
}

引用指针

引用指针允许函数修改其参数,但要求调用者显式传递一个地址。

fn foo()
{
	var data: [4]i32 = [1, 2, 3, 4];
	set_to_zero(&data);
}

fn set_to_zero(x: &[]i32)
{
	var i = 0;
	{
		if i == |x|
			goto end;
		x[i] = 0;
		i = i + 1;
		loop;
	}
	end:
}

与其他大多数语言中的指针不同,引用指针(包括指向指针的指针)会自动解引用到其基类型,任何不是引用指针的类型。

	var x: i32 = 17;
	var a: &i32 = &x;
	var b: &&i32 = &&a;
	var y: i32 = b;
	b = 30;
	// Now x == 30 and y == 17.

要更改引用指针所指向的值,需要显式修改地址。

	var x: i32 = 17;
	var y: i32 = 30;
	var z: i32 = 88;
	var a: &i32 = &x;
	&a = &y;
	// Now a points to y instead of x.
	var b: &i32 = &z;
	&a = &b;
	// Now a and b both point to z.

结构体和词

与数组类似,使用struct关键字声明的结构体类型隐式地以视图形式传递,不能用作函数的返回值。使用word8word16word32word64word128声明的固定大小结构体会按值传递。

导入

import关键字用于将标记为pub的所有函数签名、结构和常量从源文件导入到目标文件。导入本身不是公开的,因此不会被重新导入。

C语言互操作性

标记为extern的函数使用C ABI,这意味着可以从由LLVM编译的C代码中调用它们。反之,声明一个如下的函数头

    extern fn foo(buffer: []u8, length: usize);

允许您从Penne代码中调用C函数。与其他使用或支持C ABI的编程语言(如C++、Rust、Zig或WebAssembly)进行交互也是可能的。

extern函数的签名中,只允许使用数组视图、指针和原始类型i8i16i32i64u8u16u32u64usize。在extern函数中,数组视图对应于C中的(const)指针,没有长度(|x|),且不能为空。在Penne的未来版本中,指针也将假定不为空,并且必须使用“可选”类型来标记可空指针。

结构和常量也可以声明为extern,但截至v0.3.0,这没有效果。

非特性

Penne是一种专门的语言,不是通用或系统编程语言。一些我们认为对于2023年的好编程语言来说是必不可少的现代特性被省略了。这要么是因为包括它们会与Penne的前提相矛盾(见上文),要么是为了简化其实现。因此,以下明确不是Penne的特性:

  • 类;
  • 泛型;
  • 迭代器;
  • 支持大于64位的指针;
  • 保证是UTF-8的字符串类型;
  • 任何类型的内存安全。

文档

有关语言特性和错误代码的详细参考可在网站上找到。

贡献

笔锋及其编译器仍在开发中,许多语言特性(枚举、模块)尚未实现。然而,我的意图是在功能缺失时引发适当的错误消息,直到它们被实现。如果您遇到编译器段错误或恐慌,或者编译器生成无效的LLVM IR,提供最小可复现示例报告问题将非常感激。

许可证

本库由Sander在't Veld创建。根据LICENSE.txt中指定的MIT许可证提供给您。

软件按“原样”提供,不提供任何形式的保证,无论是明示的还是隐含的,包括但不限于适销性、特定用途的适用性和非侵权性保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任负责,无论是基于合同、侵权或其他方式,是否由软件、软件的使用或其他交易引起、源自或与此有关。

依赖关系

~4–14MB
~180K SLoC