src Language
src is a domain specific language for manipulating source code and building, progressively distiributed apps or PDA.
It draws a lot of inspiration from Effekt and Koka languages.
src is main aim is to provide a gradually distributed programming
environment for building software.
It tries to achive these goals by providing a thin veneer over the operating systems libc or equivalent by treating the syscalls to the operating system as effects.
Therefore the operating system becomes the effect handler for the execution environment.
use { host } from std
effect Make: async + throws + execs + reads + writes {
catch() [throws]
await<T>(f: Future<T>) [async, throws] -> T
exec(arg0: string, args: stringvec) [Make] -> i32
}
struct Local {
host: host
}
impl Make for Local {
fn catch(self) [throws] {
}
fn await<T>(f: Future<T>) [async, trhows] -> T {
yield()
}
fn exec(self, arg0: string, args: vec<string>) [Vm] -> i32 {
self.host.read("jobserver").await
if self.host.exec(arg0, args) {
raise(1)
}
}
}
Acknowledgements
Building upon the incredible work of the Rust community and many others, src would not be possible without the following projects:
Language
Specification
use super::ast::*;
use lalrpop_util::ErrorRecovery;
use crate::lexer::{Token, Location};
use crate::lexer::Word;
use crate::Db;
use super::span::Spanned;
use crate::span;
#[LALR]
grammar<'input, 'err>(errors: &'err mut Vec<ErrorRecovery<Location, Token<'input>, &'static str>>, db: &dyn Db);
extern {
type Location = Location;
enum Token<'input> {
// Operators
"|" => Token::Pipe, // |
"&" => Token::Ampersand, // &
";" => Token::Semicolon, // ;
"=" => Token::Equals, // =
// Redirections
"<" => Token::LessThan, // <
">" => Token::GreaterThan, // >
// Identifiers
// "param" => Variable::Parameter(<&'input str>), // var
// "param_default" => Variable::ParameterDefault(<&'input str>, <&'input str>), // var = value
// "positional_param" => Variable::PositionalParameter(<usize>), // $var
// Literals
"true" => Token::Word(Word::True), // true
"none" => Token::Word(Word::None), // none
"false" => Token::Word(Word::False), // false
"null" => Token::Word(Word::Null), // null
"fn" => Token::Word(Word::Fn), // fn
"if" => Token::Word(Word::If), // if
"else" => Token::Word(Word::Else), // else
"match" => Token::Word(Word::Match), // match
"let" => Token::Word(Word::Let), // let
"import" => Token::Word(Word::Import), // import
"action" => Token::Word(Word::Action), // action
"struct" => Token::Word(Word::Struct), // struct
"enum" => Token::Word(Word::Enum), // enum
"effect" => Token::Word(Word::Effect), // trait
"impl" => Token::Word(Word::Impl), // impl
"when" => Token::Word(Word::When), // when
"use" => Token::Word(Word::Use), // use
"from" => Token::Word(Word::From), // from
"where" => Token::Word(Word::Where), // where
"self" => Token::Word(Word::Self_), // self
"for" => Token::Word(Word::For), // for
"pub" => Token::Word(Word::Pub), // pub
"priv" => Token::Word(Word::Priv), // priv
"#!" => Token::Shebang, // #!
"ident" => Token::Word(Word::Ident(<&'input str>)), // a-z, A-Z, 0-9, _
"string" => Token::String(<&'input str>), // "..."
// Comments
"comment" => Token::Comment(<&'input str>), // #
// Numbers
"int" => Token::Integer(<i64>), // 0-9
"float" => Token::Float(<f64>), // [0-9]*.0-9+
// Special
"eof" => Token::Eof, // EOF
"\n" => Token::NewLine, // \n
"(" => Token::LeftParen, // (
")" => Token::RightParen, // )
"{" => Token::LeftBrace, // {
"}" => Token::RightBrace, // }
"[" => Token::LeftBracket, // [
"]" => Token::RightBracket, // ]
"," => Token::Comma, // ,
":" => Token::Colon, // :
"." => Token::Dot, // .
"-" => Token::Minus, // -
"+" => Token::Plus, // +
"/" => Token::Divide, // /
"*" => Token::Multiply, // *
"%" => Token::Percent, // %
"$" => Token::Dollar, // $
"!" => Token::Exclamation, // !
"?" => Token::Question, // ?
"~" => Token::Tilde, // ~
"@" => Token::At, // @
"^" => Token::Caret, // ^
"->" => Token::Arrow, // ->
"=>" => Token::FatArrow, // =>
}
}
#[inline]
Span<T>: Spanned<T> = {
<@L> <T> <@R> => span!(<>)
};
#[inline]
Lines<T>: Vec<T> = {
<mut v:(<T> "\n")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
}
#[inline]
Comma<T>: Vec<T> = { // (0)
<mut v:(<T> ",")*> <e:T?> => match e { // (1)
None=> v,
Some(e) => {
v.push(e);
v
}
}
};
#[inline]
Plus<T>: Vec<T> = {
<mut v:(<T> "+")*> <e:T?> => match e { // (1)
None=> v,
Some(e) => {
v.push(e);
v
}
}
};
#[inline]
Block<T>: Block<T> = {
"{" ("\n"?) <lines:Lines<T>> "}" => Block(lines)
};
// Keywords
None: Spanned<Keyword> = <lo:@L> "none" <hi:@R> => span!(lo, Keyword::None, hi);
Fn: Spanned<Keyword> = <lo:@L> "fn" <hi:@R> => span!(lo, Keyword::Fn, hi);
Effect: Spanned<Keyword> = <lo:@L> "effect" <hi:@R> => span!(lo, Keyword::Effect, hi);
Struct: Spanned<Keyword> = <lo:@L> "struct" <hi:@R> => span!(lo, Keyword::Struct, hi);
If: Spanned<Keyword> = <lo:@L> "if" <hi:@R> => span!(lo, Keyword::If, hi);
Else: Spanned<Keyword> = <lo:@L> "else" <hi:@R> => span!(lo, Keyword::Else, hi);
When: Spanned<Keyword> = <lo:@L> "when" <hi:@R> => span!(lo, Keyword::When, hi);
Use: Spanned<Keyword> = <lo:@L> "use" <hi:@R> => span!(lo, Keyword::Use, hi);
From: Spanned<Keyword> = <lo:@L> "from" <hi:@R> => span!(lo, Keyword::From, hi);
Impl: Spanned<Keyword> = <lo:@L> "impl" <hi:@R> => span!(lo, Keyword::Impl, hi);
Let: Spanned<Keyword> = <lo:@L> "let" <hi:@R> => span!(lo, Keyword::Let, hi);
True: Node = "true" => Node::Bool(true);
False: Node = "false" => Node::Bool(false);
KeywordAndVisibility<T>: KeywordAndVisibility = {
<v:Visibility> <k:T> => KeywordAndVisibility(k, v),
};
Ident: Spanned<Ident> = {
<l:@L> <i:"ident"> <r:@R> => span!(l,Ident(i.to_string(), None),r),
};
IdentWithGenerics: Spanned<Ident> = {
<l:@L> <i:"ident"> "<" <g:Comma<Ident>> ">" <r:@R> => span!(l,Ident(i.to_string(), Some(g)),r),
};
IdentOrIdentWithGenerics: Spanned<Ident> = {
<i:Ident> => i,
<i:IdentWithGenerics> => i,
<l:@L> "self" <r:@R> => span!(l, Ident("self".to_string(), None),r),
};
Punctuated<T, Token>: Vec<T> = {
<mut v:(<T> <Token>)*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};
Atom: Spanned<Value> = {
#[precedence(level="0")]
<l:@L> <i:"int"> <r:@R> => span!(l, Value::Literal(Literal::Integer(i)),r),
<l:@L> <f:"float"> <r:@R>=> span!(l, Value::Literal(Literal::Float(f)),r),
<l:@L> <s:"string"> <r:@R> => {
let start = 1;
let end = s.len() - 1;
span!(l, Value::Literal(Literal::String(s.get(start..end).expect(format!("malformed string {s}, strings must be quoted").as_str()).to_string())), r)
},
#[precedence(level="1")]
<i:Ident> => span!(i.0, Value::Ident(i.1), i.2),
};
String: Spanned<Node> = {
<l:@L> <s:"string"> <r:@R> => {
let start = 1;
let end = s.len() - 1;
span!(l, Node::String(s.get(start..end).expect(format!("malformed string {s}, strings must be quoted").as_str()).to_string()),r)
},
};
Expression: Spanned<Node> = {
#[precedence(level="1")]
Term,
#[precedence(level="2")] #[assoc(side="left")]
<lhs:Expression> "*" <rhs:Expression> => {
let (l, r) = (lhs.2, rhs.0);
span!(l,Node::BinaryExpression(BinaryOperation {
lhs: Box::new(lhs),
op: Operator::Mul,
rhs: Box::new(rhs)
}),r)
},
<l:@L> <lhs:Expression> "/" <rhs:Expression> <r:@R> => {
let (l, r) = (lhs.2, rhs.0);
span!(l,Node::BinaryExpression(BinaryOperation {
lhs: Box::new(lhs),
op: Operator::Div,
rhs: Box::new(rhs)
}),r)
},
#[precedence(level="3")] #[assoc(side="left")]
<lhs:Expression> "+" <rhs:Expression> => {
let (l, r) = (lhs.2, rhs.0);
span!(l,Node::BinaryExpression(BinaryOperation {
lhs: Box::new(lhs),
op: Operator::Add,
rhs: Box::new(rhs)
}),r)
},
<lhs:Expression> "-" <rhs:Expression> => {
let (l, r) = (lhs.2, rhs.0);
span!(l, Node::BinaryExpression(BinaryOperation {
lhs: Box::new(lhs),
op: Operator::Sub,
rhs: Box::new(rhs)
}),r)
},
};
FnCall: Spanned<Node> = {
<name:IdentOrIdentWithGenerics> "(" <args:Comma<Expression>> ")" <r:@R> => span!(name.0, Node::FnCall(FnCall(name, args)) ,r),
};
Term: Spanned<Node> = {
<s:Span<String>> => s.1,
<l:@L> <val:"int"> <r:@R> => span!(l, Node::Integer(val),r),
<i:Ident> => {
let (l, r) = (i.0, i.2);
span!(l, Node::Ident(i), r)},
<f:FnCall> => <>,
<l:@L> <true_:True> <r:@R> => span!(l,true_, r),
<l:@L> <false_:False> <r:@R> => span!(l,false_, r),
"(" <Expression> ")",
};
IdentOrIdentWithGenericsOrFnCall: Spanned<Node> = {
<i:IdentOrIdentWithGenerics> => {
let (l, r) = (i.0, i.2);
span!(l, Node::Ident(i), r)
},
<f:FnCall> => f,
};
FieldAccess: Spanned<Node> = {
<lhs:FieldAccess> "." <rhs:IdentOrIdentWithGenericsOrFnCall> => {
let (l, r) = (lhs.0, rhs.2);
span!(l,Node::FieldAccess(FieldAccess(Box::new(lhs), Box::new(rhs))),r)
},
<lhs:IdentOrIdentWithGenericsOrFnCall> => lhs,
};
Field: Spanned<FieldDef> = {
<vis:Visibility> <name:Ident> ":" <ty:IdentOrIdentWithGenerics> => {
let (l, r) = (name.0, ty.2);
span!(l, FieldDef(vis, name, ty), r)
}
};
TypeParameters: Spanned<Vec<Spanned<Ident>>> =
<l:@L> "<" <is:Comma<Ident>> ">" <r:@R> => span!(l,is ,r);
FnArg: Spanned<FnArg> = {
<self_:Span<"self">> => span!(self_.0, FnArg::Reciever, self_.2),
<field:Span<Field>> => {
let (l, r) = (field.0, field.2);
span!(l, FnArg::Field(field.1), r)
},
};
Prototype: Spanned<Prototype> = {
<l:@L> <name:IdentOrIdentWithGenerics> "("<args:Comma<FnArg>> ")" "[" <effects:Comma<Ident>> "]" <ret:("->" Ident)?> <r:@R> => {
let ret = match ret {
None => None,
Some(r) => Some(r.1),
};
span!(l, Prototype{name, args, ret, effects},r)
}
};
Statement: Spanned<Node> = {
<l:@L> Let <name:Ident> "=" <value:Expression> <r:@R> => span!(l, Node::Binding(Binding(name, Box::new(value))),r),
<IfDef> => <>,
FieldAccess => <>,
};
Visibility: Spanned<Visibility> = {
<l:@L> "pub" <r:@R> => span!(l, Visibility::Public, r),
<l:@L> "priv" <r:@R> => span!(l, Visibility::Private, r),
// elided
<l:@L> () <r:@R> => span!(l, Visibility::Private, r),
};
WhenBlock: (Spanned<Ident>,Block<Spanned<Node>>) = {
<l:@L> When <i:Ident> <lines:Block<Statement>> <r:@R> => (i, lines)
};
FnDef: Spanned<Node> = {
<l:@L> <kwv:KeywordAndVisibility<Fn>> <proto:Prototype> <block:Block<Statement>> <handlers:(WhenBlock)*> <r:@R> => span!(l, Node::FnDef(FnDef(kwv,proto, block, handlers)), r),
};
EffectDef: Spanned<Node> = {
<l:@L> <kwv:KeywordAndVisibility<Effect>> <i:Ident> ":" <effects:Plus<Ident>> <block:Block<Prototype>> <r:@R> => span!(l,Node::EffectDef(EffectDef(kwv, i, effects,block)), r),
};
StructDef: Spanned<Node> = {
<l:@L> <kwv:KeywordAndVisibility<Struct>> <i:Ident> <fields:Block<Field>> <r:@R> => span!(l, Node::StructDef(StructDef(kwv, i, fields)),r),
};
IfDef: Spanned<Node> = {
<l:@L> If <cl:@L><cond:Statement><cr:@R> <if_:Block<Statement>> <r:@R> => {
let branch = BranchDef (
Box::new(cond),
vec![
(span!(l, Node::Bool(true), cl), if_),
]
);
span!(l, Node::Branch(branch), r)
},
<l:@L> If <cl:@L> <cond:Statement> <cr:@R> <if_:Block<Statement>> <el:@L> Else <er:@R> <else_:Block<Statement>> <r:@R> => {
let branch = BranchDef (
Box::new(cond),
vec![
(span!(l,Node::Bool(true),cl), if_),
(span!(el, Node::Bool(false), er), else_),
]
);
span!(l, Node::Branch(branch), r)
},
};
UseDef: Spanned<Node> = {
<l:@L> <kwv:KeywordAndVisibility<Use>> "{" <imports:Comma<Ident>> "}" From <i:Ident> <r:@R> => {
span!(l, Node::UseDef(UseDef(kwv,imports, i)), r)
},
};
ImplDef: Spanned<Node> = {
<l:@L> <kwv:KeywordAndVisibility<Impl>> <i:Ident> <t:("for" Ident)?> <lines:Block<FnDef>> <r:@R> => span!(l,Node::ImplDef(ImplDef(kwv, i, t.map(|t| t.1), lines)),r),
};
TopLevel: Spanned<Node> = {
<FnDef> => <>,
<EffectDef> => <>,
<StructDef> => <>,
<UseDef> => <>,
<ImplDef> => <>,
};
pub Source: Module = {
<expr:("\n"* TopLevel)*> => Module(expr.into_iter().map(|e| e.1).collect()),
! => {
errors.push(<>);
Module(vec![])
}
};
Examples
innitguv
use { native_fs, native_exec } from host
use { fs } from std
struct Innitguv {
fs: native_fs,
exec: native_exec
current_pid: i32
}
impl Exec for Innitguv {
fn exec(&self, arg0: str, args: vec<str>) [nd, exec, await] -> i32 {
let path = arg0
let pid = self.exec.exec(path, args)
if pid == -1 {
return -1
}
self.current_pid = pid
yield()
}
}
impl Actor for Innitguv {
fn recv(&self, msg: Message) [recv, await] {
self.exec(msg.path, msg.args)
}
}