leafbuild 0.0.1-dev.1

A WIP C/C++ buildsystem.
Documentation
use crate::{
	grammar::{
		lexer,
		lexer::*,
		ast::*,
	},
};

grammar<'input>(input: &'input str);

Spanned<Rule>: (Rule, Span) = <start: @L> <rule: Rule> <end: @R> => (rule, Span::new(start, end));
SpannedLocation<Rule>: Span = <start: @L> <rule: Rule> <end: @R> => Span::new(start, end);

SingleComma: () = {
    "," => (),
}

#[inline]
Comma<Rule>: Vec<Rule> =
    <mut rules: (<Rule> SingleComma)*> <last: Rule?> => {
        rules.extend(last);
        rules
    };

#[inline]
CommaNoTrailing<Rule>: Vec<Rule> =
    <first: Rule> <mut rules: (SingleComma <Rule>)*> => { rules.insert(0, first); rules };

IdExpressionUnspanned: String =
    <id: r"[a-zA-Z_][a-zA-Z0-9_]*"> => String::from(id);

IdExpression = Spanned<IdExpressionUnspanned>;

NumTok: NumVal = {
    <num: r"([1-9][0-9]*|0x[0-9a-fA-F]+|0b[01]+|0[0-7]*)[uU]?[lL]?"> => NumVal::from(num)
}

Num = Spanned<NumTok>;

BoolTok: bool = {
    "true" => true,
    "false" => false,
}

BoolLit = Spanned<BoolTok>;

StrTok: String = {
    <s: r#"'(\\['nt\\]|[^'\\])+'"#> => String::from(&s[1..(s.len()-1)]),
    <s: r#"'''([^']*|[']{1,2}[^'])*'''"#> => String::from(&s[3..(s.len()-3)]),
}

Str = Spanned<StrTok>;

UnaryNot: UnaryOpcode = { <l: SpannedLocation<"not">> => UnaryOpcode::Not(l) }

pub Program = <Statement*>;
Statement: AstStatement = {
//    <call: MethodCall> ";" => AstStatement::MethodCall(call),
	<call: FuncCall> ";" => AstStatement::FuncCall(call),
	<assignment: Assignment> ";" => AstStatement::Assignment(assignment),
	<declaration: Declaration> ";" => AstStatement::Declaration(declaration),
	<statement: ConditionalStatement> => AstStatement::Conditional(statement),
	//<statement ControlStatement> => AstStatement::ControlStatement(statement),
	<statement: RepetitiveStatement> => AstStatement::Repetitive(statement),
}

// function calls
FuncCall: AstFuncCall = {
	<name:IdExpression> "(" <args:FuncArgs> ")" <end: @R> => AstFuncCall::new(name, args, end)
}

FuncArgs: AstFuncCallArgs = {
	<pos_args:PositionalFuncArgs> SingleComma <named_args:NamedFuncArgs> SingleComma? => AstFuncCallArgs::new(pos_args,named_args),
	<pos_args:PositionalFuncArgs> SingleComma? => AstFuncCallArgs::new_only_positional(pos_args),
	<named_args:NamedFuncArgs> SingleComma? => AstFuncCallArgs::new_only_named(named_args),
	() => AstFuncCallArgs::empty(),
}

#[inline]
PositionalFuncArgs = CommaNoTrailing<PositionalFuncArg>;

PositionalFuncArg: AstPositionalArg = <expr: Expr> => AstPositionalArg::from(expr);

#[inline]
NamedFuncArgs = CommaNoTrailing<NamedExpr>;

NamedExpr: AstNamedExpr = {
	<name:IdExpression> "=" <value:Expr> => AstNamedExpr::from((name, value))
}

UnaryPlusMinusOpcode: UnaryOpcode = {
	<l: SpannedLocation<"-">> => UnaryOpcode::Minus(l),
	<l: SpannedLocation<"+">> => UnaryOpcode::Plus(l),
}

