use core::option::Option::None;
use detached_str::Str;
use std::{
collections::{BTreeMap, BTreeSet, HashMap},
rc::Rc,
};
use crate::{
Diagnostic, Expression, Int, MAX_SYNTAX_RECURSION, SyntaxErrorKind, Token, TokenKind,
expression::{CatchType, ChainCall, DestructurePattern, FileSize},
tokens::{Input, Tokens},
utils::{unescape_bytes, unescape_str},
};
use detached_str::StrSlice;
use nom::{IResult, branch::alt, combinator::*, multi::*, sequence::*};
const PREC_ASSIGN: u8 = 1; const PREC_REDIRECT: u8 = 2; const PREC_PIPE: u8 = 2; const PREC_CATCH: u8 = 5;
const PREC_LAMBDA: u8 = 6; const PREC_CONDITIONAL: u8 = 7; const PREC_LOGICAL_OR: u8 = 8; const PREC_LOGICAL_AND: u8 = 9; const PREC_COMPARISON: u8 = 10;
const PREC_CMD_ARG: u8 = 11;
const PREC_FUNC_ARG: u8 = 4;
const PREC_ADD_SUB: u8 = 15; const PREC_MUL_DIV: u8 = 16; const PREC_POWER: u8 = 17; const PREC_CUSTOM: u8 = 18; const PREC_RANGE: u8 = 30; const PREC_UNARY: u8 = 31; const PREC_INDEX: u8 = 35; const PREC_GROUP: u8 = 38;
const PREC_LITERAL: u8 = 39;
#[derive(Debug)]
struct OperatorInfo<'a> {
symbol: &'a str,
precedence: u8,
right_associative: bool,
}
impl<'a> OperatorInfo<'a> {
#[inline]
fn new(symbol: &'a str, precedence: u8, right_associative: bool) -> Self {
Self {
symbol,
precedence,
right_associative,
}
}
}
fn parse_expr(input: Tokens) -> IResult<Tokens, Expression, SyntaxErrorKind> {
let (input, got) = PrattParser::parse_expr_with_precedence(input, 0, 0)?;
Ok((input, got))
}
fn parse_expr_or_failure(
input: Tokens<'_>,
min_prec: u8,
depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
cut(|input| PrattParser::parse_expr_with_precedence(input, min_prec, depth))(input)
}
struct PrattParser;
impl PrattParser {
fn parse_expr_with_precedence(
mut input: Tokens<'_>,
min_prec: u8,
mut depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (new_input, mut lhs) = Self::parse_prefix(input, min_prec, depth)?;
input = new_input;
loop {
depth += 1;
if MAX_SYNTAX_RECURSION.with(|v| depth > *v.borrow()) {
return Err(nom::Err::Failure(SyntaxErrorKind::RecursionDepth {
input: input.get_str_slice(),
depth,
}));
}
if input.is_empty() {
break;
}
let operator_token = input.first().unwrap();
let operator = operator_token.text(input);
match operator_token.kind {
TokenKind::LineBreak => {
break;
}
TokenKind::OperatorInfix => {
match operator {
"::" => {
input = input.skip_n(1);
match lhs {
Expression::Symbol(name) => {
let (new_input, mut modes) = many0(terminated( parse_symbol_string,text("::")))(input)?;
input=new_input;
modes.insert(0,name);
let (new_input, rhs) = Self::parse_prefix(input, PREC_INDEX,depth)?;
input = new_input;
lhs= Expression::ModuleCall(modes, Rc::new(rhs)) ;
}
_ => {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol",
Some(format!("{lhs:?}")),
Some("module name should be a symbol"),
))
}
}
},
"..." | "...=" | ".." | "..=" => {
if PREC_RANGE < min_prec{
break;
}
input = input.skip_n(1);
let (new_input, rhs) = Self::parse_prefix(input, PREC_RANGE,depth)?;
input = new_input;
let (nnew_input, exprs) = opt(preceded(
text(":"),
cut(alt((parse_symbol, parse_integer,parse_variable))),
))(input)?;
input = nnew_input;
lhs = Expression::RangeOp(
operator.into(),
Rc::new(lhs),
Rc::new(rhs),
exprs.map(Rc::new),
)
}
_ => unreachable!(),
}
}
TokenKind::Operator => {
let op_info = match Self::get_operator_info(operator) {
Some(opi) => opi,
None => break,
};
if op_info.precedence < min_prec {
break; }
let next_min_prec = if op_info.right_associative {
op_info.precedence
} else {
op_info.precedence + 1
};
input = input.skip_n(1);
match operator {
"?." | "?+" | "??" |"?>" | "?!" | "_!" | "?~" => {
lhs = Self::build_catch_unary(op_info, lhs)?
}
opx if opx.starts_with("__") => {
lhs = Expression::UnaryOp(opx.into(), Rc::new(lhs), false);
}
_ => {
if input.is_empty() {
break;
}
let (new_input, rhs) = parse_expr_or_failure(input, next_min_prec, depth+1).map_err(|e| match e {
nom::Err::Incomplete(_) | nom::Err::Error(_) => SyntaxErrorKind::failure(
input.get_str_slice(),
"some expression",
None,
Some("check grammar of right handside"),
),
other => other,
})?;
input = new_input;
lhs = Self::build_bin_ast(input, op_info, lhs, rhs)?;
}
}
}
TokenKind::OperatorPostfix => {
(input, lhs) = Self::build_postfix_ast(lhs, operator.to_string(), input,depth)?;
}
TokenKind::Symbol
| TokenKind::StringLiteral
| TokenKind::StringRaw
| TokenKind::StringTemplate
| TokenKind::StringSafe
| TokenKind::Bytes
| TokenKind::IntegerLiteral
| TokenKind::FloatLiteral
| TokenKind::ValueSymbol
| TokenKind::OperatorPrefix | TokenKind::Punctuation | TokenKind::Regex
| TokenKind::Time
| TokenKind::Keyword if min_prec < PREC_CMD_ARG =>
{
if operator_token.kind == TokenKind::Punctuation && !matches!(operator,"("|"["|"H{"|"M{"|"S{") {
break;
}
match &lhs {
Expression::Symbol(_)|Expression::SymbolRaw(_)
|Expression::Variable(_)| Expression::String(_)
|Expression::Index(.. ) | Expression::Property(..)=>{}
Expression::Integer(_) | Expression::Float(_) | Expression::Range(.. )
| Expression::List(_)| Expression::BSet(_)
| Expression::Map(_)| Expression::HMap(_)
| Expression::RegexDef(_) | Expression::TimeDef(_) | Expression::FileSize(_)
| Expression::Bytes(_) => {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"operator or separator",
Some(format!("{operator}")),
Some("numbers/data cannot be followed by symbols without an operator"),
));
}
_ => break
}
if input.len() == 1 {
let (new_input, rhs) =
Self::parse_expr_with_precedence(input, PREC_CMD_ARG, depth + 1)?;
input = new_input;
lhs = Expression::Command(Rc::new(lhs), Rc::new(vec![rhs]));
} else {
let (new_input, rhs) = cut(many0(|input| {
Self::parse_expr_with_precedence(input, PREC_CMD_ARG, depth+1)
}))(input)?;
input = new_input;
lhs = Expression::Command(Rc::new(lhs), Rc::new(rhs));
}
}
_ => {
break;
}
}
}
Ok((input, lhs))
}
fn parse_prefix(
input: Tokens<'_>,
min_prec: u8,
depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
SyntaxErrorKind::empty_back(input)?;
let first = input.first().unwrap();
match first.kind {
TokenKind::OperatorPrefix => {
let op = first.text(input);
match op {
"$" => cut(parse_variable)(input),
"." => cut(parse_pipe_method)(input),
"@" => cut(parse_fn_declare)(input),
"-" | "+" if min_prec == PREC_CMD_ARG => {
let input = input.skip_n(1);
let (input, arg) = cut(alt((
parse_symbol_string,
map(kind(TokenKind::IntegerLiteral), |t| {
t.to_str(input.str).to_string()
}),
)))(input)?;
Ok((input, Expression::String(format!("{}{}", op, arg))))
}
"!" | "-" => {
let prec = PREC_UNARY;
if prec < min_prec {
return Err(nom::Err::Error(SyntaxErrorKind::PrecedenceTooLow(
input.get_str_slice(),
)));
}
let input = input.skip_n(1);
let (input, expr) = parse_expr_or_failure(input, prec, depth + 1)?;
Ok((input, Expression::UnaryOp(op.into(), Rc::new(expr), true)))
}
".." | "..=" => {
let input = input.skip_n(1);
let (input, rhs) = Self::parse_prefix(input, PREC_RANGE, depth + 1)?;
let (input, steps) = opt(preceded(
text(":"),
cut(alt((parse_symbol, parse_integer, parse_variable))),
))(input)?;
Ok((
input,
Expression::RangeOp(
op.to_string(),
Rc::new(Expression::Blank),
Rc::new(rhs),
steps.map(Rc::new),
),
))
}
":" => {
let input = input.skip_n(1);
let (input, steps) =
cut(alt((parse_symbol, parse_integer, parse_variable)))(input)?;
Ok((
input,
Expression::RangeOp(
"..".to_string(),
Rc::new(Expression::Blank),
Rc::new(Expression::Blank),
Some(Rc::new(steps)),
),
))
}
_ => {
return Err(nom::Err::Failure(SyntaxErrorKind::UnknownOperator(
op.to_string(),
input.get_str_slice(),
)));
}
}
}
TokenKind::Symbol => parse_symbol(input),
TokenKind::StringLiteral if PREC_LITERAL >= min_prec => parse_string(input),
TokenKind::StringRaw if PREC_LITERAL >= min_prec => parse_string_raw(input),
TokenKind::StringTemplate if PREC_LITERAL >= min_prec => parse_string_template(input),
TokenKind::StringSafe if PREC_LITERAL >= min_prec => parse_string_safe(input),
TokenKind::Bytes if PREC_LITERAL >= min_prec => parse_bytes(input),
TokenKind::IntegerLiteral if PREC_LITERAL >= min_prec => parse_integer(input),
TokenKind::FloatLiteral if PREC_LITERAL >= min_prec => parse_float(input),
TokenKind::Radix2 | TokenKind::Radix8 | TokenKind::Radix16
if PREC_LITERAL >= min_prec =>
{
parse_radix(first.kind)(input)
}
TokenKind::ValueSymbol if PREC_LITERAL >= min_prec => parse_value_symbol(input),
TokenKind::Regex if PREC_LITERAL >= min_prec => parse_regex(input),
TokenKind::Time if PREC_LITERAL >= min_prec => parse_time(input),
TokenKind::Punctuation if PREC_GROUP >= min_prec => {
let op = first.text(input);
match op {
"(" => {
if has_lambda_arrow(input) {
cut(parse_lambda_param)(input)
} else {
cut(parse_group)(input)
}
}
"[" => {
cut(parse_list)(input)
}
"H{" => cut(parse_hashmap)(input),
"M{" => cut(parse_bmap)(input),
"S{" => cut(parse_bset)(input),
"%{" => cut(parse_domain_block)(input),
"{" => {
if looks_like_map(input) {
cut(parse_map)(input) } else {
cut(parse_block)(input)
}
}
_ => Err(nom::Err::Error(SyntaxErrorKind::UnknownOperator(
op.to_string(),
input.get_str_slice(),
))), }
}
TokenKind::Keyword if min_prec == PREC_CMD_ARG => map(kind(TokenKind::Keyword), |t| {
Expression::Symbol(t.to_str(input.str).to_string())
})(input), TokenKind::Keyword => parse_control_flow(input),
TokenKind::LineBreak => Err(nom::Err::Error(SyntaxErrorKind::CustomError(
"line ended too early".to_string(),
input.get_str_slice(),
))),
TokenKind::OperatorInfix | TokenKind::OperatorPostfix => {
Err(nom::Err::Error(SyntaxErrorKind::UnExpectedToken(
first.text(input).to_string(),
input.get_str_slice(),
)))
}
TokenKind::Operator => Err(nom::Err::Error(SyntaxErrorKind::UnExpectedToken(
first.text(input).to_string(),
input.get_str_slice(),
))),
_ => Err(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("Unexpected token kind: {:?}", first.kind),
input.get_str_slice(),
))),
}
}
fn build_postfix_ast(
lhs: Expression,
op: String,
input: Tokens<'_>,
depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
match op.as_str() {
"(" => {
let (input, args) = parse_args(input, depth)?;
Ok((input, Expression::Apply(Rc::new(lhs), Rc::new(args))))
}
"." => parse_chaind_call_or_property(input, lhs, depth),
"!" => {
let (input, args) = many0(|inp| {
PrattParser::parse_expr_with_precedence(inp, PREC_CMD_ARG, depth + 1)
})(input.skip_n(1))?;
Ok((input, Expression::Apply(Rc::new(lhs), Rc::new(args))))
}
"^" => match lhs {
Expression::Symbol(s) => Ok((input.skip_n(1), Expression::SymbolRaw(s))),
_ => Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol",
Some(format!("{lhs:?}")),
Some("only symbol need to skip eval"),
)),
},
"[" => {
parse_index(lhs, input, depth)
}
".." => {
let (input, steps) = opt(preceded(
text(":"),
cut(alt((parse_symbol, parse_integer, parse_variable))),
))(input.skip_n(1))?;
Ok((
input,
Expression::RangeOp(
op,
Rc::new(lhs),
Rc::new(Expression::Blank),
steps.map(Rc::new),
),
))
}
"K" | "M" | "G" | "T" | "P" | "B" => {
let fs = match lhs {
Expression::Integer(s) => FileSize::from(s as u64, &op),
Expression::Float(s) => FileSize::from_float(s, &op),
_ => FileSize::from_bytes(0),
};
Ok((input.skip_n(1), Expression::FileSize(fs)))
}
"%" => {
let f = match lhs {
Expression::Integer(s) => s as f64,
Expression::Float(s) => s,
_ => 0.0,
};
Ok((input.skip_n(1), Expression::Float(f / 100.0)))
}
_ => Err(nom::Err::Error(SyntaxErrorKind::UnknownOperator(
op.to_string(),
input.get_str_slice(),
))),
}
}
fn get_operator_info(op: &str) -> Option<OperatorInfo<'_>> {
match op {
"=" | ":=" | "+=" | "-=" | "*=" | "/=" => {
Some(OperatorInfo::new(op, PREC_ASSIGN, true))
}
"->" => Some(OperatorInfo::new(op, PREC_LAMBDA, true)),
"+" | "-" => Some(OperatorInfo::new(op, PREC_ADD_SUB, false)),
"*" | "/" | "%" => Some(OperatorInfo::new(op, PREC_MUL_DIV, false)),
"^" => Some(OperatorInfo::new("^", PREC_POWER, true)),
"&&" => Some(OperatorInfo::new("&&", PREC_LOGICAL_AND, false)),
"||" => Some(OperatorInfo::new("||", PREC_LOGICAL_OR, false)),
"==" | "===" | "!=" | "!==" | ">" | "<" | ">=" | "<=" => {
Some(OperatorInfo::new(op, PREC_COMPARISON, false))
}
"~:" | "!~:" => Some(OperatorInfo::new(op, PREC_COMPARISON, false)),
"?" => Some(OperatorInfo::new(
"?",
PREC_CONDITIONAL, true, )),
":" => Some(OperatorInfo::new(
":",
PREC_CONDITIONAL,
true, )),
"|" | "|>" | "|^" => Some(OperatorInfo::new(
op, PREC_PIPE, false,
)),
"<<" | ">>" | ">!" => Some(OperatorInfo::new(op, PREC_REDIRECT, false)),
"?." | "?+" | "??" | "?>" | "?!" | "_!" | "?~" | "?:" | "&:" | "_:" => {
Some(OperatorInfo::new(op, PREC_CATCH, false))
}
opa if opa.starts_with("..+") => Some(OperatorInfo::new(opa, PREC_ADD_SUB, false)),
ops if ops.starts_with("..*") => Some(OperatorInfo::new(ops, PREC_MUL_DIV, false)),
opo if opo.starts_with("..") => Some(OperatorInfo::new(opo, PREC_CUSTOM, false)),
opo if opo.starts_with("__") => Some(OperatorInfo::new(opo, PREC_CUSTOM, false)),
_ => None,
}
}
fn build_bin_ast(
input: Tokens,
op: OperatorInfo,
lhs: Expression,
rhs: Expression,
) -> Result<Expression, nom::Err<SyntaxErrorKind>> {
match op.symbol {
"+" | "-" | "*" | "/" | "%" | "^" => Ok(Expression::BinaryOp(
op.symbol.into(),
Rc::new(lhs),
Rc::new(rhs),
)),
"&&" | "||" => Ok(Expression::BinaryOp(
op.symbol.into(),
Rc::new(lhs),
Rc::new(rhs),
)),
"=" => {
match lhs.to_symbol() {
Ok(name) => Ok(Expression::Assign(name.to_string(), Rc::new(rhs))),
_ => {
Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol",
Some(format!("{lhs:?}")),
Some("only assign to symbol allowed"),
))
}
}
}
"==" | "!=" | ">" | "<" | ">=" | "<=" | "!==" | "===" | "~:" | "!~:" => Ok(
Expression::BinaryOp(op.symbol.into(), Rc::new(lhs), Rc::new(rhs)),
),
"->" => {
let params = match lhs {
Expression::Group(boxed_expr) => match boxed_expr.as_ref() {
Expression::List(elements) => elements
.as_ref()
.iter()
.map(|e| e.to_symbol().map(|s| s.to_string()))
.collect::<Result<Vec<_>, _>>(),
Expression::Symbol(s) => Ok(vec![s.to_owned()]),
_ => {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol in parameter list",
Some(boxed_expr.type_name().to_string()),
"put only valid symbols in lambda param list".into(),
));
} },
Expression::Symbol(name) => Ok(vec![name]),
Expression::Blank => Ok(vec![]),
_ => {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol or parameter list",
Some(lhs.to_string()),
"Lambda requires valid parameter list".into(),
));
}
};
let body = match rhs {
Expression::Block(_) => rhs,
Expression::Sequence(seq) => Expression::Block(Rc::new(seq)),
Expression::Group(boxed_expr) => boxed_expr.as_ref().clone(),
_ => Expression::Block(Rc::new(vec![rhs])),
};
Ok(Expression::Lambda(params.unwrap(), Rc::new(body), None))
}
"?" => {
let (true_expr, false_expr) = match rhs {
Expression::BinaryOp(op, t, f) if op == ":" => (t, f),
_ => {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"conditional values after `?`",
Some(rhs.to_string()),
"add something like `t:f`".into(),
));
}
};
Ok(Expression::If(Rc::new(lhs), true_expr, false_expr))
}
":" => {
Ok(Expression::BinaryOp(":".into(), Rc::new(lhs), Rc::new(rhs)))
}
":=" => {
match lhs.to_symbol() {
Ok(name) => Ok(Expression::Assign(
name.to_string(),
Rc::new(Expression::Quote(Rc::new(rhs))),
)),
_ => {
Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol",
Some(lhs.to_string()),
"only assign to symbol allowed".into(),
))
}
}
}
"+=" | "-=" | "*=" | "/=" => Ok(Expression::BinaryOp(
op.symbol.into(),
Rc::new(lhs),
Rc::new(rhs),
)),
"|" | "|>" | "|^" => Ok(Expression::Pipe(
op.symbol.into(),
Rc::new(lhs),
Rc::new(rhs),
)),
"<<" | ">>" | ">!" => Ok(Expression::Pipe(
op.symbol.into(),
Rc::new(lhs),
Rc::new(rhs),
)),
"?:" => Ok(Expression::Catch(
Rc::new(lhs),
CatchType::OnError,
Some(Rc::new(rhs)),
)),
"&:" => Ok(Expression::Catch(
Rc::new(lhs),
CatchType::OnSuccess,
Some(Rc::new(rhs)),
)),
"_:" => Ok(Expression::Catch(
Rc::new(lhs),
CatchType::OnEmpty,
Some(Rc::new(rhs)),
)),
opx if opx.starts_with("..") => {
Ok(Expression::BinaryOp(opx.into(), Rc::new(lhs), Rc::new(rhs)))
}
_ => {
unreachable!()
}
}
}
fn build_catch_unary(
op: OperatorInfo,
lhs: Expression,
) -> Result<Expression, nom::Err<SyntaxErrorKind>> {
Ok(match op.symbol {
"?." => Expression::Catch(Rc::new(lhs), CatchType::Ignore, None),
"?+" => Expression::Catch(Rc::new(lhs), CatchType::PrintStd, None),
"??" => Expression::Catch(Rc::new(lhs), CatchType::PrintErr, None),
"?>" => Expression::Catch(Rc::new(lhs), CatchType::PrintOver, None),
"?!" => Expression::Catch(Rc::new(lhs), CatchType::TerminateOnErr, None),
"_!" => Expression::Catch(Rc::new(lhs), CatchType::TerminateOnEmpty, None),
"?~" => Expression::Catch(Rc::new(lhs), CatchType::ToBoolean, None),
_ => unreachable!(),
})
}
}
fn parse_control_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let keyword = input
.first()
.map(|t| t.text(input))
.ok_or_else(|| nom::Err::Error(SyntaxErrorKind::NoExpression))?;
match keyword {
"if" => parse_if_flow(input),
"match" => parse_match_flow(input),
"for" => parse_for_flow(input),
"while" => parse_while_flow(input),
"loop" => parse_loop_flow(input),
"break" => parse_break(input),
"continue" => parse_continue(input),
"return" => parse_return(input),
"shift" => parse_shift(input),
_ => Err(nom::Err::Error(SyntaxErrorKind::NoExpression)),
}
}
fn parse_group(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
delimited(
terminated(text("("), opt(kind(TokenKind::LineBreak))),
map(
|inp| {
parse_expr(inp).map_err(|e| match e {
nom::Err::Error(_) => SyntaxErrorKind::failure(
inp.get_str_slice(),
"some expression",
None,
Some("check grammar in this group"),
),
other => other,
})
},
|e| Expression::Group(Rc::new(e)),
),
cut(preceded(opt(kind(TokenKind::LineBreak)), text_close(")"))),
)(input)
}
fn parse_list(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
delimited(
terminated(text("["), opt(kind(TokenKind::LineBreak))),
terminated(
map(
separated_list0(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
|inp| PrattParser::parse_expr_with_precedence(inp, PREC_FUNC_ARG, 0),
),
Expression::from,
),
opt(alt((
map(text(","), |_| {}),
map(kind(TokenKind::LineBreak), |_| {}),
))),
),
cut(text_close("]")),
)(input)
}
fn parse_pipe_method(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text(".")(input)?;
let (input, method_name) = cut(parse_symbol_string)(input)?;
let (input, args_opt) = opt(|input| parse_args(input, 0))(input)?;
match args_opt {
Some(args) => Ok((input, Expression::PipeMethod(method_name, Rc::new(args)))),
_ => Ok((input, Expression::String(format!(".{}", method_name)))),
}
}
fn parse_chaind_call_or_property(
input: Tokens<'_>,
lhs: Expression,
depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, method_name) = cut(parse_symbol_string)(input.skip_n(1))?;
match input.first() {
Some(s) if s.text(input) == "(" => {
let (input, args) = parse_args(input, depth)?;
let chain_call = ChainCall {
method: method_name,
args,
};
match lhs {
Expression::Chain(base, mut calls) => {
calls.push(chain_call);
Ok((input, Expression::Chain(base, calls)))
}
_ => Ok((input, Expression::Chain(Rc::new(lhs), vec![chain_call]))),
}
}
_ => {
Ok((
input,
Expression::Property(Rc::new(lhs), Rc::new(Expression::String(method_name))),
))
}
}
}
fn parse_args(
input: Tokens<'_>,
depth: usize,
) -> IResult<Tokens<'_>, Vec<Expression>, SyntaxErrorKind> {
delimited(
terminated(text("("), opt(kind(TokenKind::LineBreak))),
cut(terminated(
separated_list0(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
|inp| {
PrattParser::parse_expr_with_precedence(inp, PREC_FUNC_ARG, depth + 1)
},
),
opt(kind(TokenKind::LineBreak)),
)),
cut(text_close(")")),
)(input)
}
fn parse_param(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, (String, Option<Expression>), SyntaxErrorKind> {
alt((
map(
separated_pair(
parse_symbol_string,
text("="),
cut(parse_literal),
),
|(name, expr)| (name, Some(expr)), ),
map(parse_symbol_string, |s| (s, None)), ))(input)
}
#[allow(clippy::type_complexity)]
fn parse_param_list(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, (Vec<(String, Option<Expression>)>, Option<String>), SyntaxErrorKind> {
let (input, _) = cut(text("("))(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"params declaration",
None,
Some("declare params like (x,y,z=0)"),
)
})?;
let (input, _) = opt(kind(TokenKind::LineBreak))(input)?; let (input, params) = separated_list0(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
parse_param,
)(input)?;
let (input, param_collector) = opt(preceded(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
preceded(text("*"), parse_symbol_string),
))(input)?;
let (input, _) = cut(text_close(")"))(input)?;
Ok((input, (params, param_collector)))
}
fn parse_lambda_param(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, expr) = delimited(
text("("),
map(separated_list0(text(","), parse_symbol), |symbols| {
Expression::from(symbols)
}),
|input| {
text_close(")")(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"valid lambda parameters",
None,
Some("lambda parameters must be comma-separated symbols: (x, y) -> body"),
)
})
},
)(input)?;
Ok((input, Expression::Group(Rc::new(expr))))
}
fn parse_fn_declare(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = opt(many0(kind(TokenKind::LineBreak)))(input)?;
SyntaxErrorKind::empty_back(input)?;
let (input, decos) = many0(preceded(
text("@"),
terminated(
tuple((parse_symbol_string, opt(|input| parse_args(input, 0)))),
kind(TokenKind::LineBreak),
),
))(input)?;
let (input, _) = text("fn")(input)?;
let (input, name) = cut(parse_symbol_string)(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"function name",
None,
Some("add a name"),
)
})?;
let (input, (params, param_collector)) = cut(parse_param_list)(input)?; let (input, _) = opt(kind(TokenKind::LineBreak))(input)?;
if match input.first() {
Some(&token) if token.text(input).ne("{") => true,
None => true,
_ => false,
} {
return Err(SyntaxErrorKind::failure(
input.get_str_slice(),
"function body",
None,
Some("add a body like {...}"),
));
}
let (input, body) = cut(parse_block)(input)?;
let (input, handler_options) = opt(alt((
map(text("?."), |_| (CatchType::Ignore, None)),
map(text("?+"), |_| (CatchType::PrintStd, None)),
map(text("??"), |_| (CatchType::PrintErr, None)),
map(text("?>"), |_| (CatchType::PrintOver, None)),
map(text("?!"), |_| (CatchType::TerminateOnErr, None)),
map(text("_!"), |_| (CatchType::TerminateOnEmpty, None)),
map(text("?~"), |_| (CatchType::ToBoolean, None)),
map(preceded(text("?:"), cut(parse_expr)), |e| {
(CatchType::OnError, Some(Rc::new(e)))
}),
map(preceded(text("&:"), cut(parse_expr)), |e| {
(CatchType::OnSuccess, Some(Rc::new(e)))
}),
map(preceded(text("_:"), cut(parse_expr)), |e| {
(CatchType::OnEmpty, Some(Rc::new(e)))
}),
)))(input)?;
let last_body = match handler_options {
Some((ctyp, handler)) => Rc::new(Expression::Catch(Rc::new(body), ctyp, handler)),
_ => Rc::new(body),
};
Ok((
input,
Expression::Function(name, params, param_collector, last_body, decos),
))
}
fn parse_return(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("return")(input)?;
let (input, expr) = opt(parse_expr)(input)?;
Ok((
input,
Expression::Return(Rc::new(expr.unwrap_or(Expression::None))),
))
}
fn parse_shift(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("shift")(input)?;
Ok((input, Expression::Shift))
}
fn parse_break(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("break")(input)?;
let (input, expr) = opt(parse_expr)(input)?;
Ok((
input,
Expression::Break(Rc::new(expr.unwrap_or(Expression::None))),
))
}
fn parse_continue(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("continue")(input)?;
Ok((input, Expression::Continue))
}
#[inline]
fn kind(kind: TokenKind) -> impl Fn(Tokens<'_>) -> IResult<Tokens<'_>, StrSlice, SyntaxErrorKind> {
move |input: Tokens<'_>| match input.first() {
Some(&token) if token.kind == kind => Ok((input.skip_n(1), token.range)),
_ => Err(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("expect token kind: {kind:?}"),
input.get_str_slice(),
))),
}
}
#[inline]
fn text<'a>(text: &'a str) -> impl Fn(Tokens<'a>) -> IResult<Tokens<'a>, Token, SyntaxErrorKind> {
move |input: Tokens<'a>| match input.first() {
Some(&token) if token.text(input) == text => Ok((input.skip_n(1), token)),
_ => Err(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("expect {text:?}"),
input.get_str_slice(),
))), }
}
#[inline]
fn text_close<'a>(
text: &'static str,
) -> impl Fn(Tokens<'a>) -> IResult<Tokens<'a>, Token, SyntaxErrorKind> {
move |input: Tokens<'a>| match input.first() {
Some(&token) if token.text(input) == text => Ok((input.skip_n(1), token)),
_ => Err(SyntaxErrorKind::unclosed_delimiter(
input.get_str_slice(),
text,
)),
}
}
#[inline]
fn parse_symbol(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
map(kind(TokenKind::Symbol), |t| {
Expression::Symbol(t.to_str(input.str).to_string())
})(input)
}
#[inline]
fn parse_symbol_string(input: Tokens<'_>) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
map(kind(TokenKind::Symbol), |t| t.to_str(input.str).to_string())(input)
}
#[inline]
fn parse_variable(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
preceded(text("$"), map(parse_symbol_string, Expression::Variable))(input)
}
fn parse_string_literal_inner(
input: Tokens<'_>,
kind: TokenKind,
) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
let token = input
.first()
.filter(|t| t.kind == kind)
.ok_or(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("expect token kind: {:?}", kind),
input.get_str_slice(),
)))?;
let cs = token.text_inner(input);
let r = if token.open_len > 2 {
unescape_str(cs, true)
} else {
unescape_str(cs, false)
};
Ok((input.skip_n(1), r))
}
fn parse_string(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_string_literal_inner(input, TokenKind::StringLiteral)?;
Ok((input, Expression::String(r)))
}
fn parse_strings_via_kind(
input: Tokens<'_>,
kind: TokenKind,
) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
let token = input
.first()
.filter(|t| t.kind == kind)
.ok_or(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("expect token kind: {:?}", kind),
input.get_str_slice(),
)))?;
let cs = token.text_inner(input);
let r = if token.open_len > 2 {
cs.to_string()
} else {
cs.replace("\\'", "'")
.replace("\\\"", "\"")
.replace("\\\\", "\\")
};
Ok((input.skip_n(1), r))
}
fn parse_string_raw_inner(input: Tokens<'_>) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
parse_strings_via_kind(input, TokenKind::StringRaw)
}
fn parse_string_raw(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_string_raw_inner(input)?;
Ok((input, Expression::String(r)))
}
fn parse_regex(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_strings_via_kind(input, TokenKind::Regex)?;
Ok((input, Expression::RegexDef(r)))
}
fn parse_time(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_strings_via_kind(input, TokenKind::Time)?;
Ok((input, Expression::TimeDef(r)))
}
fn parse_string_safe(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_strings_via_kind(input, TokenKind::StringSafe)?;
Ok((input, Expression::StringSafe(r)))
}
fn parse_bytes(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_strings_via_kind(input, TokenKind::Bytes)?;
let r = unescape_bytes(&r);
Ok((input, Expression::Bytes(r)))
}
fn find_matching_brace(s: &str, start: usize) -> Option<usize> {
let mut depth = 1usize;
let mut i = start;
for ch in s[start..].chars() {
match ch {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
_ => {}
}
i += ch.len_utf8();
}
None
}
fn parse_brace_segment(template: &str, start: usize, prefix: &str) -> Option<(Expression, usize)> {
let end = find_matching_brace(template, start)?;
let inner = &template[start..end];
let expr = match parse_script(inner) {
Ok(expr) => expr,
Err(_) => Expression::String(format!("{prefix}{{{inner}}}")),
};
Some((expr, end + 1)) }
fn split_template_segments(template: &str) -> Vec<Expression> {
let mut segments: Vec<Expression> = Vec::new();
let mut literal = String::new();
let mut i = 0usize;
while i < template.len() {
let c = match template[i..].chars().next() {
Some(c) => c,
None => break,
};
let char_len = c.len_utf8();
match c {
'\\' => {
if let Some(next) = template[i + char_len..].chars().next() {
match next {
'{' | '$' | '}' => {
literal.push(next);
i += char_len + next.len_utf8();
}
_ => {
literal.push('\\');
i += char_len;
}
}
} else {
literal.push('\\');
i += char_len;
}
}
'$' => {
let rest = &template[i + char_len..];
if let Some(next) = rest.chars().next() {
if next == '{' {
let start = i + char_len + next.len_utf8();
if let Some((expr, next_i)) = parse_brace_segment(template, start, "$") {
if !literal.is_empty() {
segments.push(Expression::String(std::mem::take(&mut literal)));
}
segments.push(expr);
i = next_i;
} else {
literal.push('$');
i += char_len;
}
} else if next.is_alphanumeric() || next == '_' {
if !literal.is_empty() {
segments.push(Expression::String(std::mem::take(&mut literal)));
}
let var_start = i + char_len;
let mut var_end = var_start;
for ch in template[var_start..].chars() {
if ch.is_alphanumeric() || ch == '_' || ch == '.' || ch == '-' {
var_end += ch.len_utf8();
} else {
break;
}
}
segments.push(Expression::Variable(
template[var_start..var_end].to_string(),
));
i = var_end;
} else {
literal.push('$');
i += char_len;
}
}
}
'{' => {
let start = i + char_len;
if let Some((expr, next_i)) = parse_brace_segment(template, start, "") {
if !literal.is_empty() {
segments.push(Expression::String(std::mem::take(&mut literal)));
}
segments.push(expr);
i = next_i;
} else {
literal.push('{');
i += char_len;
}
}
_ => {
literal.push(c);
i += char_len;
}
}
}
if !literal.is_empty() {
segments.push(Expression::String(literal));
}
segments
}
fn parse_string_template(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, r) = parse_string_literal_inner(input, TokenKind::StringTemplate)?;
let segments = split_template_segments(&r);
Ok((input, Expression::StringTemplate(segments)))
}
fn parse_literal(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
alt((
parse_integer,
parse_float,
parse_string,
parse_string_raw,
parse_string_template,
parse_value_symbol,
parse_regex,
parse_time,
parse_radix(TokenKind::Radix2),
parse_radix(TokenKind::Radix8),
parse_radix(TokenKind::Radix16),
))(input)
}
fn parse_set_inner<'a>(input: Tokens<'a>) -> IResult<Tokens<'a>, Vec<Expression>, SyntaxErrorKind> {
let (input, _) = terminated(text("S{"), opt(kind(TokenKind::LineBreak)))(input)?;
let (input, pairs) = separated_list0(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
|inp| {
alt((
parse_literal,
parse_variable,
parse_symbol,
parse_list,
parse_bmap,
parse_hashmap,
parse_map,
parse_bset,
))(inp)
.map_err(|e| match e {
nom::Err::Failure(inner) => nom::Err::Failure(inner),
_ => SyntaxErrorKind::expected(
inp.get_str_slice(),
"a value",
None,
Some("add a value for this item"),
),
})
},
)(input)?;
let (input, comma) = opt(text(","))(input)?;
let (input, _) = opt(kind(TokenKind::LineBreak))(input)?;
let (input, _) = text_close("}")(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
if comma.is_some() { "`}`" } else { "`}` or `,`" },
input.first().map(|t| t.text(input).to_string()),
if comma.is_some() {
Some("unclosed Set?")
} else {
Some("missing comma between items?")
},
)
})?;
Ok((input, pairs))
}
fn parse_map_inner<'a>(
input: Tokens<'a>,
tag: &'static str,
) -> IResult<Tokens<'a>, Vec<(String, Option<Expression>)>, SyntaxErrorKind> {
let (input, _) = terminated(text(tag), opt(kind(TokenKind::LineBreak)))(input)?;
let (input, pairs) = separated_list0(
terminated(text(","), opt(kind(TokenKind::LineBreak))),
tuple((
|inp| {
alt((
parse_symbol_string,
map(kind(TokenKind::StringRaw), |t| {
t.to_str(input.str).to_string()
}),
map(kind(TokenKind::StringLiteral), |t| {
t.to_str(input.str).to_string()
}),
))(inp)
.map_err(|e| match e {
nom::Err::Failure(inner) => nom::Err::Failure(inner),
_ => SyntaxErrorKind::expected(
inp.get_str_slice(),
"a value",
None,
Some("add a symbol/string as key"),
),
})
},
opt(preceded(
terminated(text(":"), opt(kind(TokenKind::LineBreak))),
cut(|inp| {
PrattParser::parse_expr_with_precedence(inp, PREC_FUNC_ARG, 0)
.map_err(|e| match e {
nom::Err::Failure(inner) => nom::Err::Failure(inner),
_ => SyntaxErrorKind::failure(
inp.get_str_slice(),
"a value",
None,
Some("add a value for this item"),
),
})
}),
)),
)),
)(input)?;
let (input, comma) = opt(text(","))(input)?;
let (input, _) = opt(kind(TokenKind::LineBreak))(input)?;
let (input, _) = text_close("}")(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(), if comma.is_some() { "`}`" } else { "`}` or `,`" },
input.first().map(|t| t.text(input).to_string()),
if comma.is_some() {
Some("unclosed Map?")
} else {
Some("missing comma between map entries?")
},
)
})?;
Ok((input, pairs))
}
fn has_lambda_arrow(input: Tokens<'_>) -> bool {
let mut depth = 0usize;
for (i, token) in input.slice.iter().enumerate() {
match token.range.to_str(input.str) {
"(" => depth += 1,
")" => {
depth -= 1;
if depth == 0 {
return input
.slice
.get(i + 1)
.is_some_and(|t| t.range.to_str(input.str) == "->");
}
}
_ => {}
}
}
false
}
fn looks_like_map(input: Tokens<'_>) -> bool {
let mut i = 1;
while input
.slice
.get(i)
.is_some_and(|t| t.kind == TokenKind::LineBreak)
{
i += 1;
}
let first = input.slice.get(i);
let second = input.slice.get(i + 1);
match (first, second) {
(Some(f), Some(s)) => {
let is_key = matches!(
f.kind,
TokenKind::Symbol | TokenKind::StringLiteral | TokenKind::StringRaw
);
let s_text = s.range.to_str(input.str);
is_key && (s_text == ":" || s_text == ",")
}
_ => false,
}
}
fn parse_map(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, pairs) = parse_map_inner(input, "{")?;
let map: BTreeMap<String, Expression> = pairs
.into_iter()
.map(|(k, v)| match v {
Some(ex) => (k, ex),
None => (k.clone(), Expression::Variable(k)),
})
.collect();
Ok((input, Expression::from(map)))
}
fn parse_bmap(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, pairs) = parse_map_inner(input, "M{")?;
let map: BTreeMap<String, Expression> = pairs
.into_iter()
.map(|(k, v)| match v {
Some(ex) => (k, ex),
None => (k.clone(), Expression::Variable(k)),
})
.collect();
Ok((input, Expression::from(map)))
}
fn parse_hashmap(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, pairs) = parse_map_inner(input, "H{")?;
let map: HashMap<String, Expression> = pairs
.into_iter()
.map(|(k, v)| match v {
Some(ex) => (k, ex),
None => (k.clone(), Expression::Variable(k)),
})
.collect();
Ok((input, Expression::from(map)))
}
fn parse_bset(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, pairs) = parse_set_inner(input)?;
let mut map = BTreeSet::new();
for e in pairs {
map.insert(e);
}
Ok((input, Expression::BSet(Rc::new(map))))
}
#[inline]
fn parse_integer(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, num) = kind(TokenKind::IntegerLiteral)(input)?;
let num = num
.to_str(input.str)
.replace('_', "")
.parse::<Int>()
.map_err(|e| SyntaxErrorKind::failure(num, "Integer", Some(format!("error: {e}")), None))?;
Ok((input, Expression::Integer(num)))
}
#[inline]
fn parse_float(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, num) = kind(TokenKind::FloatLiteral)(input)?;
let num = num
.to_str(input.str)
.replace('_', "")
.parse::<f64>()
.map_err(|e| {
SyntaxErrorKind::failure(
num,
"float",
Some(format!("error: {e}")),
Some("valid floats can be written like 1.0 or 5.23"),
)
})?;
Ok((input, Expression::Float(num)))
}
fn parse_radix(
kind: TokenKind,
) -> impl Fn(Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
move |input: Tokens<'_>| {
let token = input
.first()
.filter(|t| t.kind == kind)
.ok_or(nom::Err::Error(SyntaxErrorKind::CustomError(
format!("expect token kind: {:?}", kind),
input.get_str_slice(),
)))?;
let cs = token.text_inner(input).replace("_", "");
let parsed = match kind {
TokenKind::Radix2 => Int::from_str_radix(&cs, 2),
TokenKind::Radix8 => Int::from_str_radix(&cs, 8),
TokenKind::Radix16 => Int::from_str_radix(&cs, 16),
_ => unreachable!(),
};
let value = parsed.map_err(|e| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"Radix Integer",
Some(format!("error: {e}")),
None,
)
})?;
Ok((input.skip_n(1), Expression::Integer(value)))
}
}
#[inline]
fn parse_value_symbol(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
map(kind(TokenKind::ValueSymbol), |s| {
match s.to_str(input.str) {
"true" => Expression::Boolean(true),
"false" => Expression::Boolean(false),
"_" => Expression::Blank,
_ => Expression::None,
}
})(input)
}
#[inline]
fn parse_blank(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, v) = kind(TokenKind::ValueSymbol)(input)?;
if v.to_str(input.str) == "_" {
return Ok((input, Expression::Blank));
}
Err(nom::Err::Error(SyntaxErrorKind::NoExpression))
}
fn normalize_linebreaks(tokens: &mut Vec<Token>) {
let mut i = 0;
while i < tokens.len() {
if tokens[i].kind == TokenKind::LineBreak {
let mut j = i + 1;
while j < tokens.len() && tokens[j].kind == TokenKind::LineBreak {
j += 1;
}
if j > i + 1 {
tokens.drain(i + 1..j);
} else {
i += 1; }
} else {
i += 1;
}
}
}
pub fn tokenize_source(input: &Str) -> Result<Vec<Token>, nom::Err<SyntaxErrorKind>> {
#[cfg(windows)]
let input: Str = input.trim_matches('\0').into();
#[cfg(windows)]
let tokenization_input = Input::new(&input);
#[cfg(unix)]
let tokenization_input = Input::new(input);
let (mut token_vec, mut diagnostics) = super::parse_tokens(tokenization_input);
diagnostics.retain(|d| d != &Diagnostic::Valid);
if !diagnostics.is_empty() {
return Err(nom::Err::Failure(SyntaxErrorKind::TokenizationErrors(
diagnostics.into_boxed_slice(),
)));
}
token_vec.retain(|t| {
!matches!(
t.kind,
TokenKind::Whitespace | TokenKind::Comment | TokenKind::ModeTip
)
});
normalize_linebreaks(&mut token_vec);
Ok(token_vec)
}
pub fn use_script(input: &str) -> Result<ModuleInfo, nom::Err<SyntaxErrorKind>> {
let str: Str = input.into();
let token_vec = tokenize_source(&str)?;
let (_, parsed) = parse_module_selective(Tokens {
str: &str,
slice: token_vec.as_slice(),
})?;
Ok(parsed)
}
pub fn parse_script(input: &str) -> Result<Expression, nom::Err<SyntaxErrorKind>> {
let str: Str = input.into();
let token_vec = tokenize_source(&str)?;
let (_, parsed) = parse_script_tokens(Tokens {
str: &str,
slice: token_vec.as_slice(),
})?;
Ok(parsed)
}
pub fn parse_script_tokens(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
if input.is_empty() {
return Ok((input, Expression::None));
}
let (input, module) = terminated(
parse_module,
opt(alt((kind(TokenKind::LineBreak), eof_slice))), )(input)?;
let (input, _) = opt(many0(kind(TokenKind::LineBreak)))(input)?;
if !input.is_empty() {
return Err(nom::Err::Failure(SyntaxErrorKind::TokenizationErrors(
Box::new([Diagnostic::NotTokenized(input.get_str_slice())]),
)));
}
match module.len() {
0 => Err(nom::Err::Error(SyntaxErrorKind::NoExpression)),
1 => {
let s = module.first().unwrap();
Ok((input, s.clone()))
}
_ => Ok((input, Expression::Sequence(module))),
}
}
fn parse_module(input: Tokens<'_>) -> IResult<Tokens<'_>, Vec<Expression>, SyntaxErrorKind> {
let (input, module) = cut(many0(alt((
terminated(
parse_use_statement,
opt(kind(TokenKind::LineBreak)), ),
terminated(
parse_fn_declare,
opt(kind(TokenKind::LineBreak)), ), terminated(
parse_statement,
opt(kind(TokenKind::LineBreak)), ), ))))(input)?;
Ok((input, module))
}
fn parse_statement(mut input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
(input, _) = opt(many0(kind(TokenKind::LineBreak)))(input)?;
let keyword = input
.first()
.map(|t| t.text(input))
.ok_or_else(|| nom::Err::Error(SyntaxErrorKind::NoExpression))?;
match keyword {
"fn" => parse_fn_declare(input), "use" => parse_use_statement(input), "let" => parse_lets(input),
"set" => parse_set(input),
"alias" => parse_alias(input),
"export" => parse_export(input),
"del" => parse_del(input),
_ => parse_single_expr(input),
}
}
fn parse_single_expr(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, expr) = terminated(
parse_expr, opt(alt((
kind(TokenKind::LineBreak),
eof_slice, ))),
)(input)?;
Ok((input, expr))
}
fn parse_if_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("if")(input)?;
let (input, cond) = cut(parse_expr)(input)?;
let (input, then_block) = alt((
parse_domain_block, parse_block_as_sequence, ))(input)
.map_err(|e| match e {
nom::Err::Error(_) => SyntaxErrorKind::failure(
input.get_str_slice(),
"a block `{...}`",
input.first().map(|t| t.text(input).to_string()),
Some("if condition must be followed by a block: `if cond { ... }`"),
),
other => other,
})?; let (input, else_branch) = opt(preceded(
text("else"),
cut(alt((
parse_if_flow, parse_block_or_expr, ))),
))(input)
.map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"some body",
None,
Some("add a body for `else`"),
)
})?;
Ok((
input,
Expression::If(
Rc::new(cond),
Rc::new(then_block),
Rc::new(else_branch.unwrap_or(Expression::None)),
),
))
}
fn parse_while_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("while")(input)?;
let (input, cond) = cut(parse_expr)(input)?;
let (input, body) = cut(parse_block)(input)?;
Ok((input, Expression::While(Rc::new(cond), Rc::new(body))))
}
fn parse_loop_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("loop")(input)?;
let (input, body) = cut(parse_block)(input)?;
Ok((input, Expression::Loop(Rc::new(body))))
}
fn parse_for_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("for")(input)?;
let (input, pat_ind) = opt(terminated(parse_symbol_string, text(",")))(input)?;
let (input, pat_var) = cut(parse_symbol_string)(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"loop variable",
input.first().map(|t| t.text(input).to_string()),
Some("add a variable name"),
)
})?;
let (input, _) = cut(text("in"))(input)?;
let (input, iterable) = cut(parse_expr)(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"loop target",
input.first().map(|t| t.text(input).to_string()),
Some("add a iterable expression"),
)
})?;
let (input, body) = cut(parse_block)(input)?;
Ok((
input,
Expression::For(pat_var, pat_ind, Rc::new(iterable), Rc::new(body)),
))
}
fn parse_match_flow(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("match")(input)?;
let (input, target) = cut(parse_expr)(input)?;
let (input, _) = cut(terminated(text("{"), opt(kind(TokenKind::LineBreak))))(input)?;
let (input, expr_map) = cut(separated_list1(
kind(TokenKind::LineBreak),
separated_pair(
parse_pattern,
cut(terminated(text("=>"), opt(kind(TokenKind::LineBreak)))),
cut(|inp| {
parse_expr(inp).map_err(|e| match e {
nom::Err::Error(_) => SyntaxErrorKind::failure(
inp.get_str_slice(),
"invalid arm action",
inp.first().map(|t| t.text(inp).to_string()),
Some("check grammar of this arm action"),
),
other => other,
})
}),
),
))(input)?;
let (input, _) = opt(kind(TokenKind::LineBreak))(input)?;
let (input, _) = cut(terminated(text_close("}"), opt(kind(TokenKind::LineBreak))))(input)?;
let branches = expr_map.into_iter().collect::<Vec<_>>();
Ok((input, Expression::Match(Rc::new(target), Rc::new(branches))))
}
fn parse_block_or_expr(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
alt((
parse_domain_block, parse_block_as_sequence, parse_expr, ))(input)
}
fn parse_domain_block(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, block) = delimited(
terminated(text("%{"), opt(many0(kind(TokenKind::LineBreak)))),
map(
many0(terminated(
parse_statement,
opt(many0(kind(TokenKind::LineBreak))),
)),
|stmts| Expression::Block(Rc::new(stmts)),
),
cut(text_close("}")),
)(input)?;
Ok((input, block))
}
fn parse_block(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, block) = delimited(
terminated(text("{"), opt(many0(kind(TokenKind::LineBreak)))),
map(
many0(terminated(
parse_statement,
opt(many0(kind(TokenKind::LineBreak))),
)),
|stmts| Expression::Block(Rc::new(stmts)),
),
cut(text_close("}")),
)(input)?;
Ok((input, block))
}
fn parse_block_as_sequence(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, block) = delimited(
terminated(text("{"), opt(many0(kind(TokenKind::LineBreak)))),
map(
many0(terminated(
parse_statement,
opt(many0(kind(TokenKind::LineBreak))),
)),
Expression::Sequence,
),
cut(text_close("}")),
)(input)?;
Ok((input, block))
}
fn parse_array_destructure(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, Vec<DestructurePattern>, SyntaxErrorKind> {
delimited(
text("["),
cut(separated_list1(
text(","),
alt((
map(preceded(text("*"), cut(parse_symbol_string)), |s| {
DestructurePattern::Rest(s)
}),
map(cut(parse_symbol_string), |s| {
DestructurePattern::Identifier(s)
}),
)),
)),
cut(text_close("]")),
)(input)
}
fn parse_map_destructure(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, Vec<DestructurePattern>, SyntaxErrorKind> {
delimited(
text("{"),
cut(separated_list1(
text(","),
alt((
map(
separated_pair(parse_symbol_string, text(":"), cut(parse_symbol_string)),
|(i, n)| DestructurePattern::Renamed((i, n)),
),
map(cut(parse_symbol_string), |s| {
DestructurePattern::Identifier(s)
}),
)),
)),
cut(text_close("}")),
)(input)
}
fn parse_alias(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("alias")(input)?;
let (input, symbol) = cut(parse_symbol_string)(input)?;
let (input, _) = cut(text("="))(input)?;
let (input, expr) = cut(parse_expr)(input)?;
Ok((input, Expression::AliasDef(symbol, Rc::new(expr))))
}
fn parse_export(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("export")(input)?;
let (input, name) = cut(parse_symbol_string)(input)?;
let (input, expr_op) = opt(preceded(text("="), parse_expr))(input)?;
if let Some(expr) = expr_op {
Ok((input, Expression::Export(name, Some(Rc::new(expr)))))
} else {
Ok((input, Expression::Export(name, None)))
}
}
fn parse_set(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("set")(input)?;
let (input, name) = cut(parse_symbol_string)(input)?;
let (input, _) = cut(text("="))(input)?;
let (input, expr) = cut(parse_expr)(input)?;
Ok((input, Expression::SetParent(name, Rc::new(expr))))
}
fn parse_lets(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("let")(input)?;
let keyword = input
.first()
.map(|t| t.text(input))
.ok_or_else(|| nom::Err::Error(SyntaxErrorKind::NoExpression))?;
let (input, pattern) = match keyword {
"[" => parse_array_destructure(input)?,
"{" => parse_map_destructure(input)?,
_ => return alt((parse_lazy_assign, parse_declare))(input),
};
let (input, exprs) = preceded(
text("="),
cut(|input| {
parse_expr(input).map_err(|e| match e {
nom::Err::Error(_) => SyntaxErrorKind::failure(
input.get_str_slice(),
"a value expression",
input.first().map(|t| t.text(input).to_string()),
Some("check grammar after `=`"),
),
other => other,
})
}),
)(input)?;
Ok((
input,
Expression::DestructureAssign(pattern, Rc::new(exprs)),
))
}
fn parse_lazy_assign(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, symbol) = parse_symbol_string(input)?;
let (input, _) = text(":=")(input)?; let (input, expr) = cut(parse_expr)(input)?;
Ok((
input,
Expression::Declare(symbol, Rc::new(Expression::Quote(Rc::new(expr)))),
))
}
fn parse_declare(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, symbols) = separated_list1(
text(","),
alt((
parse_symbol_string,
parse_operator,
parse_custom_postfix_operator,
)),
)(input)
.map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"comma separated var name",
None,
Some("try: `let x, y = 1`"),
)
})?;
let (input, value_expr) = opt(preceded(
text("="),
cut(|input| {
parse_expr(input).map_err(|e| match e {
nom::Err::Error(_) => SyntaxErrorKind::failure(
input.get_str_slice(),
"a value expression",
input.first().map(|t| t.text(input).to_string()),
Some("check grammar after `=`"),
),
other => other,
})
}),
))(input)?;
let value = value_expr.map_or(Rc::new(Expression::None), |v| Rc::new(v));
return if symbols.len() == 1 {
Ok((input, Expression::Declare(symbols[0].clone(), value)))
} else {
let assignments = (0..symbols.len())
.map(|i| Expression::Declare(symbols[i].clone(), value.clone()))
.collect();
Ok((input, Expression::Sequence(assignments)))
};
}
fn parse_del(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("del")(input)?;
let (input, symbol) = cut(parse_symbol_string)(input).map_err(|_| {
SyntaxErrorKind::failure(
input.get_str_slice(),
"symbol",
Some("no symbol".into()),
Some("you can only del symbol"),
)
})?;
Ok((input, Expression::Del(symbol)))
}
#[inline]
fn parse_operator(input: Tokens<'_>) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
map(kind(TokenKind::Operator), |t| {
t.to_str(input.str).to_string()
})(input)
}
#[inline]
fn parse_custom_postfix_operator(
input: Tokens<'_>,
) -> IResult<Tokens<'_>, String, SyntaxErrorKind> {
map(kind(TokenKind::OperatorPostfix), |t| {
t.to_str(input.str).to_string()
})(input)
}
fn parse_pattern(input: Tokens<'_>) -> IResult<Tokens<'_>, Vec<Expression>, SyntaxErrorKind> {
let (input, pat) = separated_list1(
text(","),
alt((
parse_num_range,
parse_string,
parse_string_raw,
parse_value_symbol,
parse_symbol,
parse_regex,
parse_time,
parse_integer,
parse_float,
)),
)(input)
.map_err(|_| {
SyntaxErrorKind::expected(
input.get_str_slice(),
"match pattern",
input.first().map(|t| t.text(input).to_string()),
Some("add a string/symbol/regex/number/range/time"),
)
})?;
Ok((input, pat))
}
fn parse_num_range(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, start) = alt((parse_integer, parse_blank))(input)?;
let (new_input, optoken) = alt((text("..="), text("..")))(input)?;
let operator = optoken.text(input);
let input = new_input;
let (input, stop) = alt((parse_integer, parse_blank))(input)?;
let (input, step) = opt(preceded(text(":"), cut(alt((parse_integer, parse_blank)))))(input)?;
let r = Expression::RangeOp(
operator.to_string(),
Rc::new(start),
Rc::new(stop),
step.map(Rc::new),
);
Ok((input, r))
}
#[inline]
fn eof_slice(input: Tokens<'_>) -> IResult<Tokens<'_>, StrSlice, SyntaxErrorKind> {
if input.is_empty() {
Ok((input, StrSlice::default()))
} else {
Err(nom::Err::Error(SyntaxErrorKind::Expected {
input: input.get_str_slice(),
expected: "end of input",
found: None,
hint: Some("Check your input"),
}))
}
}
fn parse_index(
target: Expression,
input: Tokens<'_>,
depth: usize,
) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, expr) = delimited(
text("["),
|inp| PrattParser::parse_expr_with_precedence(inp, PREC_ADD_SUB, depth + 1),
cut(text_close("]")),
)(input)?;
Ok((input, Expression::Index(Rc::new(target), Rc::new(expr))))
}
#[derive(Debug, Clone)]
pub struct ModuleInfo {
pub use_statements: Vec<(Option<String>, String)>, pub functions: HashMap<String, Expression>,
}
fn parse_module_selective(input: Tokens<'_>) -> IResult<Tokens<'_>, ModuleInfo, SyntaxErrorKind> {
let mut use_statements = Vec::new();
let mut functions = HashMap::new();
let mut remaining = input;
while !remaining.is_empty() {
match parse_use_statement(remaining) {
Ok((rest, use_stmt)) => {
if let Expression::Use(alias, path) = use_stmt {
use_statements.push((alias, path));
}
remaining = rest;
continue;
}
Err(nom::Err::Failure(e)) => return Err(nom::Err::Failure(e)), Err(_) => {} }
match parse_fn_declare(remaining) {
Ok((rest, func)) => {
if let Expression::Function(name, ..) = &func {
functions.insert(name.clone(), func);
}
remaining = rest;
continue;
}
Err(nom::Err::Failure(e)) => return Err(nom::Err::Failure(e)), Err(_) => {} }
let mut place = 0;
for token in remaining.iter() {
place += 1;
if token.kind == TokenKind::LineBreak {
break;
}
}
remaining = remaining.skip_n(place);
}
Ok((
remaining,
ModuleInfo {
use_statements,
functions,
},
))
}
fn parse_use_statement(input: Tokens<'_>) -> IResult<Tokens<'_>, Expression, SyntaxErrorKind> {
let (input, _) = text("use")(input)?;
let (input, module_path) = cut(alt((parse_symbol_string, parse_string_raw_inner)))(input)?;
let (input, alias) = opt(preceded(text("as"), parse_symbol_string))(input)?;
Ok((input, Expression::Use(alias, module_path)))
}