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>: Spanned<KeywordAndVisibility> = {
<lo:@L> <v:Visibility> <k:T> <hi:@R> => span!(lo, KeywordAndVisibility(k, v), hi),
};
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>> <r:@R> => span!(l, Node::FnDef(FnDef(kwv,proto, block)), 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![])
}
};