piske 0.1.2

The piske programming langauge for generative art
Documentation
use ast::*;
use sindra::Node;
use sindra::float::float_ext;
use sindra::int::int_ext;
use sindra::string::{match_str_ext, convert_string};
use regex::Regex;

#[pub]
program -> Node<Program>
    = b:block { Node::new(Program(b)) }

#[pub]
block -> Node<Block>
    = vec:(ws stmt:statement ws { stmt })* { Node::new(Block(vec)) }

#[pub]
statement -> Node<Statement>
    = declare_statement
    / assign_statement
    / fn_define_statement
    / return_statement
    / break_statement
    / print_statement
    / expr:expression ws ";"? ws { Node::new(Statement::Expression(expr)) }

fn_define_statement -> Node<Statement>
    = kw_fn fn_ident:identifier ws '(' vec:(ws params:parameters { params }) ')' ws "->" ws
        ret_type:identifier ws "{" body:block "}" {
            Node::new(Statement::FnDefine(FunctionDef { name: fn_ident,
                ret_type: ret_type, params: vec, body: body }))
        }

parameters -> Vec<Node<Parameter>>
    = parameter_ws**","

parameter_ws -> Node<Parameter>
    = ws p:parameter ws { p }

parameter -> Node<Parameter>
    = ident:identifier ws ":" ws ty:identifier { Node::new(Parameter { name: ident.clone(),
        ty: ty.clone() }) }

declare_statement -> Node<Statement>
    = kw_let ident:identifier ws "=" expr:expression_ws (";" ws)? {
        Node::new(Statement::Declare(ident, expr))
    }

assign_statement -> Node<Statement>
    = ident:identifier  ws "=" expr:expression ws (";" ws)? {
        Node::new(Statement::Assign(ident, expr))
    }

return_statement -> Node<Statement>
    = kw_return ws expr:expression ws (";" ws)? {
        Node::new(Statement::Return(expr))
    }

break_statement -> Node<Statement>
    = kw_break ws expr:expression ws (";" ws)? {
        Node::new(Statement::Break(expr))
    }

print_statement -> Node<Statement>
    = kw_print exprs:expressions (";" ws)? {
        Node::new(Statement::Print(exprs))
    }

expressions -> Vec<Node<Expression>>
    = expression_ws**","

keyword<k> = k !@"\p{XID_Continue}"@ ws
kw_let = keyword<"let">
kw_fn = keyword<"fn">
kw_global = keyword<"set">
kw_if = keyword<"if">
kw_else = keyword<"else">
kw_true = keyword<"true">
kw_false = keyword<"false">
kw_iterate = keyword<"iterate">
kw_over = keyword<"over">
kw_return = keyword<"return">
kw_break = keyword<"break">
kw_print = keyword<"print">

kw = kw_let / kw_global / kw_fn / kw_if / kw_else / kw_true / kw_false / kw_iterate / kw_over
    / kw_return / kw_break / kw_print;

type -> Node<Identifier>
    = primitive_type / identifier

primitive_type -> Node<Identifier>
    = 'real' { Node::new(Identifier("real".to_string())) }
    / 'int' { Node::new(Identifier("int".to_string())) }
    / 'complex' { Node::new(Identifier("complex".to_string())) }
    / 'bool' { Node::new(Identifier("bool".to_string())) }

#[pub]
arith_expression -> Node<Expression>
    = infix_arith
    / signed_or_unsigned_primary

expression_ws -> Node<Expression>
    = ws e:expression ws { e }

#[pub]
expression -> Node<Expression>
    = i:identifier ws "(" pl:arg_list ")" {
        Node::new(Expression::FnCall { name: i, args: pl })
    }
    / "{" ws b:block ws "}" { Node::new(Expression::Block(b)) }
    / arith_expression
    / lit:literal { Node::new(Expression::Literal(lit)) }
    / ifelse
    / loop

arg_list -> Vec<Node<Expression>>
    = expression_ws**","

atom -> Node<Expression>
    = ws n:num ws { Node::new(Expression::Literal(n)) }
    / ws i:identifier ws { Node::new(Expression::Identifier(i)) }
    / ws g:grouped_arith ws { g }

conjugated_atom -> Node<Expression>
    = a:atom ws "`" {
        Node::new(Expression::Postfix { op: PostfixOp::Conjugate, left: Box::new(a) })
    }

imaginary_atom -> Node<Expression>
    = a:atom ws "i" {
        Node::new(Expression::Postfix { op: PostfixOp::Imaginary, left: Box::new(a) })
    }

primary -> Node<Expression>
    = ws c:conjugated_atom ws { c }
    / ws i:imaginary_atom ws { i }
    / ws a:atom ws { a }

signed_primary -> Node<Expression>
    = ws "+" p:primary {
        Node::new(Expression::Prefix { op: PrefixOp::UnaryPlus, right: Box::new(p) })
     }
    / ws "-" p:primary {
        Node::new(Expression::Prefix { op: PrefixOp::UnaryMinus, right: Box::new(p) })
    }

signed_or_unsigned_primary -> Node<Expression>
    = primary
    / signed_primary

grouped_arith -> Node<Expression>
    = "(" expr:arith_expression ")" { expr }

