rusty-javac 0.2.2

A Java compiler written in Rust.
Documentation
use crate::parser::{JavaSyntaxKind, Parser};
use crate::parser::{stmt, ty, type_decl};

pub(crate) fn expr(p: &mut Parser) {
    assignment_expr(p);
}

pub(crate) fn assignment_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    ternary_expr(p);
    if p.at_any(&[
        Eq, PlusEq, MinusEq, StarEq, SlashEq, AmpEq, PipeEq, CaretEq, PercentEq, LtLtEq, GtGtEq,
        GtGtGtEq,
    ]) {
        p.bump();
        assignment_expr(p);
    }
}

pub(crate) fn ternary_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    binary_expr(p, 0);
    if p.eat(Question) {
        expr(p);
        p.expect(Colon);
        ternary_expr(p);
    }
}

pub(crate) fn binary_expr(p: &mut Parser, min_prec: usize) {
    unary_expr(p);
    loop {
        let prec = binop_prec(p);
        if prec == 0 || prec < min_prec {
            break;
        }
        let op = p.kind();
        p.bump();
        if op == JavaSyntaxKind::InstanceofKw {
            ty::type_(p);
            if p.at(JavaSyntaxKind::Ident) {
                p.bump();
            }
        } else {
            binary_expr(p, prec + 1);
        }
    }
}

pub(crate) fn binop_prec(p: &Parser) -> usize {
    use JavaSyntaxKind::*;
    match p.kind() {
        PipePipe => 1,
        AmpAmp => 2,
        Pipe => 3,
        Caret => 4,
        Amp => 5,
        EqEq | Neq => 6,
        Lt | Gt | Le | Ge | InstanceofKw => 7,
        LtLt | GtGt | GtGtGt => 8,
        Plus | Minus => 9,
        Star | Slash | Percent => 10,
        _ => 0,
    }
}

pub(crate) fn unary_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    match p.kind() {
        Plus | Minus => {
            p.bump();
            unary_expr(p);
        }
        Inc | Dec => {
            p.bump();
            unary_expr(p);
        }
        Tilde | Bang => {
            p.bump();
            unary_expr(p);
        }
        _ => {
            cast_or_postfix_expr(p);
        }
    }
}

pub(crate) fn cast_or_postfix_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    if p.at(LParen) && is_cast(p) {
        let m = p.start();
        p.expect(LParen);
        ty::type_(p);
        p.expect(RParen);
        unary_expr(p);
        m.complete(p, CastExpr);
        postfix_suffix(p);
        return;
    }
    primary_expr(p);
    postfix_suffix(p);
}

pub(crate) fn is_cast(p: &Parser) -> bool {
    use JavaSyntaxKind::*;
    let mut la = p.lookahead();
    if !la.eat(LParen) {
        return false;
    }
    la.skip_type();
    la.skip_array_dims();
    if !la.at(RParen) {
        return false;
    }
    la.advance();
    la.skip_trivia();
    !la.at(Arrow)
}

pub(crate) fn postfix_suffix(p: &mut Parser) {
    use JavaSyntaxKind::*;
    loop {
        match p.kind() {
            Dot => {
                p.bump();
                if p.at(NewKw) {
                    new_expr(p);
                } else {
                    p.expect(Ident);
                }
            }
            LBrack => {
                p.bump();
                expr(p);
                p.expect(RBrack);
            }
            LParen => {
                argument_list(p);
            }
            Inc | Dec => {
                p.bump();
                break;
            }
            _ => break,
        }
    }
}

