erl_parse 0.0.8

Erlang source code parser
Documentation
use erl_tokenize::tokens::{AtomToken, SymbolToken, VariableToken};
use erl_tokenize::values::{Keyword, Symbol};
use erl_tokenize::{LexicalToken, Position, PositionRange};

use super::commons::parts::BinaryOp;
use super::exprs;
use super::Literal;
use crate::traits::{Parse, TokenRead};
use crate::{ErrorKind, Parser, Result};

#[derive(Debug, Clone)]
pub enum Expr {
    Literal(Literal),
    Variable(VariableToken),
    Tuple(Box<exprs::Tuple>),
    Map(Box<exprs::Map>),
    MapUpdate(Box<exprs::MapUpdate>),
    Record(Box<exprs::Record>),
    RecordUpdate(Box<exprs::RecordUpdate>),
    RecordFieldIndex(Box<exprs::RecordFieldIndex>),
    RecordFieldAccess(Box<exprs::RecordFieldAccess>),
    List(Box<exprs::List>),
    ListComprehension(Box<exprs::ListComprehension>),
    Bits(Box<exprs::Bits>),
    BitsComprehension(Box<exprs::BitsComprehension>),
    Fun(Box<exprs::Fun>),
    Parenthesized(Box<exprs::Parenthesized>),
    FunCall(Box<exprs::FunCall>),
    UnaryOpCall(Box<exprs::UnaryOpCall>),
    BinaryOpCall(Box<exprs::BinaryOpCall>),
    Match(Box<exprs::Match>),
    Block(Box<exprs::Block>),
    Catch(Box<exprs::Catch>),
    If(Box<exprs::If>),
    Case(Box<exprs::Case>),
    Receive(Box<exprs::Receive>),
    Try(Box<exprs::Try>),
}
impl Parse for Expr {
    fn parse_non_left_recor<T>(parser: &mut Parser<T>) -> Result<Self>
    where
        T: TokenRead,
    {
        let kind = track!(parser.peek(|parser| HeadKind::guess(parser)))?;
        let expr = match kind {
            HeadKind::Literal => Expr::Literal(track!(parser.parse())?),
            HeadKind::Variable => Expr::Variable(track!(parser.parse())?),
            HeadKind::Tuple => Expr::Tuple(track!(parser.parse())?),
            HeadKind::Map => Expr::Map(track!(parser.parse())?),
            HeadKind::Record => Expr::Record(track!(parser.parse())?),
            HeadKind::RecordFieldIndex => Expr::RecordFieldIndex(track!(parser.parse())?),
            HeadKind::List => Expr::List(track!(parser.parse())?),
            HeadKind::ListComprehension => Expr::ListComprehension(track!(parser.parse())?),
            HeadKind::Bits => Expr::Bits(track!(parser.parse())?),
            HeadKind::BitsComprehension => Expr::BitsComprehension(track!(parser.parse())?),
            HeadKind::Fun => Expr::Fun(track!(parser.parse())?),
            HeadKind::UnaryOpCall => Expr::UnaryOpCall(track!(parser.parse())?),
            HeadKind::Parenthesized => Expr::Parenthesized(track!(parser.parse())?),
            HeadKind::Block => Expr::Block(track!(parser.parse())?),
            HeadKind::Catch => Expr::Catch(track!(parser.parse())?),
            HeadKind::If => Expr::If(track!(parser.parse())?),
            HeadKind::Case => Expr::Case(track!(parser.parse())?),
            HeadKind::Receive => Expr::Receive(track!(parser.parse())?),
            HeadKind::Try => Expr::Try(track!(parser.parse())?),
            _ => track_panic!(ErrorKind::InvalidInput, "unreachable"),
        };
        Ok(expr)
    }
    fn parse<T>(parser: &mut Parser<T>) -> Result<Self>
    where
        T: TokenRead,
    {
        if let Ok(expr) = parser.transaction(Parser::parse) {
            return Ok(Expr::Match(expr));
        }

        let mut head = track!(Self::parse_non_left_recor(parser))?;
        loop {
            let kind = track!(parser.peek(|parser| TailKind::guess(parser)))?;
            head = match kind {
                TailKind::FunCall => Expr::FunCall(track!(parser.parse_tail(head))?),
                TailKind::MapUpdate => Expr::MapUpdate(track!(parser.parse_tail(head))?),
                TailKind::RecordUpdate => Expr::RecordUpdate(track!(parser.parse_tail(head))?),
                TailKind::RecordFieldAccess => {
                    Expr::RecordFieldAccess(track!(parser.parse_tail(head))?)
                }
                TailKind::BinaryOpCall => Expr::BinaryOpCall(track!(parser.parse_tail(head))?),
                TailKind::None => break,
            };
        }
        Ok(head)
    }
}
impl PositionRange for Expr {
    fn start_position(&self) -> Position {
        match *self {
            Expr::Literal(ref x) => x.start_position(),
            Expr::Variable(ref x) => x.start_position(),
            Expr::Tuple(ref x) => x.start_position(),
            Expr::Map(ref x) => x.start_position(),
            Expr::MapUpdate(ref x) => x.start_position(),
            Expr::Record(ref x) => x.start_position(),
            Expr::RecordUpdate(ref x) => x.start_position(),
            Expr::RecordFieldIndex(ref x) => x.start_position(),
            Expr::RecordFieldAccess(ref x) => x.start_position(),
            Expr::List(ref x) => x.start_position(),
            Expr::ListComprehension(ref x) => x.start_position(),
            Expr::Bits(ref x) => x.start_position(),
            Expr::BitsComprehension(ref x) => x.start_position(),
            Expr::Parenthesized(ref x) => x.start_position(),
            Expr::Fun(ref x) => x.start_position(),
            Expr::FunCall(ref x) => x.start_position(),
            Expr::UnaryOpCall(ref x) => x.start_position(),
            Expr::BinaryOpCall(ref x) => x.start_position(),
            Expr::Match(ref x) => x.start_position(),
            Expr::Block(ref x) => x.start_position(),
            Expr::Catch(ref x) => x.start_position(),
            Expr::If(ref x) => x.start_position(),
            Expr::Case(ref x) => x.start_position(),
            Expr::Receive(ref x) => x.start_position(),
            Expr::Try(ref x) => x.start_position(),
        }
    }
    fn end_position(&self) -> Position {
        match *self {
            Expr::Literal(ref x) => x.end_position(),
            Expr::Variable(ref x) => x.end_position(),
            Expr::Tuple(ref x) => x.end_position(),
            Expr::Map(ref x) => x.end_position(),
            Expr::MapUpdate(ref x) => x.end_position(),
            Expr::Record(ref x) => x.end_position(),
            Expr::RecordUpdate(ref x) => x.end_position(),
            Expr::RecordFieldIndex(ref x) => x.end_position(),
            Expr::RecordFieldAccess(ref x) => x.end_position(),
            Expr::List(ref x) => x.end_position(),
            Expr::ListComprehension(ref x) => x.end_position(),
            Expr::Bits(ref x) => x.end_position(),
            Expr::BitsComprehension(ref x) => x.end_position(),
            Expr::Parenthesized(ref x) => x.end_position(),
            Expr::Fun(ref x) => x.end_position(),
            Expr::FunCall(ref x) => x.end_position(),
            Expr::UnaryOpCall(ref x) => x.end_position(),
            Expr::BinaryOpCall(ref x) => x.end_position(),
            Expr::Match(ref x) => x.end_position(),
            Expr::Block(ref x) => x.end_position(),
            Expr::Catch(ref x) => x.end_position(),
            Expr::If(ref x) => x.end_position(),
            Expr::Case(ref x) => x.end_position(),
            Expr::Receive(ref x) => x.end_position(),
            Expr::Try(ref x) => x.end_position(),
        }
    }
}