ExprAtom: Box<Expr> = {
    <f: FuncCall> => Box::new(Expr::FuncCall(f)),
    <n: Num> => Box::new(Expr::Atom(Atom::Number(n))),
//    MethodCall,
}

UnaryPlusMinusExpression: Box<Expr> = {
    <op: UnaryPlusMinusOpcode> <expr: UnaryPlusMinusExpression> => Box::new(Expr::UnaryOp(op, expr)),
    ExprAtom,
}

UnaryLogicalAndBitwiseNot: UnaryOpcode = {
	<l: SpannedLocation<"not">> => UnaryOpcode::Not(l),
	<l: SpannedLocation<"~">> => UnaryOpcode::BitwiseNot(l),
}

UnaryLogicalAndBitwiseNotExpression: Box<Expr> = {
    <op: UnaryLogicalAndBitwiseNot> <expr: UnaryPlusMinusExpression> => Box::new(Expr::UnaryOp(op, expr)),
    UnaryPlusMinusExpression
}

MulOp: Opcode = {
    <l: SpannedLocation<"*">> => Opcode::Mul(l),
    <l: SpannedLocation<"/">> => Opcode::Div(l),
    <l: SpannedLocation<"%">> => Opcode::Mod(l),
}

Factor: Box<Expr> = {
    <left: Factor> <op: MulOp> <right: UnaryLogicalAndBitwiseNotExpression> => Box::new(Expr::Op(left, op, right)),
    UnaryLogicalAndBitwiseNotExpression,
}

AddOp: Opcode = {
    <l: SpannedLocation<"+">> => Opcode::Add(l),
    <l: SpannedLocation<"-">> => Opcode::Sub(l),
}

AddExpr: Box<Expr> = {
    <left: AddExpr> <op: AddOp> <right: Factor> => Box::new(Expr::Op(left, op, right)),
    Factor,
}

BitShiftOp: Opcode = {
    <l: SpannedLocation<"<<">> => Opcode::LBitshift(l),
    <l: SpannedLocation<">>">> => Opcode::RBitshift(l)
}

BitShiftExpr: Box<Expr> = {
    <left: BitShiftExpr> <op: BitShiftOp> <right: AddExpr> => Box::new(Expr::Op(left, op, right)),
    AddExpr,
}

RelOp: Opcode = {
	<l: SpannedLocation<">">> => Opcode::G(l),
	<l: SpannedLocation<"<">> => Opcode::L(l),
	<l: SpannedLocation<">=">> => Opcode::GE(l),
	<l: SpannedLocation<"<=">> => Opcode::LE(l),
	<l: SpannedLocation<"in">> => Opcode::In(l),
	<l: SpannedLocation<("not" "in")>> => Opcode::NotIn(l),
}

RelExpr: Box<Expr> = {
	<left: RelExpr> <op: RelOp> <right: BitShiftExpr> => Box::new(Expr::Op(left, op, right)),
	AddExpr,
}

RelOp2: Opcode = {
    <l: SpannedLocation<"==">> => Opcode::Equal(l),
    <l: SpannedLocation<"!=">> => Opcode::NE(l),
}

RelExpr2: Box<Expr> = {
	<left: RelExpr2> <op: RelOp2> <right: RelExpr> => Box::new(Expr::Op(left, op, right)),
	RelExpr,
}

AndConditionalExpr: Box<Expr> = {
	<left: AndConditionalExpr> <op: AndOp> <right: RelExpr2> => Box::new(Expr::Op(left, op, right)),
	RelExpr2,
}

AndOp: Opcode = {
	<l: SpannedLocation<"and">> => Opcode::And(l),
}

OrConditionalExpr: Box<Expr> = {
	<left: OrConditionalExpr> <op: OrOp> <right: AndConditionalExpr> => Box::new(Expr::Op(left, op, right)),
	AndConditionalExpr,
}

OrOp: Opcode = {
    <l: SpannedLocation<"or">> => Opcode::Or(l),
}

