use crate::parser::ast::{PExpr, Opcode, TypeExpr, Pattern};
use crate::parser::span::{Spanned, SpPExpr, SpTypeExpr, SpPattern};
use std::str::FromStr;
use std::collections::VecDeque;
use lalrpop_util::ParseError;
grammar;
// -------------------- Utils --------------------
// A comma separated list with minimum one element and optional trailing comma
Comma<T>: Vec<T> = {
<t: T> <mut v: ("," <T>)*> ","? => {
v.insert(0, t);
v
}
}
FieldItem<T>: (String, T) = {
<i: LowerId> ":" <e: T> => (i, e)
};
Fields<T>: Vec<(String, T)> = {
"{}" => Vec::new(),
"{" <Comma<FieldItem<T>>> "}"
};
Span<T>: Spanned<T> = {
<l: @L> <term: T> <r: @R> => Spanned { span: (l, r), term: Box::new(term) },
}
// -------------------- Identifiers --------------------
LowerId: String = {
<s: r"[a-z][A-Za-z0-9'_]*"> => s.to_string()
}
UpperId: String = {
<s: r"[A-Z][A-Za-z0-9'_]*"> => s.to_string()
}
// -------------------- Literals --------------------
Bool: bool = {
"true" => true,
"false" => false,
};
Int: i64 = {
r"-?[0-9]+" =>? i64::from_str(<>)
.map_err(|_| ParseError::User {
error: "Integer overflow"
})
};
Float: f64 = {
r"-?[0-9]+\.[0-9]*" =>? f64::from_str(<>)
.map_err(|e| ParseError::User {
error: "Float overflow"
})
};
String: String = {
<s: r#""(\\"|[^"])*""#> => {
let mut chars = s.chars();
chars.next();
chars.next_back();
let s = chars.as_str();
let s = s.replace(r#"\\\""#, r#"\""#);
let s = s.replace("\\n", "\n");
s
}
};
Unit: () = {
r"\(\)" => (),
};
// -------------------- Types --------------------
// Presented with lowest precedence at the top
FunctionType: TypeExpr = {
<SpPrefixType> "->" <SpType> => TypeExpr::TEFunction(<>),
PrefixType,
};
PrefixType: TypeExpr = {
"@" <SpPrefixType> => TypeExpr::TEDelay(<>),
"#" <SpPrefixType> => TypeExpr::TEStable(<>),
TypeTerm,
};
SpPrefixType: SpTypeExpr = Span<PrefixType>;
// Minimum of 2 elements
TupleType: TypeExpr = {
"(" <t: SpType> "," <mut v: Comma<SpType>> ")" => {
v.insert(0, t);
TypeExpr::TETuple(v)
}
};
// e.g. IntEvent = Stream<Option<int>>
GenericArgs: Vec<SpTypeExpr> = {
"<" <Comma<SpType>> ">"
};
UserType: TypeExpr = {
<v: (<LowerId> "::")*> <t: UpperId> => TypeExpr::TEUser(v, t, Vec::new()),
<v: (<LowerId> "::")*> <t: UpperId> <g: GenericArgs> => TypeExpr::TEUser(v, t, g),
};
TypeTerm: TypeExpr = {
"bool" => TypeExpr::TEBool,
"int" => TypeExpr::TEInt,
"float" => TypeExpr::TEFloat,
"string" => TypeExpr::TEString,
Unit => TypeExpr::TEUnit,
TupleType,
"[" <SpType> "]" => TypeExpr::TEList(<>),
UserType,
"(" <FunctionType> ")",
};
pub SpType: SpTypeExpr = Span<FunctionType>;
// -------------------- Operators --------------------
// Grouped by Precedence
CompOp: Opcode = {
"==" => Opcode::Eq,
"<" => Opcode::Lt,
">" => Opcode::Gt,
};
ConsStreamOp: Opcode = {
":" => Opcode::Cons,
"<<" => Opcode::Stream,
};
AddSubOp: Opcode = {
"+" => Opcode::Add,
"-" => Opcode::Sub,
};
MulDivModOp: Opcode = {
"*" => Opcode::Mul,
"/" => Opcode::Div,
"%" => Opcode::Mod,
};
PrefixOp: Opcode = {
"~" => Opcode::Neg,
"!" => Opcode::Not,
"@" => Opcode::Delay,
"#" => Opcode::Stable,
"!@" => Opcode::Adv,
"!#" => Opcode::Unbox,
"out" => Opcode::Out,
"into" => Opcode::Into,
};
// -------------------- Operator Expressions --------------------
// Lowest precedence at the top
// Right associative
OrExpr: PExpr = {
<e1: SpAndExpr> "||" <e2: SpExpr> => PExpr::BinOp(e1, Opcode::Or, e2),
AndExpr
};
// Right associative
AndExpr: PExpr = {
<e1: SpComp> "&&" <e2: SpAndExpr> => PExpr::BinOp(e1, Opcode::And, e2),
Comp
};
SpAndExpr: SpPExpr = Span<AndExpr>;
// Left associative
Comp: PExpr = {
SpComp CompOp SpConsStream => PExpr::BinOp(<>),
ConsStream
};
SpComp: SpPExpr = Span<Comp>;
// Right associative
ConsStream: PExpr = {
SpAddSub ConsStreamOp SpConsStream => PExpr::BinOp(<>),
AddSub
};
SpConsStream: SpPExpr = Span<ConsStream>;
// Left associative
AddSub: PExpr = {
SpAddSub AddSubOp SpMulDivMod => PExpr::BinOp(<>),
MulDivMod
};
SpAddSub: SpPExpr = Span<AddSub>;
// Left associative
MulDivMod: PExpr = {
SpMulDivMod MulDivModOp SpVariantStructExpr => PExpr::BinOp(<>),
VariantStructExpr
};
SpMulDivMod: SpPExpr = Span<MulDivMod>;
VariantStructExpr: PExpr = {
<mut v: (<LowerId> "::")*> <t: UpperId> "::" <s: UpperId> <e: SpVariantStructExpr> => {
v.push(t);
PExpr::Variant(v, s, Some(e)) // e.g. Some (64)
},
<mut v: (<LowerId> "::")*> <t: UpperId> "::" <s: UpperId> => {
v.push(t);
PExpr::Variant(v, s, None)
},
<v: (<LowerId> "::")*> <s: UpperId> <f: Fields<SpExpr>> => PExpr::StructExpr(v, s, f), // Circle {pos: (0, 0), r: 10 }
AppExpr,
}
SpVariantStructExpr: SpPExpr = Span<VariantStructExpr>;
// Left associative
AppExpr: PExpr = {
<SpAppExpr> <SpDotExpr> => PExpr::App(<>),
DotExpr,
};
SpAppExpr: SpPExpr = Span<AppExpr>;
// Left associative
DotExpr: PExpr = {
<SpDotExpr> "." <LowerId> => PExpr::ProjStruct(<>),
Prefix
};
SpDotExpr: SpPExpr = Span<DotExpr>;
Prefix: PExpr = {
PrefixOp SpPrefix => PExpr::UnOp(<>),
Term,
};
SpPrefix: SpPExpr = Span<Prefix>;
// -------------------- Terms --------------------
// The simplest expressions
ListTerm: PExpr = {
"[]" => PExpr::List(VecDeque::new()),
"[" <v: Comma<SpExpr>> "]" => PExpr::List(VecDeque::from(v)),
};
TupleTerm: PExpr = {
"(" <e: SpExpr> "," <mut v: Comma<SpExpr>> ")" => {
v.insert(0, e);
PExpr::Tuple(v)
}
};
VarTerm: PExpr = {
<p: (<LowerId> "::")*> <v: LowerId> => {
PExpr::Var(p, v)
},
}
Term: PExpr = {
Bool => PExpr::Bool(<>),
Int => PExpr::Int(<>),
Float => PExpr::Float(<>),
String => PExpr::String(<>),
Unit => PExpr::Unit,
ListTerm,
TupleTerm,
VarTerm,
"(" <Expr> ")",
};
// -------------------- Patterns --------------------
// Lowest precedence at the top
// Left associative
OrPattern: Pattern = {
<SpPattern> "|" <SpConsPattern> => Pattern::Or(<>),
ConsPattern,
};
// Right associative
ConsPattern: Pattern = {
<Span<PatternTerm>> ":" <SpConsPattern> => Pattern::Cons(<>),
<Span<PatternTerm>> "<<" <SpConsPattern> => Pattern::Stream(<>),
PatternTerm,
};
SpConsPattern: SpPattern = Span<ConsPattern>;
ListPattern: Pattern = {
"[]" => Pattern::List(Vec::new()),
"[" <v: Comma<SpPattern>> "]" => Pattern::List(v)
}
TuplePattern: Pattern = {
"(" <e: SpPattern> "," <mut v: Comma<SpPattern>> ")" => {
v.insert(0, e);
Pattern::Tuple(v)
}
};
StructPatternItem: (String, Option<SpPattern>) = {
<i: LowerId> ":" <p: SpPattern> => (i, Some(p)),
<i: LowerId> => (i, None), // Bind field value to field name
".." => ("..".to_string(), None) // Ignore rest of the fields
};
StructPattern: Pattern = {
<p: (<LowerId> "::")*> <s: UpperId> "{}" => Pattern::Struct(p, s, Vec::new()),
<p: (<LowerId> "::")*> <s: UpperId> "{" <v: Comma<StructPatternItem>> "}" => Pattern::Struct(p, s, v),
};
PatternTerm: Pattern = {
"_" => Pattern::Underscore,
Bool => Pattern::Bool(<>),
Int => Pattern::Int(<>),
Float => Pattern::Float(<>),
String => Pattern::String(<>),
Unit => Pattern::Unit,
<mut v: (<LowerId> "::")*> <t: UpperId> "::" <s: UpperId> <p: Span<PatternTerm>> => {
v.push(t);
Pattern::Variant(v, s, Some(p))
},
<mut v: (<LowerId> "::")*> <t: UpperId> "::" <s: UpperId> => {
v.push(t);
Pattern::Variant(v, s, None)
},
TuplePattern,
ListPattern,
StructPattern,
<LowerId> => Pattern::Var(<>, false),
"(" <OrPattern> ")",
}
SpPattern: SpPattern = Span<OrPattern>;
LetPattern: Pattern = {
<Span<LetPatternTerm>> "<<" <Span<LetPattern>> => Pattern::Stream(<>),
<Span<LetPatternTerm>> ":" <Span<LetPattern>> => Pattern::Cons(<>),
LetPatternTerm,
}
LetPatternTerm: Pattern = {
<LowerId> => Pattern::Var(<>, false),
"_" => Pattern::Underscore,
"(" <p: Span<LetPattern>> "," <mut v: Comma<Span<LetPattern>>> ")" => {
v.insert(0, p);
Pattern::Tuple(v)
},
"(" <LetPattern> ")",
}
// -------------------- Expressions --------------------
BlockTerm: PExpr = {
"let" <p: Span<LetPattern>> "=" <e: SpExpr> ";" => PExpr::Let(p, e),
<e: Expr> ";" => e
};
BlockExpr: PExpr = {
"{" <mut v: Span<BlockTerm>*> <r: SpExpr> "}" => {
v.push(r);
PExpr::Block(v)
}
};
IfExpr: PExpr = {
"if" <e1: SpExpr> "then" <e2: SpExpr> "else" <e3: SpExpr> => PExpr::If(e1, e2, e3)
};
FnArgOp: Pattern = {
<Span<FnArg>> "<<" <Span<FnArgOp>> => Pattern::Stream(<>),
FnArg,
}
FnArg: Pattern = {
"_" => Pattern::Underscore,
Unit => Pattern::Unit,
<LowerId> => Pattern::Var(<>, false),
"#" <LowerId> => Pattern::Var(<>, true), // Mark arguments that are stable
"(" <p: Span<FnArg>> "," <mut v: Comma<Span<FnArg>>> ")" => {
v.insert(0, p);
Pattern::Tuple(v)
},
"(" <FnArgOp> ")",
}
FnExpr: PExpr = {
"fn" <v: Span<FnArgOp>+> "->" <e: SpExpr> => PExpr::Fn(v, e),
};
MatchItem: (SpPattern, SpPExpr) = {
<SpPattern> "=>" <SpExpr> => (<>),
};
MatchExpr: PExpr = {
"match" <e: SpExpr> "with" "{" <v: Comma<MatchItem>> "}" => PExpr::Match(e, v)
};
pub Expr: PExpr = {
OrExpr,
BlockExpr,
IfExpr,
FnExpr,
MatchExpr,
};
SpExpr: SpPExpr = Span<Expr>;
// -------------------- Top-Level Expressions --------------------
LetExpr: PExpr = {
"let" <p: Span<LetPattern>> "=" <e: SpExpr> => PExpr::Let(p, e),
};
GenericParams: Vec<String> = {
"<" <Comma<UpperId>> ">"
};
TypeExpr: PExpr = {
"type" <i: UpperId> "=" <t: SpType> => PExpr::TypeDef(i, Vec::new(), t),
"type" <i: UpperId> <v: GenericParams> "=" <t: SpType> => PExpr::TypeDef(i, v, t),
};
VariantItem: (String, Option<SpTypeExpr>) = {
<i: UpperId> <t: SpType?> => (i, t),
};
StructExpr: PExpr = {
"struct" <i: UpperId> <f: Fields<SpType>> => PExpr::StructDef(i, Vec::new(), f),
"struct" <i: UpperId> <p: GenericParams> <f: Fields<SpType>> => PExpr::StructDef(i, p, f),
};
EnumExpr: PExpr = {
"enum" <i: UpperId> "{" <v: Comma<VariantItem>> "}" => PExpr::EnumDef(i, Vec::new(), v),
"enum" <i: UpperId> <p: GenericParams> "{" <v: Comma<VariantItem>> "}" =>
PExpr::EnumDef(i, p, v),
}
LetWithAnd: PExpr = {
"let" <p1: Span<LetPattern>> "=" <e1: SpExpr> "and" <p2: Span<LetPattern>> "=" <e2: SpExpr> "with" <e3: SpExpr> =>
PExpr::LetAndWith(p1, e1, p2, e2, e3),
};
UseExpr: PExpr = {
"use" <mut v: (<LowerId> "::")+> <t: LowerId> => {
v.push(t);
PExpr::Use(v, false)
},
"use" <mut v: (<LowerId> "::")+> <t: UpperId> => {
v.push(t);
PExpr::Use(v, true)
},
"use" <mut v: (<LowerId> "::")+> "*" => {
v.push("*".to_string());
PExpr::Use(v, false)
},
}
pub Item: PExpr = {
LetExpr,
TypeExpr,
EnumExpr,
StructExpr,
LetWithAnd,
UseExpr,
}
pub Program: Vec<SpPExpr> = {
<Span<Item>*>
}