// Adapted from LALRPOP book examples
use std::str::FromStr;
use crate::ast::*;
use crate::external_operators::OperatorChars;
use std::collections::HashMap;
use super::string_escapes::process_string;
use lalrpop_util::ParseError;
grammar<'ast>(state: &'ast ParserState);
extern {
type Error = (usize, String, usize);
}
// Helpers
Comma<T>: Vec<T> = {
<mut v:(<T> ",")*> <e:T?> => match e {
None => v,
Some(e) => {
v.push(e);
v
}
}
};
// Name strings,
pub Name: String = {
<s:r"[A-Za-z][A-Za-z0-9_\-]*"> => s.to_string()
};
// Full Program
pub Program: Program = {
<mut t:Program> <f:Function> => {t.env.insert(f.0, f.1); t},
<f: Function> => Program { env: HashMap::from([(f.0, f.1)]) }
};
// Function name
pub FunctionNameString: String = {
"#" <s:Name> => s
};
// Funciton
pub Function: (String, Vec<Pattern>) = {
<n: FunctionNameString> <p: Patterns> => (n, p),
};
// Patterns for a function
Patterns: Vec<Pattern> = {
<mut ps: Patterns> <p: Pattern> => { ps.push(p); ps },
<p: Pattern> => vec![p],
};
// Singular pattern
pub Pattern: Pattern = {
<e:Expr> ";" => Pattern {start: Expr::tuple(0, vec![], 0), result: e, guards: Vec::new()},
<n:Expr> "->" <e:Expr> ";" => Pattern {start: n, result: e, guards: Vec::new()},
<n:Expr> "->" <e:Expr> <g:Guards> ";" => Pattern {start: n, result: e, guards: g},
};
// Guards for a pattern
Guards: Vec<Guard> = {
<mut gs: Guards> <g: Guard> => { gs.push(g); gs },
<g: Guard> => vec![g],
};
// Singular guard
pub Guard: Guard = {
"|" <n:Expr> => Guard {expr: n}
};
// Non interpolated strings
pub StringTerm: String = {
<l:@L> <s:r#""[^"]*""#> <r:@R> =>? process_string(&s[1..s.len()-1])
.map_err(|s| ParseError::User {error: (l, s, r)}),
};
// Interpolated strings initial part
StringInt: Vec<InterpolationPart> = {
<l:@L> <s: r#"f"([^\{\}\\"]|\\\{|\\\}|\\\\|\\")*"f"#> <r: @R> =>? Ok(
vec![InterpolationPart::String(process_string(&s[2..s.len()-2])
.map_err(|s| ParseError::User {error: (l, s, r)})?)]),
<l1:@L> <s1: r#"f"([^\{\}\\"]|\\\{|\\\}|\\\\|\\")*\{"#> <r1: @R>
<mut si: StringIntMid> <l2: @L>
<s2: r#"\}([^\{\}\\"]|\\\{|\\\}|\\\\|\\")*"f"#> <r2: @R> =>? Ok({
si.insert(0, InterpolationPart::String(
process_string(&s1[2..s1.len()-1])
.map_err(|s| ParseError::User {error: (l1, s, r1)})?
));
si.push(InterpolationPart::String(
process_string(&s2[1..s2.len()-2])
.map_err(|s| ParseError::User {error: (l2, s, r2)})?
));
si
}),
};
// Central sections within an interpolation string
StringIntMid: Vec<InterpolationPart> = {
Expr => vec![InterpolationPart::Expr(<>)],
<mut si: StringIntMid> <l: @L>
<s: r#"\}([^\{\}\\"]|\\\{|\\\}|\\\\|\\")*\{"#>
<r: @R> <e: Expr> =>? Ok({
si.push(InterpolationPart::String(
process_string(&s[1..s.len()-1])
.map_err(|s| ParseError::User {error: (l, s, r)})?
));
si.push(InterpolationPart::Expr(e));
si
})
};
// Numbers
Num: i32 = {
r"[0-9]+" => i32::from_str(<>).unwrap(),
};
// Toplevel for an expression
// Seperated out so that can change the top level if necessary
pub Expr: Expr = {
LogicExpr
};
// Logic operations
LogicExpr: Expr = {
@L EqualityExpr LogicOp AddSubExpr @R => Expr::op(<>),
EqualityExpr,
};
// Logic operations operator characters
LogicOp: Opcode = {
"&&" => Opcode::And,
"||" => Opcode::Or,
};
// Equality operations
EqualityExpr: Expr = {
@L EqualityExpr EqualityOp AddSubExpr @R => Expr::op(<>),
AddSubExpr,
};
// Equality operations operator characters
EqualityOp: Opcode = {
"==" => Opcode::Eq,
"!=" => Opcode::Neq,
"<=" => Opcode::Leq,
">=" => Opcode::Geq,
"<" => Opcode::Lt,
">" => Opcode::Gt,
};
// Addition and stubtratction operations
AddSubExpr: Expr = {
@L AddSubExpr AddSubExprOp Factor @R => Expr::op(<>),
Factor,
};
// Addition and stubtratction operatior characters
AddSubExprOp: Opcode = {
"+" => Opcode::Add,
"-" => Opcode::Sub,
};
// Multiplication, division and modulo operations
Factor: Expr = {
@L Factor FactorOp CustomBinOp @R => Expr::op(<>),
CustomBinOp,
};
// Multiplication, division and modulo operator charactes
FactorOp: Opcode = {
"*" => Opcode::Mul,
"/" => Opcode::Div,
"%" => Opcode::Mod,
};
// Unary operations
UnaryOp: UnaryOp = {
"!" => UnaryOp::Not,
"-" => UnaryOp::Neg,
};
//------------------------
// Custom operators
//------------------------
CustomOperator: OperatorChars = {
"@" => OperatorChars::At,
"&" => OperatorChars::And,
"?" => OperatorChars::QuestionMark,
"ยง" => OperatorChars::Section,
"$" => OperatorChars::Dollar,
"\\" => OperatorChars::Backslash,
"~" => OperatorChars::Tilda,
"^" => OperatorChars::Carat,
}
// Custom defined binary expressions
CustomBinOp: Expr = {
<l:@L> <le:CustomBinOp> <o: CustomOperator> <re:CustomUnaryOp> <r:@R> =>? {
if state.binary_ops.contains(&o) {
Ok(Expr::custom_op(<>))
} else {
Err(ParseError::User { error:
(l, "This binary operator is not defined".to_string(), r)
})
}
},
CustomUnaryOp,
}
// Custom defined unary expressions
CustomUnaryOp: Expr = {
<l:@L> <o: CustomOperator> <e:UnaryExpr> <r:@R> =>? {
if state.unary_ops.contains(&o) {
Ok(Expr::custom_unary(<>))
} else {
Err(ParseError::User { error:
(l, "This unary operator is not defined".to_string(), r)
})
}
},
UnaryExpr,
}
// Built in unary expressions
UnaryExpr: Expr = {
@L UnaryOp CallTerm @R => Expr::unary(<>),
CallTerm,
}
// Function
CallTerm: Expr = {
<l: @L> <e:CallTerm> "(" <l1:@L> <c: Comma<Expr>> <r1:@R> ")" <r:@R> =>
Expr::func_call(l, e, Expr::tuple(l1, c, r1), r),
Term,
}
// Other types of expression
// Underscore (for pattern matching underscore)
// Number, unary operator, tuples (and bracketed expressions), strings
// Lambda expressions
Term: Expr = {
<l: @L> <m:"_"> <r:@R> => Expr::var(l, "_".to_string(), r),
@L Name @R => Expr::var(<>),
@L Num @R => Expr::number(<>),
<l:@L> "(" <v: Comma<Expr>> ")" <r:@R> => {
if v.len() == 1 {
v[0].clone()
} else {
Expr::tuple(<>)
}
},
@L StringTerm @R => Expr::string(<>),
@L StringInt @R => Expr::interpolation_string(<>),
<l:@L> "|" <l1:@L> <s: Comma<Expr>> <r1:@L> "=>" <e: Expr> "|" <r:@R> =>
Expr::lambda(l, Pattern {
start: Expr::tuple(l1, s, r1), result: e, guards: vec![]
}, r),
};
// Strings for the lexxer to ignore
match {
r"\s*" => { }, // Skip any white space character strings
r"//[^\n\r]*[\n\r]*" => { }, // Skip `// comments`
r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/" => { }, // Skip `/* comments */`
_
}