TernaryExpr: Box<Expr> = {
    <condition: OrConditionalExpr> <qmark: SpannedLocation<"?">> <if_true: TernaryExpr> <colon: SpannedLocation<":">> <if_false: OrConditionalExpr> =>
        Box::new(Expr::Ternary{condition, qmark, if_true, colon, if_false}),
    OrConditionalExpr
}

Expr = TernaryExpr;

//MemberAccessExpr: AstPropertyAccess = {
//    <t:Term> <dot: SpannedLocation<".">> <prop_name:IdExpression> => AstPropertyAccess::new(t, dot, prop_name),
//}
//
//MethodCall: AstMethodCall = {
//	<property_access:MemberAccess> "(" <args:FuncArgs> ")" <end: @R> => AstMethodCall::new(property_access, args, end),
//}

Declaration: AstDeclaration = {
	<let_tok: SpannedLocation<"let">> <name: IdExpression> <eq: SpannedLocation<"=">> <value: Expr> => AstDeclaration::new(let_tok, name, eq, value)
}

Assignment: AstAssignment = {
	<bind_name: Expr> <op: AtrOp> <val: Expr> => AstAssignment::new(bind_name, op, val),
}

AtrOp: AstAtrOp = {
	<l: SpannedLocation<"=">> => AstAtrOp::Atr(l),
	<l: SpannedLocation<"+=">> => AstAtrOp::AddAtr(l),
	<l: SpannedLocation<"-=">> => AstAtrOp::SubAtr(l),
	<l: SpannedLocation<"*=">> => AstAtrOp::MulAtr(l),
	<l: SpannedLocation<"/=">> => AstAtrOp::DivAtr(l),
	<l: SpannedLocation<"%=">> => AstAtrOp::ModAtr(l),
}

ArrayLit: Box<Expr> = {
	<lbrace: SpannedLocation<"{">> <args: Comma<Expr>> <rbrace: SpannedLocation<"}">> =>
            Box::new(Expr::Atom(Atom::ArrayLit((args, lbrace, rbrace))))
}

MapLit: Box<Expr> = {
	<lbrace: SpannedLocation<"{">> <args: Comma<NamedExpr>> <rbrace: SpannedLocation<"}">> =>
                Box::new(Expr::Atom(Atom::MapLit((args, lbrace, rbrace))))
}

#[inline]
ConditionalStatement: AstConditionalStatement = {
	<initial: If> <others: ElseIf*> <else_statement: Else?> => AstConditionalStatement::new(initial, others, else_statement),
}

If: AstIf = {
	<if_tok: SpannedLocation<"if">> <cond: Expr> <lbrace: SpannedLocation<"{">> <statements: Statement*> <rbrace: SpannedLocation<"}">> =>
        AstIf::new(if_tok, cond, lbrace, statements, rbrace),
}

ElseIf: AstElseIf = {
	<else_tok: SpannedLocation<"else">> <if_: If> => AstElseIf::new(else_tok, if_),
}

Else: AstElse = {
	<else_tok: SpannedLocation<"else">> <lbrace: SpannedLocation<"{">> <statements: Statement+> <rbrace: SpannedLocation<"}">> =>
        AstElse::new(else_tok, lbrace, statements, rbrace),
}

RepetitiveStatement: AstRepetitiveStatement = {
    <foreach_tok: SpannedLocation<"foreach">> <for_in_expr: ForInExpr> <lbrace: SpannedLocation<"{">> <statements:Statement*> <rbrace: SpannedLocation<"}">> =>
        AstRepetitiveStatement::new(foreach_tok, for_in_expr, lbrace, statements, rbrace),
}

ForInExpr: AstForInExpr = {
    <id: IdExpression> <in_tok: SpannedLocation<"in">> <expr: Expr> => AstForInExpr::new(id, in_tok, expr),
}

ControlStatement: AstControlStatement = {
	<break_tok: SpannedLocation<"break">> => AstControlStatement::Break(break_tok),
	<continue_tok: SpannedLocation<"continue">> => AstControlStatement::Continue(continue_tok),
}


