ripex 0.3.0

Multi-language structural parsing and fact extraction.
Documentation
use super::state::Parser;
use crate::diagnostics::DiagnosticCode;
use crate::js::ast::*;
use crate::js::lexer::TokenKind;

pub fn parse_jsx_element(parser: &mut Parser) -> ExprRef {
    let _start = parser.current_pos();
    let elem = parse_jsx_element_inner(parser);
    parser.ast.alloc(Expr::JSXElement(elem))
}

fn parse_jsx_element_inner(parser: &mut Parser) -> JSXElement {
    let start = parser.current_pos();
    let opening = parse_jsx_opening(parser);
    let mut children = Vec::new();

    if !opening.self_closing {
        while !(parser.is_eof()
            || parser.peek() == TokenKind::Lt && parser.peek_ahead(1) == TokenKind::Slash)
        {
            if parser.peek() == TokenKind::RBrace || parser.peek() == TokenKind::Eof {
                break;
            }
            children.push(parse_jsx_child(parser));
        }

        let closing = if parser.peek() == TokenKind::Lt {
            Some(parse_jsx_closing(parser))
        } else {
            None
        };
        JSXElement {
            span: parser.span_since(start),
            opening,
            children,
            closing,
        }
    } else {
        JSXElement {
            span: parser.span_since(start),
            opening,
            children,
            closing: None,
        }
    }
}

fn parse_jsx_opening(parser: &mut Parser) -> JSXOpening {
    let start = parser.current_pos();
    parser.expect(TokenKind::Lt).ok();
    let name = parse_jsx_name(parser);
    let mut attrs = Vec::new();

    while parser.peek() != TokenKind::Gt && parser.peek() != TokenKind::Slash && !parser.is_eof() {
        if parser.peek() == TokenKind::LBrace {
            attrs.push(parse_jsx_spread_attr(parser));
        } else {
            attrs.push(parse_jsx_attr(parser));
        }
    }

    let self_closing = parser.peek() == TokenKind::Slash;
    if self_closing {
        parser.advance();
    }

    parser.expect(TokenKind::Gt).ok();

    JSXOpening {
        span: parser.span_since(start),
        name,
        attrs,
        self_closing,
    }
}

fn parse_jsx_closing(parser: &mut Parser) -> JSXClosing {
    let start = parser.current_pos();
    parser.expect(TokenKind::Lt).ok();
    parser.expect(TokenKind::Slash).ok();
    let name = parse_jsx_name(parser);
    parser.expect(TokenKind::Gt).ok();
    JSXClosing {
        span: parser.span_since(start),
        name,
    }
}

fn parse_jsx_name(parser: &mut Parser) -> JSXName {
    let start = parser.current_pos();
    match parser.peek() {
        TokenKind::Ident | TokenKind::This => {
            let tok = parser.advance();
            let id = JSXIdent {
                span: tok.span,
                name: tok.value,
            };

            if parser.peek() == TokenKind::Colon {
                parser.advance();
                let ns = id;
                let tok = parser.advance();
                let name = JSXIdent {
                    span: tok.span,
                    name: tok.value,
                };
                JSXName::Namespace(JSXNamespace {
                    span: parser.span_since(start),
                    namespace: ns,
                    name,
                })
            } else {
                let mut name = JSXName::Ident(id);
                while parser.peek() == TokenKind::Dot {
                    parser.advance();
                    let tok = parser.advance();
                    let prop = JSXIdent {
                        span: tok.span,
                        name: tok.value,
                    };
                    name = JSXName::Member(JSXMember {
                        span: parser.span_since(start),
                        object: Box::new(name),
                        property: prop,
                    });
                }
                name
            }
        }
        _ => {
            let tok = parser.current_token().clone();
            let err = parser.error(DiagnosticCode::InvalidJSX, &tok);
            parser.errors.push(err);
            parser.advance();
            JSXName::Ident(JSXIdent {
                span: tok.span,
                name: String::new(),
            })
        }
    }
}

fn parse_jsx_attr(parser: &mut Parser) -> JSXAttr {
    let start = parser.current_pos();
    let tok = parser.advance();
    let name = JSXName::Ident(JSXIdent {
        span: tok.span,
        name: tok.value,
    });

    let value = if parser.peek() == TokenKind::Eq {
        parser.advance();
        Some(parse_jsx_attr_val(parser))
    } else {
        None
    };

    JSXAttr::Attr(JSXAttribute {
        span: parser.span_since(start),
        name,
        value,
    })
}

fn parse_jsx_spread_attr(parser: &mut Parser) -> JSXAttr {
    let start = parser.current_pos();
    parser.advance();
    let arg = super::expressions::parse_assign_expr(parser);
    parser.expect(TokenKind::RBrace).ok();
    JSXAttr::Spread(SpreadExpr {
        span: parser.span_since(start),
        arg,
    })
}

fn parse_jsx_attr_val(parser: &mut Parser) -> JSXAttrVal {
    let _start = parser.current_pos();
    match parser.peek() {
        TokenKind::String => {
            let tok = parser.advance();
            JSXAttrVal::Str(StrLit {
                span: tok.span,
                value: tok.value,
                raw: String::new(),
            })
        }
        TokenKind::LBrace => {
            parser.advance();
            let expr = super::expressions::parse_assign_expr(parser);
            parser.expect(TokenKind::RBrace).ok();
            JSXAttrVal::Expr(expr)
        }
        TokenKind::Lt => {
            let elem = parse_jsx_element_inner(parser);
            JSXAttrVal::Element(elem)
        }
        _ => {
            let tok = parser.advance();
            JSXAttrVal::Str(StrLit {
                span: tok.span,
                value: tok.value,
                raw: String::new(),
            })
        }
    }
}

fn parse_jsx_child(parser: &mut Parser) -> JSXChild {
    match parser.peek() {
        TokenKind::LBrace => {
            parser.advance();
            let expr = super::expressions::parse_assign_expr(parser);
            parser.expect(TokenKind::RBrace).ok();
            JSXChild::Expr(expr)
        }
        TokenKind::Lt => {
            if parser.peek_ahead(1) == TokenKind::Slash {
                JSXChild::Text(String::new())
            } else if parser.peek_ahead(1) == TokenKind::Gt {
                let frag = parse_jsx_fragment_inner(parser);
                JSXChild::Fragment(frag)
            } else {
                let elem = parse_jsx_element_inner(parser);
                JSXChild::Element(elem)
            }
        }
        _ => {
            let mut text = String::new();
            while !parser.is_eof()
                && parser.peek() != TokenKind::LBrace
                && parser.peek() != TokenKind::Lt
            {
                let tok = parser.advance();
                text.push_str(&tok.value);
            }
            JSXChild::Text(text)
        }
    }
}

pub fn parse_jsx_fragment(parser: &mut Parser) -> ExprRef {
    let frag = parse_jsx_fragment_inner(parser);
    parser.ast.alloc(Expr::JSXFragment(frag))
}

fn parse_jsx_fragment_inner(parser: &mut Parser) -> JSXFragment {
    let start = parser.current_pos();
    parser.expect(TokenKind::Lt).ok();
    parser.expect(TokenKind::Gt).ok();

    let mut children = Vec::new();
    while !parser.is_eof() {
        if parser.peek() == TokenKind::Lt && parser.peek_ahead(1) == TokenKind::Slash {
            break;
        }
        children.push(parse_jsx_child(parser));
    }

    parser.expect(TokenKind::Lt).ok();
    parser.expect(TokenKind::Slash).ok();
    parser.expect(TokenKind::Gt).ok();

    JSXFragment {
        span: parser.span_since(start),
        children,
    }
}