ldscript-parser 0.3.0

A Linker Script file parser
Documentation
use expressions::expression;
use expressions::Expression;
use idents::{string, symbol};
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::combinator::opt;
use nom::IResult;
use whitespace::opt_space;

#[derive(Debug, PartialEq)]
pub enum AssignOperator {
    Equals,
    Plus,
    Minus,
    Multiply,
    Divide,
    ShiftLeft,
    ShiftRight,
    And,
    Or,
}

#[derive(Debug, PartialEq)]
pub enum Statement {
    Assign {
        name: String,
        operator: AssignOperator,
        expression: Box<Expression>,
    },
    Hidden {
        name: String,
        expression: Box<Expression>,
    },
    Provide {
        name: String,
        expression: Box<Expression>,
    },
    ProvideHidden {
        name: String,
        expression: Box<Expression>,
    },
    Assert {
        expr: Box<Expression>,
        text: String,
    },
}

fn assign_operator(input: &str) -> IResult<&str, AssignOperator> {
    map(
        alt((
            tag("="),
            tag("+="),
            tag("-="),
            tag("*="),
            tag("/="),
            tag("<<="),
            tag(">>="),
            tag("&="),
            tag("|="),
        )),
        |op: &str| match op {
            "=" => AssignOperator::Equals,
            "+=" => AssignOperator::Plus,
            "-=" => AssignOperator::Minus,
            "*=" => AssignOperator::Multiply,
            "/=" => AssignOperator::Divide,
            "<<=" => AssignOperator::ShiftLeft,
            ">>=" => AssignOperator::ShiftRight,
            "&=" => AssignOperator::And,
            "|=" => AssignOperator::Or,
            _ => panic!("wrong operator"),
        },
    )(input)
}

fn special_assign(input: &str) -> IResult<&str, Statement> {
    let (input, keyword) = alt((tag("PROVIDE_HIDDEN"), tag("PROVIDE"), tag("HIDDEN")))(input)?;
    let (input, _) = wsc!(tag("("))(input)?;
    let (input, name) = symbol(input)?;
    let (input, _) = wsc!(tag("="))(input)?;
    let (input, expr) = expression(input)?;
    let (input, _) = wsc!(tag(")"))(input)?;
    let (input, _) = tag(";")(input)?;
    Ok((
        input,
        match keyword {
            "HIDDEN" => Statement::Hidden {
                name: name.into(),
                expression: Box::new(expr),
            },
            "PROVIDE" => Statement::Provide {
                name: name.into(),
                expression: Box::new(expr),
            },
            "PROVIDE_HIDDEN" => Statement::ProvideHidden {
                name: name.into(),
                expression: Box::new(expr),
            },
            _ => panic!("invalid assign keyword"),
        },
    ))
}

fn assign(input: &str) -> IResult<&str, Statement> {
    let (input, name) = symbol(input)?;
    let (input, op) = wsc!(assign_operator)(input)?;
    let (input, expr) = expression(input)?;
    let (input, _) = opt_space(input)?;
    let (input, _) = tag(";")(input)?;
    Ok((
        input,
        Statement::Assign {
            name: name.into(),
            operator: op,
            expression: Box::new(expr),
        },
    ))
}

fn assert_stmt(input: &str) -> IResult<&str, Statement> {
    let (input, _) = tag("ASSERT")(input)?;
    let (input, _) = wsc!(tag("("))(input)?;
    let (input, expr) = expression(input)?;
    let (input, _) = wsc!(tag(","))(input)?;
    let (input, text) = string(input)?;
    let (input, _) = wsc!(tag(")"))(input)?;
    let (input, _) = opt(tag(";"))(input)?;
    Ok((
        input,
        Statement::Assert {
            expr: Box::new(expr),
            text: text.into(),
        },
    ))
}

pub fn statement(input: &str) -> IResult<&str, Statement> {
    alt((special_assign, assign, assert_stmt))(input)
}

#[cfg(test)]
mod tests {
    use expressions::Expression;
    use statements::*;

    #[test]
    fn test_statement() {
        assert_done!(
            statement("A = 11 ;"),
            Statement::Assign {
                name: "A".into(),
                operator: AssignOperator::Equals,
                expression: Box::new(Expression::Number(11)),
            }
        );
        assert_done!(
            statement("PROVIDE ( x = x ) ;"),
            Statement::Provide {
                name: "x".into(),
                expression: Box::new(Expression::Ident("x".into())),
            }
        );
        assert_done!(statement("PROBLEM += HELLO ( WORLD , 0 ) + 1 ;"));
    }
}