extern {
    type Location = usize;
    type Error = lexer::LexicalError<'input>;

    enum lexer::Token<'input> {
        "+=" => Token {token: Tk::PlusEq, data: <&'input str>},
        "-=" => Token {token: Tk::MinusEq, data: <&'input str>},
        "*=" => Token {token: Tk::MulEq, data: <&'input str>},
        "/=" => Token {token: Tk::DivEq, data: <&'input str>},
        "%=" => Token {token: Tk::ModEq, data: <&'input str>},
        "+" => Token {token: Tk::Plus, data: <&'input str>},
        "-" => Token {token: Tk::Minus, data: <&'input str>},
        "*" => Token {token: Tk::Mul, data: <&'input str>},
        "/" => Token {token: Tk::Slash, data: <&'input str>},
        "%" => Token {token: Tk::Percent, data: <&'input str>},
        "==" => Token {token: Tk::EqualEqual, data: <&'input str>},
        ">=" => Token {token: Tk::GreaterEqual, data: <&'input str>},
        ">" => Token {token: Tk::GreaterThan, data: <&'input str>},
        "<=" => Token {token: Tk::LessEqual, data: <&'input str>},
        "<" => Token {token: Tk::LessThan, data: <&'input str>},
        "!=" => Token {token: Tk::NotEqual, data: <&'input str>},
        "=" => Token {token: Tk::Equal, data: <&'input str>},
        "<<" => Token {token: Tk::ShiftLeft, data: <&'input str>},
        ">>" => Token {token: Tk::ShiftRight, data: <&'input str>},
        "(" => Token {token: Tk::LParen, data: <&'input str>},
        "[" => Token {token: Tk::LBracket, data: <&'input str>},
        "{" => Token {token: Tk::LBrace, data: <&'input str>},
        ")" => Token {token: Tk::RParen, data: <&'input str>},
        "]" => Token {token: Tk::RBracket, data: <&'input str>},
        "}" => Token {token: Tk::RBrace, data: <&'input str>},
        "." => Token {token: Tk::Dot, data: <&'input str>},
        ":" => Token {token: Tk::Colon, data: <&'input str>},
        "?" => Token {token: Tk::QMark, data: <&'input str>},
        ";" => Token {token: Tk::Semicolon, data: <&'input str>},
        "," => Token {token: Tk::Comma, data: <&'input str>},
        "~" => Token {token: Tk::Tilda, data: <&'input str>},
        "and" => Token {token: Tk::And, data: <&'input str>},
        "or" => Token {token: Tk::Or, data: <&'input str>},
        "not" => Token {token: Tk::Not, data: <&'input str>},
        "in" => Token {token: Tk::In, data: <&'input str>},
        "let" => Token {token: Tk::Let, data: <&'input str>},
        "if" => Token {token: Tk::If, data: <&'input str>},
        "else" => Token {token: Tk::Else, data: <&'input str>},
        "foreach" => Token {token: Tk::ForEach, data: <&'input str>},
        "continue" => Token {token: Tk::Continue, data: <&'input str>},
        "break" => Token {token: Tk::Break, data: <&'input str>},
        "true" => Token {token: Tk::True, data: <&'input str>},
        "false" => Token {token: Tk::False, data: <&'input str>},
        r"([1-9][0-9]*|0x[0-9a-fA-F]+|0b[01]+|0[0-7]*)[uU]?[lL]?" => Token {token: Tk::Number, data: <&'input str>},
        r"[a-zA-Z_][a-zA-Z0-9_]*" => Token {token: Tk::Id, data: <&'input str>},
        r#"'(\\['nt\\]|[^'\\])+'"# => Token {token: Tk::String, data: <&'input str>},
        r#"'''([^']*|[']{1,2}[^'])*'''"# => Token {token: Tk::MultilineString, data: <&'input str>},
    }
}