#[derive(Debug)]
enum HeadKind {
    Literal,
    Variable,
    Tuple,
    Map,
    Record,
    RecordFieldIndex,
    List,
    ListComprehension,
    Bits,
    BitsComprehension,
    Fun,
    UnaryOpCall,
    Parenthesized,
    Block,
    Catch,
    If,
    Case,
    Receive,
    Try,
    Annotated,
}
impl HeadKind {
    fn guess<T: TokenRead>(parser: &mut Parser<T>) -> Result<Self> {
        Ok(match track!(parser.parse())? {
            LexicalToken::Symbol(t) => match t.value() {
                Symbol::OpenBrace => HeadKind::Tuple,
                Symbol::DoubleLeftAngle => {
                    let maybe_comprehension = parser.parse::<Expr>().is_ok()
                        && parser
                            .expect::<SymbolToken>(&Symbol::DoubleVerticalBar)
                            .is_ok();
                    if maybe_comprehension {
                        HeadKind::BitsComprehension
                    } else {
                        HeadKind::Bits
                    }
                }
                Symbol::OpenParen => HeadKind::Parenthesized,
                Symbol::OpenSquare => {
                    let maybe_comprehension = parser.parse::<Expr>().is_ok()
                        && parser
                            .expect::<SymbolToken>(&Symbol::DoubleVerticalBar)
                            .is_ok();
                    if maybe_comprehension {
                        HeadKind::ListComprehension
                    } else {
                        HeadKind::List
                    }
                }
                Symbol::Sharp => {
                    if parser.parse::<AtomToken>().is_ok() {
                        let next = parser.parse::<SymbolToken>().map(|t| t.value()).ok();
                        if next == Some(Symbol::Dot) {
                            HeadKind::RecordFieldIndex
                        } else {
                            HeadKind::Record
                        }
                    } else {
                        HeadKind::Map
                    }
                }
                Symbol::Plus | Symbol::Hyphen => HeadKind::UnaryOpCall,
                _ => track_panic!(ErrorKind::UnexpectedToken(t.into())),
            },
            LexicalToken::Keyword(t) => match t.value() {
                Keyword::Begin => HeadKind::Block,
                Keyword::Catch => HeadKind::Catch,
                Keyword::If => HeadKind::If,
                Keyword::Case => HeadKind::Case,
                Keyword::Receive => HeadKind::Receive,
                Keyword::Try => HeadKind::Try,
                Keyword::Fun => HeadKind::Fun,
                Keyword::Bnot | Keyword::Not => HeadKind::UnaryOpCall,
                _ => track_panic!(ErrorKind::UnexpectedToken(t.into())),
            },
            LexicalToken::Variable(_) => {
                if parser.expect::<SymbolToken>(&Symbol::DoubleColon).is_ok() {
                    HeadKind::Annotated
                } else {
                    HeadKind::Variable
                }
            }
            _ => HeadKind::Literal,
        })
    }
}

