A language that is highly inspired by Golang's Goroutines and Channels.
It will be a compiled, garbage collected language implemented in Zig.
It is primarily a personal project to learn how to implement concurrency models on top of threads, as well as how to make and design a toy language from scratch.
Non-exhaustive list of some of the biggest and most important features planned:
- Easy, Colorless concurrency model
- Gargabe Collection
- Byte-Code VM shipped in the final executable
- Extendable VM (through Zig Modules)
- Type-Safe
Take a look at the example/
directory. There you can find src/main.flow
as a
basic starting point to get familiar with the syntax of flow. Additionally, you
can look at the different test cases in tests/cases/
to get a feel of how some
of the stuff behaves.
If you are using nix
, you can start an ad-hoc shell environment including
flowc
with this command:
nix shell gitlab:flowlang/flowlang
Otherwise, you need to install Flow from source, by cloning this Repository and
running zig build
. After that, the compiler is located at
zig-out/bin/compiler
.
Flow supports extensions for the runtime and compiler. That means, you can write Zig Modules to implement performance critical tasks and export them to be globally available functions in Flow-Land. For an example on how to write an extension yourself, you can look at betterAdd.
You can fetch the module via Zig's package manager:
zig fetch --save git+https://gitlab.com/flowlang/betteradd
You also need a Zig file, where you re-export all the functions you need. That
way, you can also import a Module, but only use a single function, and the rest
of the Module will not be included in the compiled output. The exports.zig
of
the example looks simply like this:
const lib = @import("betterAdd");
pub const betterAdd = lib.betterAdd;
This exports.zig
includes all modules exports the functions that are needed.
You can define different names for these functions in here as well, to avoid
naming collisions.
Here is the build.zig
to build the compiler with the extension enabled.
const std = @import("std");
const flow = @import("flow");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const betterAdd = b.dependency("betterAdd", .{});
const flow_dep = b.dependency("flow", .{});
const compiler = flow.buildCompiler(b, flow_dep.builder, .{
.target = target,
.optimize = optimize,
.extensions = .{
.modules = &.{
.{ .name = "betterAdd", .module = betterAdd.module("betterAdd") },
},
.exports_file = b.path("exports.zig"),
},
});
b.installArtifact(compiler);
}
To add more extensions, you just have to add them as a dependency via zig fetch
,
re-export the functions you want to use and add another entry in the .modules
array.
- bool
- int
- float
- string
- array
- struct (later)
- channel (later)
- Digits/Numbers (
1
,1234
,12.34
) - String Literals (
"String"
) true
/false
null
// arithmetic
+, -, *, /
// Comparison
==, <=, >=, !=
// Logical
and, or
// Unary
!, -
// String concat
.
// shorthands for assignment
+=
-=
*=
/=
%=
.=
var x: int = 1;
var y: int;
var z = 1;
const a: string = "hello";
const b: string;
const c = "world";
Blocks:
{
// This is a block
}
Conditionals:
if condition block
---
if condition block
else block
Loops:
for initializer;condition;continue-expr block
// shorthand for just a condition
for condition block
// infinite loop
for block
Functions:
func identifier(age: int, decimal: float, name: string, flag: bool, chn: int channel) returntype block
---
// closure / anonymous function
func(parameters) use (closure-values) returntype block
---
// method on a struct
meth identifier(parameters) returntype block
Channel:
channel id: int;
---
// (chn is a channel "object")
// reading
chn -> variable
// writing
chn <- value
flow call()
flow {
a = 1;
}
err
is a keyword, which itself is the value of the current error in a catch block. It can also be
used to create or reference an Error
func() catch block
try func()
// this is the same as:
func() catch return err
//Errors must be specified in the return type with an Exclamation Mark (`!`)
func thisMayFail() !void block
//To create an error:
var myError = err.MyErrorName
if myError == err.MyErrorName block
Byte code is split into 3 simple sections:
- constants
- top-level functions
- main code