pub(crate) fn primary_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    match p.kind() {
        IntLiteral | LongLiteral | FloatLiteral | DoubleLiteral | CharLiteral | StringLiteral
        | TextBlockLiteral | TrueKw | FalseKw | NullKw => {
            let m = p.start();
            p.bump();
            m.complete(p, Literal);
        }
        ThisKw => {
            let m = p.start();
            p.bump();
            m.complete(p, ThisExpr);
        }
        SuperKw => {
            let m = p.start();
            p.bump();
            m.complete(p, SuperExpr);
        }
        NewKw => {
            new_expr(p);
        }
        SwitchKw => {
            stmt::switch_expr(p);
        }
        LParen if is_lambda_paren(p) => {
            lambda_expr_from_paren(p);
        }
        LParen => {
            let m = p.start();
            p.bump();
            expr(p);
            p.expect(RParen);
            m.complete(p, ParenExpr);
        }
        Ident if is_ident_lambda(p) => {
            lambda_expr_from_ident(p);
        }
        Ident => {
            name_expr(p);
        }
        _ => {
            p.err_and_bump(format!("unexpected token in expression: {:?}", p.kind()));
        }
    }
}

pub(crate) fn name_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    p.expect(Ident);
    while p.eat(Dot) {
        let sm = p.start();
        p.expect(Ident);
        sm.complete(p, MemberSelect);
    }
    m.complete(p, Name);
}

pub(crate) fn new_expr(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    p.expect(NewKw);
    ty::type_no_array(p);
    if p.at(LBrack) {
        p.bump();
        if !p.at(RBrack) {
            expr(p);
        }
        p.expect(RBrack);
        while p.eat(LBrack) {
            if !p.at(RBrack) {
                expr(p);
            }
            p.expect(RBrack);
        }
        if p.at(LBrace) {
            array_init(p);
        }
    } else {
        argument_list(p);
        if p.at(LBrace) {
            type_decl::class_body(p);
        }
    }
    m.complete(p, NewExpr);
}

pub(crate) fn array_init(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    p.expect(LBrace);
    while !p.at(RBrace) && p.kind() != Error {
        expr(p);
        if !p.eat(Comma) {
            break;
        }
    }
    p.eat(Comma);
    p.expect(RBrace);
    m.complete(p, ArrayInit);
}

pub(crate) fn argument_list(p: &mut Parser) {
    use JavaSyntaxKind::*;
    p.expect(LParen);
    if !p.at(RParen) {
        expr(p);
        while p.eat(Comma) {
            expr(p);
        }
    }
    p.expect(RParen);
}

pub(crate) fn expr_list(p: &mut Parser) {
    expr(p);
    while p.eat(JavaSyntaxKind::Comma) {
        expr(p);
    }
}

fn is_lambda_paren(p: &mut Parser) -> bool {
    use JavaSyntaxKind::*;
    let mut depth = 1;
    let mut i = p.pos + 1;
    loop {
        if i >= p.tokens.len() {
            return false;
        }
        match p.tokens[i].kind {
            LParen => depth += 1,
            RParen => {
                depth -= 1;
                if depth == 0 {
                    i += 1;
                    while i < p.tokens.len() && matches!(p.tokens[i].kind, Whitespace | Comment) {
                        i += 1;
                    }
                    return i < p.tokens.len() && p.tokens[i].kind == Arrow;
                }
            }
            Whitespace | Comment => {}
            Arrow => return false,
            _ => {}
        }
        i += 1;
    }
}

fn is_ident_lambda(p: &mut Parser) -> bool {
    use JavaSyntaxKind::*;
    let mut next = p.pos + 1;
    while next < p.tokens.len() && matches!(p.tokens[next].kind, Whitespace | Comment) {
        next += 1;
    }
    next < p.tokens.len() && p.tokens[next].kind == Arrow
}

fn lambda_expr_from_paren(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    p.expect(LParen);
    while !p.at(RParen) && p.kind() != Error {
        lambda_param(p);
        p.eat(Comma);
    }
    p.expect(RParen);
    p.expect(Arrow);
    lambda_body(p);
    m.complete(p, LambdaExpr);
}

fn lambda_expr_from_ident(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    lambda_param(p);
    p.expect(Arrow);
    lambda_body(p);
    m.complete(p, LambdaExpr);
}

fn lambda_param(p: &mut Parser) {
    use JavaSyntaxKind::*;
    let m = p.start();
    p.expect(Ident);
    m.complete(p, LambdaParam);
}

fn lambda_body(p: &mut Parser) {
    use JavaSyntaxKind::*;
    if p.at(LBrace) {
        stmt::block(p);
    } else {
        expr(p);
    }
}