ripex 0.2.0

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

use super::state::Parser;

pub fn parse_pat(parser: &mut Parser) -> Pat {
    parse_binding_pat(parser)
}

pub fn parse_binding_pat(parser: &mut Parser) -> Pat {
    let start = parser.current_pos();
    match parser.peek() {
        TokenKind::LBrace => parse_object_pat(parser),
        TokenKind::LBracket => parse_array_pat(parser),
        TokenKind::DotDotDot => parse_rest_pat(parser),
        _ => {
            if parser.peek() == TokenKind::Ident || parser.peek().is_keyword() {
                let ident = parse_binding_ident(parser);
                if parser.peek() == TokenKind::Colon {
                    let tok = parser.current_token().clone();
                    let err = parser.error(DiagnosticCode::UnexpectedToken, &tok);
                    parser.errors.push(err);
                    parser.advance();
                    let _inner = parse_binding_pat(parser);
                    let _span = parser.span_since(start);
                    if let Pat::Ident(bi) = ident {
                        Pat::Object(ObjectPat {
                            span: bi.span,
                            props: vec![ObjectPatProp::Shorthand(bi)],
                            rest: None,
                        })
                    } else {
                        ident
                    }
                } else {
                    ident
                }
            } else {
                let tok = parser.current_token().clone();
                let err = parser.error(DiagnosticCode::UnexpectedToken, &tok);
                parser.errors.push(err);
                parser.advance();
                Pat::Ident(BindingIdent {
                    span: tok.span,
                    id: Ident {
                        span: tok.span,
                        name: String::new(),
                        optional: false,
                    },
                    type_ann: None,
                    optional: false,
                })
            }
        }
    }
}

pub fn parse_assignment_pat(parser: &mut Parser) -> Pat {
    match parser.peek() {
        TokenKind::LBrace => parse_object_pat(parser),
        TokenKind::LBracket => parse_array_pat(parser),
        _ => {
            if parser.peek() == TokenKind::Ident || parser.peek().is_keyword() {
                let ident = parse_binding_ident(parser);
                if parser.peek() == TokenKind::Eq {
                    parse_assign_pat(parser, ident)
                } else {
                    ident
                }
            } else {
                let expr = parser.parse_expr();
                Pat::Expr(expr)
            }
        }
    }
}

pub fn parse_binding_ident(parser: &mut Parser) -> Pat {
    let _start = parser.current_pos();
    let tok = parser.advance();
    let name = tok.value.clone();
    let ident = Ident {
        span: tok.span,
        name,
        optional: false,
    };
    let type_ann = super::typescript::maybe_parse_ts_type_ann(parser);
    let span = if let Some(ref ta) = type_ann {
        Span::new(tok.span.start, ta.span().end)
    } else {
        tok.span
    };
    Pat::Ident(BindingIdent {
        span,
        id: ident,
        type_ann,
        optional: false,
    })
}

pub fn parse_object_pat(parser: &mut Parser) -> Pat {
    let start = parser.current_pos();
    parser.expect(TokenKind::LBrace).ok();
    let mut props = Vec::new();
    while parser.peek() != TokenKind::RBrace && !parser.is_eof() {
        if parser.peek() == TokenKind::Comma {
            parser.advance();
            continue;
        }
        if parser.peek() == TokenKind::DotDotDot {
            let rest = parse_rest_pat(parser);
            if let Pat::Rest(r) = rest {
                props.push(ObjectPatProp::Rest(r));
            }
            break;
        }
        props.push(parse_obj_pat_prop(parser));
        if parser.peek() == TokenKind::Comma {
            parser.advance();
        } else {
            break;
        }
    }
    parser.expect(TokenKind::RBrace).ok();
    let span = parser.span_since(start);
    Pat::Object(ObjectPat {
        span,
        props,
        rest: None,
    })
}

