funki_lang 0.1.2

A customisable embeddable functional langauge.
Documentation
// 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 */`
    _
}