fr 0.1.1

A programming language with an unusual compiler backend
Documentation
use crate::compile::*;

grammar;


extern {
    type Location = usize;
    type Error = String;
}


pub Str: String = <s:r#""(\\.|[^"])*""#> => String::from(&s[1..s.len()-1]).replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\0","\0").replace("\\\"", "\"");
pub Char: char = <s:r#"'(\\.|[^"])'"#> => String::from(&s[1..s.len()-1]).replace("\\n","\n").replace("\\r","\r").replace("\\t","\t").replace("\\0","\0").replace("\\\"", "\"").chars().next().unwrap();

pub Num: Literal = {
    r"([0-9]+([.][0-9]*)?|[.][0-9]+)" => match <>.parse::<u8>() {
        Ok(val) => Literal::byte_int(val),
        Err(_) => Literal::unsigned_short(<>.parse::<u16>().unwrap()),
    },
}

pub Flag: Flag = {
    "#" "[" <FlagName> "]" => <>
}

pub FlagName: Flag = {
    "enable" "(" "brainfuck" ")" => Flag::EnableBrainFuck,
    "enable" "(" "size_warn" ")" => Flag::EnableSizeWarn
}

pub Value: Eval = {
    "(" <Value> ")" => <>,
    "*" <name: Value> => Eval::Deref(Deref::new(name)),
    <name: Ident> => Eval::Load(Load::new(name)),
    "&" <name: Ident> => Eval::Refer(Refer::new(Eval::Load(Load::new(name)))),
    <name: Ident> <args: List<"(", Value, ",", ")">> => Eval::Call(Call::new(name, args)),
    <Literal> => Eval::Literal(<>)
}

pub Expr: Expr = {
    <IfStatement> => <>,
    <WhileLoop> => <>,
    <Assign> ";" => <>,
    <Value> ";" => Expr::Eval(<>),
    "return" <ret: Value> ";" => Expr::Return(Return::new(ret))
}

pub Assign: Expr = {
    <lhs: Value> "=" <rhs: Value> => Expr::Assign(Assign::new(lhs, rhs)),
    "def" <lhs: Ident> "=" <rhs: Value> => Expr::Define(Define::new(lhs, rhs)),
}

pub Program: Program = {
    <flags: Flag*> <fndefs: FunctionDef+> => Program::new(flags, fndefs)
}

pub Body: Vec<Expr> = Expr* => <>;

pub Ident: String = {
    r"[a-zA-Z_][a-zA-Z0-9_]*" => <>.to_string()
}

pub WhileLoop: Expr = {
    "while" <condition:Ident> "{" <body:Body> "}" => Expr::While(While::new(Eval::Load(Load::new(condition)), body))
}

pub IfStatement: Expr = {
    "if" <condition:Value> "{" <then_body:Body> "}" <else_clause:("else" "{" <Body> "}")?> => {
        match else_clause {
            Some(clause) => {
                Expr::If(If::new(condition, then_body, clause))
            }
            None => {
                Expr::If(If::new(condition, then_body, vec![]))
            }
        }
    }
}

FunctionDef: UserFn = "fn" <name: Ident> <args: List<"(", Ident, ",", ")">> "{" <body: Body> "}" => UserFn::new(name, args, body);


Literal: Literal = {
    <Str> => Literal::string(<>),
    <Char> => Literal::character(<>),
    <Num> => <>
}

Infix<First, Operator, Second>: (First, Operator, Second) = {
    <first:First> <op: Operator> <second:Second> => {
        (<>)
    }
}

List<Begin, T, Sep, End>: Vec<T> = {
    <first:Begin> <list: (<T> <Sep>)*> <end:T?> <last:End> => {
        match end {
            None => list.iter().map(|(v, s)| v.clone()).collect(),
            Some(val) => {
                let mut list: Vec<_> = list.iter().map(|(v, s)| v.clone()).collect();
                list.push(val);
                list
            }
        }
    }
}