Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Lys, a language that compiles to WebAssembly.
Read more about it in this blog post.
make watch
and then I run the tests in other terminal with make snapshot
For the time being I'll use npm to distribute the language.
npm i -g lys
Create a folder and a file main.lys
import support::env
#[export]
fun test(): void = {
support::test::START("This is a test suite")
printf("Hello %X", 0xDEADBEEF)
support::test::mustEqual(3 as u8, 3 as u16, "assertion name")
support::test::END()
}
Run lys main.lys --test --wast
. It will create main.wasm
main.wast
and will run the exported function named test
.
struct Vector3(x: f32, y: f32, z: f32)
impl Vector3 {
fun -(lhs: Vector3, rhs: Vector3): Vector3 =
Vector3(
lhs.x - rhs.x,
lhs.y - rhs.y,
lhs.z - rhs.z
)
#[getter]
fun length(this: Vector3): f32 =
f32.sqrt(
this.x * this.x +
this.y * this.y +
this.z * this.z
)
}
fun distance(from: Vector3, to: Vector3): f32 = {
(from - to).length
}
// this snippet is an actual unit test
import support::test
enum Color {
Red
Green
Blue
Custom(r: i32, g: i32, b: i32)
}
fun isRed(color: Color): boolean = {
match color {
case is Red -> true
case is Custom(r, g, b) -> r == 255 && g == 0 && b == 0
else -> false
}
}
#[export]
fun main(): void = {
mustEqual(isRed(Red), true, "isRed(Red)")
mustEqual(isRed(Green), false, "isRed(Green)")
mustEqual(isRed(Blue), false, "isRed(Blue)")
mustEqual(isRed(Custom(255,0,0)), true, "isRed(Custom(255,0,0))")
mustEqual(isRed(Custom(0,1,3)), false, "isRed(Custom(0,1,3))")
mustEqual(isRed(Custom(255,1,3)), false, "isRed(Custom(255,1,3))")
}
// this snippet is an actual unit test
enum Tree {
Node(value: i32, left: Tree, right: Tree)
Empty
}
fun sum(arg: Tree): i32 = {
match arg {
case is Empty -> 0
case is Node(value, left, right) -> value + sum(left) + sum(right)
}
}
#[export]
fun main(): void = {
val tree = Node(42, Node(3, Empty, Empty), Empty)
support::test::mustEqual(sum(tree), 45, "sum(tree) returns 45")
}
The compiler only knows how to emit functions and how to link function names. I did that so I had fewer things hardcoded into the compiler and allows me to write the language in the language.
To do that, I had to add either a %wasm { ... }
code block, and a %stack { ... }
type.
%wasm { ... }
: can only be used as a function body, not as an expression. It is literally the code that will be emited to WAST. The parameter names remain the same (prefixed with $
as WAST indicates). Other symbols can be resolved with fully::qualified::names
.
%stack { wasm="i32", size=4 }
: it is a type literal, it indicates how much memory should be allocated in structs (size
) and what type to use in locals and function parameters (wasm
, it needs a better name).
/** We first define the type `int` */
type int = %stack { wasm="i32", size=4 }
/** Implement some operators for the type `int` */
impl int {
fun +(lhs: int, rhs: int): int = %wasm {
(i32.add (local.get $lhs) (local.get $rhs))
}
fun -(lhs: int, rhs: int): int = %wasm {
(i32.sub (local.get $lhs) (local.get $rhs))
}
fun >(lhs: int, rhs: int): boolean = %wasm {
(i32.gt_s (local.get $lhs) (local.get $rhs))
}
}
fun fibo(n: int, x1: int, x2: int): int = {
if (n > 0) {
fibo(n - 1, x2, x1 + x2)
} else {
x1
}
}
#[export "fibonacci"] // "fibonacci" is the name of the exported function
fun fib(n: int): int = fibo(n, 0, 1)
enum Tree {
Node(value: i32, left: Tree, right: Tree)
Empty
}
Is the sugar syntax for
type Tree = Node | Empty
struct Node(value: i32, left: Tree, right: Tree)
struct Empty()
impl Tree {
fun is(lhs: Tree): boolean = lhs is Node || lhs is Empty
// ...
}
impl Node {
fun as(lhs: Node): Tree = %wasm { (local.get $lhs) }
// ... many methods were removed for clarity ..
}
impl Empty {
fun as(lhs: Node): Tree = %wasm { (local.get $lhs) }
// ...
}
is
and as
operators are just functionsimpl u8 {
/**
* Given an expression with the shape:
*
* something as Type
* ^^^^^^^^^ ^^^^
* $lhs $rhs
*
* A function with the signature:
* fun as($lhs: LHSType): $rhs = ???
*
* Will be searched in the impl of LHSType
*
*/
fun as(lhs: u8): f32 = %wasm { (f32.convert_i32_u (local.get $lhs)) }
}
fun byteAsFloat(value: u8): f32 = value as f32
struct CustomColor(rgb: i32)
type Red = void
impl Red {
fun is(lhs: CustomColor): boolean = match lhs {
case is Custom(rgb) -> (rgb & 0xFF0000) == 0xFF0000
else -> false
}
}
var x = CustomColor(0xFF0000) is Red
// this may not be a good thing, but you get the idea
The struct
keyword is only a high level construct that creats a type and base implementation of something that behaves like a data type, normally in the heap.
struct Node(value: i32, left: Tree, right: Tree)
Is the sugar syntax for
// We need to keep the name and order of the fields for deconstructors
type Node = %struct { value, left, right }
impl Node {
fun as(lhs: Node): Tree = %wasm {
(local.get $lhs)
}
#[explicit]
fun as(lhs: Node): ref = %wasm {
(local.get $lhs)
}
// the discriminant is the type number assigned by the compiler
#[inline]
private fun Node$discriminant(): u64 = {
val discriminant: u32 = Node.^discriminant
discriminant as u64 << 32
}
// this is the function that gets called when Node is used as a function call
fun apply(value: i32, left: Tree, right: Tree): Node = {
// a pointer is allocated. Then using the function `fromPointer` it is converted
// to a valid Node reference
var $ref = fromPointer(system::core::memory::calloc(1 as u32, Node.^allocationSize))
property$0($ref, value)
property$1($ref, left)
property$2($ref, right)
$ref
}
// this function converts a raw address into a valid Node type
private fun fromPointer(ptr: u32): Node = %wasm {
(i64.or (call Node$discriminant) (i64.extend_i32_u (local.get $ptr)))
}
fun ==(a: Node, b: Node): boolean = %wasm {
(i64.eq (local.get $a) (local.get $b))
}
fun !=(a: Node, b: Node): boolean = %wasm {
(i64.ne (local.get $a) (local.get $b))
}
#[getter]
fun value(self: Node): i32 = property$0(self)
#[setter]
fun value(self: Node, value: i32): void = property$0(self, value)
#[inline]
private fun property$0(self: Node): i32 = i32.load(self, Node.^property$0_offset)
#[inline]
private fun property$0(self: Node, value: i32): void = i32.store(self, value, Node.^property$0_offset)
#[getter]
fun left(self: Node): Tree = property$1(self)
#[setter]
fun left(self: Node, value: Tree): void = property$1(self, value)
#[inline]
private fun property$1(self: Node): Tree = Tree.load(self, Node.^property$1_offset)
#[inline]
private fun property$1(self: Node, value: Tree): void = Tree.store(self, value, Node.^property$1_offset)
#[getter]
fun right(self: Node): Tree = property$2(self)
#[setter]
fun right(self: Node, value: Tree): void = property$2(self, value)
#[inline]
private fun property$2(self: Node): Tree = Tree.load(self, Node.^property$2_offset)
#[inline]
private fun property$2(self: Node, value: Tree): void = Tree.store(self, value, Node.^property$2_offset)
fun is(a: (Node | ref)): boolean = %wasm {
(i64.eq (i64.and (i64.const 0xffffffff00000000) (local.get $a)) (call Node$discriminant))
}
fun store(lhs: ref, rhs: Node, offset: u32): void = %wasm {
(i64.store (i32.add (local.get $offset) (call addressFromRef (local.get $lhs))) (local.get $rhs))
}
fun load(lhs: ref, offset: u32): Node = %wasm {
(i64.load (i32.add (local.get $offset) (call addressFromRef (local.get $lhs))))
}
}
FAQs
Unknown package
The npm package lys receives a total of 6 weekly downloads. As such, lys popularity was classified as not popular.
We found that lys demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.