infix_arith -> Node<Expression> = #infix<signed_or_unsigned_primary> {
    #L l "==" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::Equal),
            left: Box::new(l), right: Box::new(r) })
    }
       l "!=" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::NotEqual),
            left: Box::new(l), right: Box::new(r) })
    }
    #L l "<=" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::LessThanEqual),
            left: Box::new(l), right: Box::new(r) })
    }
       l ">=" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::GreaterThanEqual),
            left: Box::new(l), right: Box::new(r) })
    }
    #L l "<" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::LessThan),
            left: Box::new(l), right: Box::new(r) })
    }
       l ">" r {
        Node::new(Expression::Infix { op: InfixOp::Comparison(CompareOp::GreaterThan),
            left: Box::new(l), right: Box::new(r) })
    }
    #L l "+" r {
        Node::new(Expression::Infix { op: InfixOp::Add, left: Box::new(l), right: Box::new(r) })
    }
       l "-" r {
        Node::new(Expression::Infix { op: InfixOp::Subtract, left: Box::new(l), right: Box::new(r) })
    }
    #L l "*" r {
        Node::new(Expression::Infix { op: InfixOp::Multiply, left: Box::new(l), right: Box::new(r) })
    }
       l "/" r {
        Node::new(Expression::Infix { op: InfixOp::Divide, left: Box::new(l), right: Box::new(r) })
    }
    #R l "^" r {
        Node::new(Expression::Infix { op: InfixOp::Power, left: Box::new(l), right: Box::new(r) })
    }
}

ifelse -> Node<Expression>
    = ws kw_if ws cond:expression ws ifb:paren_block ws kw_else ws elseb:paren_block ws {
        Node::new(Expression::IfElse {
            cond: Box::new(cond),
            if_block: ifb,
            else_block: Some(elseb)
        })
    }
    / ws kw_if ws cond:expression ws ifb:paren_block ws {
        Node::new(Expression::IfElse {
            cond: Box::new(cond),
            if_block: ifb,
            else_block: None
        })
    }

paren_block -> Node<Block>
    = "{" ws b:block ws "}" { b }

loop -> Node<Expression>
    = interval

interval -> Node<Expression>
    = ws kw_iterate ws kw_over ws int:set_interval ws body:paren_block ws {
        Node::new(Expression::Loop {
            variant: None,
            set: int,
            body: body,
        })
    }
    / ws kw_iterate ws i:identifier ws "=" ws int:set_interval ws body:paren_block ws {
        Node::new(Expression::Loop {
            variant: Some(i),
            set: int,
            body: body,
        })
    }

set_interval -> Node<Set>
    = "[" ws start:expression ws "," ws end:expression ws ")" {
        Node::new(Set::Interval {
            start: Box::new(start),
            end: Box::new(end),
            end_inclusive: false,
            step: Box::new(Node::new(Expression::Literal(Node::new(Literal::Int(1)))))
        })
    }
    / "[" ws start:expression ws ".." ws step:expression ".." end:expression ws ")" {
        Node::new(Set::Interval {
            start: Box::new(start),
            end: Box::new(end),
            end_inclusive: false,
            step: Box::new(Node::new(Expression::Literal(Node::new(Literal::Int(1)))))
        })
    }
    / "[" ws start:expression ws "," ws end:expression ws "]" {
        Node::new(Set::Interval {
            start: Box::new(start),
            end: Box::new(end),
            end_inclusive: true,
            step: Box::new(Node::new(Expression::Literal(Node::new(Literal::Int(1)))))
        })
    }
    / "[" ws start:expression ws ".." ws step:expression ".." end:expression ws "]" {
        Node::new(Set::Interval {
            start: Box::new(start),
            end: Box::new(end),
            end_inclusive: true,
            step: Box::new(Node::new(Expression::Literal(Node::new(Literal::Int(1)))))
        })
    }

ws = #quiet<(ws_char / eol / comment)*>

eol
    = "\n"
    / "\r\n"
    / "\r"
    / "\u{2028}"
    / "\u{2029}"

eol_char = [\n\r\u{2028}\u{2029}]

ws_char
    = [ \t\u{00A0}\u{FEFF}\u{1680}\u{180E}\u{2000}-\u{200A}\u{202F}\u{205F}\u{3000}]

comment
    = comment_single_line
    / comment_multi_line

comment_single_line
    = "//" (!eol_char .)*

comment_multi_line
    = "/*" (!"*/" .)* "*/"

#[pub]
literal -> Node<Literal>
    = num
    / string
    / boolean

num -> Node<Literal>
    = f:#ext<float> { Node::new(Literal::Float(f)) }
    / i:#ext<int> { Node::new(Literal::Int(i)) }

str -> String
    = s:#ext<match_str> {? convert_string(s) }

string -> Node<Literal>
    = s:str { Node::new(Literal::String(s)) }

truefalse -> bool
    = "true" { true }
    / "false" { false }

boolean -> Node<Literal>
    = b:truefalse { Node::new(Literal::Boolean(b)) }

identifier -> Node<Identifier>
    = !kw s:@"\p{XID_Start}\p{XID_Continue}*"@ {
        Node::new(Identifier(s.get(0).unwrap().as_str().to_string()))
    }