#[derive(Debug)]
enum TailKind {
    FunCall,
    MapUpdate,
    RecordUpdate,
    RecordFieldAccess,
    BinaryOpCall,
    None,
}
impl TailKind {
    fn guess<T: TokenRead>(parser: &mut Parser<T>) -> Result<Self> {
        let is_eos = track!(parser.eos())?;
        if is_eos {
            return Ok(TailKind::None);
        }

        let token = track!(parser.parse::<LexicalToken>())?;
        Ok(match token.as_symbol_token().map(SymbolToken::value) {
            Some(Symbol::OpenParen) | Some(Symbol::Colon) => TailKind::FunCall,
            Some(Symbol::Sharp) => {
                if parser
                    .parse::<LexicalToken>()
                    .ok()
                    .and_then(|t| t.as_atom_token().map(|_| ()))
                    .is_some()
                {
                    let is_record_update = parser
                        .parse::<LexicalToken>()
                        .ok()
                        .and_then(|t| t.as_symbol_token().map(|t| t.value() == Symbol::OpenBrace))
                        .unwrap_or(false);
                    if is_record_update {
                        TailKind::RecordUpdate
                    } else {
                        TailKind::RecordFieldAccess
                    }
                } else {
                    TailKind::MapUpdate
                }
            }
            _ => {
                if BinaryOp::from_token(token).is_ok() {
                    TailKind::BinaryOpCall
                } else {
                    TailKind::None
                }
            }
        })
    }
}