fn parse_obj_pat_prop(parser: &mut Parser) -> ObjectPatProp {
    let start = parser.current_pos();
    if parser.peek() == TokenKind::Ident || parser.peek().is_keyword() {
        let ident = parse_ident(parser);
        if parser.peek() == TokenKind::Colon {
            parser.advance();
            let value = Box::new(parse_binding_pat(parser));
            let span = parser.span_since(start);
            let key = PropName::Ident(ident.clone());
            ObjectPatProp::KeyValue(KeyValuePatProp { span, key, value })
        } else if parser.peek() == TokenKind::Eq {
            parser.advance();
            let right = parser.parse_assignment_expr();
            let binding = Pat::Ident(BindingIdent {
                span: ident.span,
                id: ident.clone(),
                type_ann: None,
                optional: false,
            });
            let value = Pat::Assign(AssignPat {
                span: parser.span_since(start),
                left: Box::new(binding),
                right,
            });
            ObjectPatProp::KeyValue(KeyValuePatProp {
                span: parser.span_since(start),
                key: PropName::Ident(ident),
                value: Box::new(value),
            })
        } else {
            let type_ann = super::typescript::maybe_parse_ts_type_ann(parser);
            let span = if type_ann.is_some() {
                parser.span_since(start)
            } else {
                ident.span
            };
            ObjectPatProp::Shorthand(BindingIdent {
                span,
                id: ident,
                type_ann,
                optional: false,
            })
        }
    } else {
        let tok = parser.current_token().clone();
        let err = parser.error(DiagnosticCode::UnexpectedToken, &tok);
        parser.errors.push(err);
        parser.advance();
        ObjectPatProp::Shorthand(BindingIdent {
            span: tok.span,
            id: Ident {
                span: tok.span,
                name: String::new(),
                optional: false,
            },
            type_ann: None,
            optional: false,
        })
    }
}

pub fn parse_array_pat(parser: &mut Parser) -> Pat {
    let start = parser.current_pos();
    parser.expect(TokenKind::LBracket).ok();
    let mut elements = Vec::new();
    let mut rest = None;
    while parser.peek() != TokenKind::RBracket && !parser.is_eof() {
        if parser.peek() == TokenKind::Comma {
            elements.push(None);
            parser.advance();
            continue;
        }
        if parser.peek() == TokenKind::DotDotDot {
            if let Pat::Rest(rp) = parse_rest_pat(parser) {
                rest = Some(Box::new(rp));
            }
            break;
        }
        elements.push(Some(parse_binding_pat(parser)));
        if parser.peek() == TokenKind::Comma {
            parser.advance();
        } else {
            break;
        }
    }
    parser.expect(TokenKind::RBracket).ok();
    let span = parser.span_since(start);
    Pat::Array(ArrayPat {
        span,
        elements,
        rest,
    })
}

pub fn parse_rest_pat(parser: &mut Parser) -> Pat {
    let start = parser.current_pos();
    parser.expect(TokenKind::DotDotDot).ok();
    let arg = Box::new(parse_binding_pat(parser));
    let span = parser.span_since(start);
    Pat::Rest(RestPat { span, arg })
}

pub fn parse_assign_pat(parser: &mut Parser, left: Pat) -> Pat {
    let start = parser.current_pos();
    parser.expect(TokenKind::Eq).ok();
    let right = parser.parse_assignment_expr();
    let span = parser.span_since(start);
    Pat::Assign(AssignPat {
        span,
        left: Box::new(left),
        right,
    })
}

pub fn parse_ident(parser: &mut Parser) -> Ident {
    let tok = parser.advance();
    Ident {
        span: tok.span,
        name: tok.value,
        optional: false,
    }
}

pub fn convert_expr_to_pat(parser: &mut Parser, expr: ExprRef, _span: Span) -> Pat {
    match parser.ast[expr].clone() {
        Expr::Ident(id) => Pat::Ident(BindingIdent {
            span: id.span,
            id,
            type_ann: None,
            optional: false,
        }),
        _ => Pat::Expr(expr),
    }
}

pub fn parse_prop_name(parser: &mut Parser) -> PropName {
    let _start = parser.current_pos();
    match parser.peek() {
        TokenKind::String => {
            let tok = parser.advance();
            PropName::Str(StrLit {
                span: tok.span,
                value: tok.value,
                raw: String::new(),
            })
        }
        TokenKind::Number | TokenKind::BigInt => {
            let tok = parser.advance();
            let val: f64 = tok.value.parse().unwrap_or(0.0);
            PropName::Num(NumLit {
                span: tok.span,
                value: val,
                raw: tok.value,
            })
        }
        TokenKind::LBracket => {
            parser.advance();
            let expr = parser.parse_assignment_expr();
            parser.expect(TokenKind::RBracket).ok();
            PropName::Computed(expr)
        }
        TokenKind::PrivateName => {
            let tok = parser.advance();
            PropName::Ident(Ident {
                span: tok.span,
                name: tok.value,
                optional: false,
            })
        }
        _ => {
            let tok = parser.advance();
            PropName::Ident(Ident {
                span: tok.span,
                name: tok.value,
                optional: false,
            })
        }
    }
}