ldscript-parser 0.1.0

A Linker Script file parser
Documentation
use statements::{statement, Statement};
use commands::{command, Command};
use idents::symbol;
use idents::pattern;
use expressions::Expression;
use expressions::expression;
use whitespace::opt_space;

#[derive(Debug, PartialEq)]
pub enum SectionCommand {
    Statement(Statement),
    Command(Command),
    OutputSection {
        name: String,
        vma_address: Option<Box<Expression>>,
        s_type: Option<OutputSectionType>,
        lma_address: Option<Box<Expression>>,
        section_align: Option<Box<Expression>>,
        align_with_input: bool,
        subsection_align: Option<Box<Expression>>,
        constraint: Option<OutputSectionConstraint>,
        content: Vec<OutputSectionCommand>,
        region: Option<String>,
        lma_region: Option<String>,
        fillexp: Option<Box<Expression>>,
    },
}

#[derive(Debug, PartialEq)]
pub enum OutputSectionCommand {
    Statement(Statement),
    Fill { expr: Box<Expression> },
    Data {
        d_type: DataType,
        value: Box<Expression>,
    },
    InputSection {
        file: SectionPattern,
        sections: Vec<SectionPattern>,
    },
    KeepInputSection {
        file: SectionPattern,
        sections: Vec<SectionPattern>,
    },
}

#[derive(Debug, PartialEq)]
pub enum DataType {
    Byte,
    Short,
    Long,
    Quad,
}

#[derive(Debug, PartialEq)]
pub enum SectionPattern {
    Simple(String),
    SortByName(String),
    SortByAlignment(String),
    SortByInitPriority(String),
    SortNone(String),
    ExcludeFile {
        files: Vec<String>,
        pattern: Box<SectionPattern>,
    },
}

#[derive(Debug, PartialEq)]
pub enum OutputSectionType {
    NoLoad,
    DSect,
    Copy,
    Info,
    Overlay,
}

#[derive(Debug, PartialEq)]
pub enum OutputSectionConstraint {
    OnlyIfRo,
    OnlyIfRw,
}

named!(output_section_type<&str, OutputSectionType>, alt_complete!(
    map!(tag!("(NOLOAD)"), |_| OutputSectionType::NoLoad) |
    map!(tag!("(DSECT)"), |_| OutputSectionType::DSect) |
    map!(tag!("(COPY)"), |_| OutputSectionType::Copy) |
    map!(tag!("(INFO)"), |_| OutputSectionType::Info) |
    map!(tag!("(OVERLAY)"), |_| OutputSectionType::Overlay)
));

named!(output_section_constraint<&str, OutputSectionConstraint>, alt_complete!(
    map!(tag!("ONLY_IF_RO"), |_| OutputSectionConstraint::OnlyIfRo) |
    map!(tag!("ONLY_IF_RW"), |_| OutputSectionConstraint::OnlyIfRw)
));

named!(sorted_sp<&str, SectionPattern>, do_parse!(
    keyword: alt_complete!(
        tag!("SORT_BY_NAME") |
        tag!("SORT_BY_ALIGNMENT") |
        tag!("SORT_BY_INIT_PRIORITY") |
        tag!("SORT_NONE") |
        tag!("SORT")
    )
    >>
    wsc!(tag!("("))
    >>
    inner: pattern
    >>
    opt_space
    >>
    tag!(")")
    >>
    (match keyword {
        "SORT" | "SORT_BY_NAME" => SectionPattern::SortByName(inner.into()),
        "SORT_BY_ALIGNMENT" => SectionPattern::SortByAlignment(inner.into()),
        "SORT_BY_INIT_PRIORITY" => SectionPattern::SortByInitPriority(inner.into()),
        "SORT_NONE" => SectionPattern::SortNone(inner.into()),
        _ => panic!("wrong sort keyword"),
    })
));

named!(exclude_file_sp<&str, SectionPattern>, do_parse!(
    tag!("EXCLUDE_FILE")
    >>
    opt_space
    >>
    tag!("(")
    >>
    files: wsc!(many1!(
        map!(pattern, String::from)
    ))
    >>
    tag!(")")
    >>
    opt_space
    >>
    inner: section_pattern
    >>
    (SectionPattern::ExcludeFile {
        files: files,
        pattern: Box::new(inner),
    })
));

named!(simple_sp<&str, SectionPattern>, map!(
    pattern,
    |x: &str| SectionPattern::Simple(x.into())
));

named!(section_pattern<&str, SectionPattern>, alt!(
    exclude_file_sp | sorted_sp | simple_sp
));

named!(data_osc<&str, OutputSectionCommand>, do_parse!(
    d_type: alt_complete!(
        tag!("BYTE") | tag!("SHORT") | tag!("LONG") | tag!("QUAD")
    )
    >>
    wsc!(tag!("("))
    >>
    value: expression
    >>
    wsc!(tag!(")"))
    >>
    opt_complete!(tag!(";"))
    >>
    (OutputSectionCommand::Data {
        d_type: match d_type {
            "BYTE" => DataType::Byte,
            "SHORT" => DataType::Short,
            "LONG" => DataType::Long,
            "QUAD" => DataType::Quad,
            _ => panic!("invalid data type")
        },
        value: Box::new(value)
    })
));

named!(fill_osc<&str, OutputSectionCommand>, do_parse!(
    tag!("FILL")
    >>
    wsc!(tag!("("))
    >>
    expr: expression
    >>
    wsc!(tag!(")"))
    >>
    opt_complete!(tag!(";"))
    >>
    (OutputSectionCommand::Fill {
        expr: Box::new(expr)
    })
));

named!(statement_osc<&str, OutputSectionCommand>, map!(
    statement,
    |stmt| OutputSectionCommand::Statement(stmt)
));

named!(input_osc<&str, OutputSectionCommand>, do_parse!(
    file: section_pattern
    >>
    opt_space
    >>
    sections: opt_complete!(
        delimited!(
            wsc!(tag!("(")),
            wsc!(many1!(
                section_pattern
            )),
            wsc!(tag!(")"))
        )
    )
    >>
    (OutputSectionCommand::InputSection {
        file: file,
        sections: match sections {
            Some(s) => s,
            None => Vec::new(),
        },
    })
));

named!(keep_osc<&str, OutputSectionCommand>, do_parse!(
    tag!("KEEP")
    >>
    wsc!(tag!("("))
    >>
    inner: input_osc
    >>
    wsc!(tag!(")"))
    >>
    (match inner {
        OutputSectionCommand::InputSection {
            file,
            sections
        } => OutputSectionCommand::KeepInputSection {
            file: file,
            sections: sections,
        },
        _ => panic!("wrong output section command"),
    })
));

named!(output_section_command<&str, OutputSectionCommand>, alt_complete!(
    statement_osc | keep_osc | data_osc | fill_osc | input_osc
));

named!(statement_sc<&str, SectionCommand>, map!(
    statement,
    |stmt| SectionCommand::Statement(stmt)
));

named!(command_sc<&str, SectionCommand>, map!(
    command,
    |cmd| SectionCommand::Command(cmd)
));

named!(output_sc<&str, SectionCommand>, do_parse!(
    name: alt!(tag!("/DISCARD/") | symbol)
    >>
    opt_space
    >>
    s_type1: opt!(output_section_type) // ugly hack
    >>
    vma: wsc!(opt!(expression))
    >>
    s_type2: opt!(output_section_type)
    >>
    wsc!(tag!(":"))
    >>
    lma: opt!(delimited!(
        tag!("AT("),
        wsc!(expression),
        tag!(")")
    ))
    >>
    opt_space
    >>
    section_align: opt!(delimited!(
        tag!("ALIGN("),
        wsc!(expression),
        tag!(")")
    ))
    >>
    align_with_input: wsc!(opt!(
        tag!("ALIGN_WITH_INPUT")
    ))
    >>
    subsection_align: opt!(delimited!(
        tag!("SUBALIGN("),
        wsc!(expression),
        tag!(")")
    ))
    >>
    constraint: wsc!(opt!(output_section_constraint))
    >>
    wsc!(tag!("{"))
    >>
    content: wsc!(many0!(
        output_section_command
    ))
    >>
    wsc!(tag!("}"))
    >>
    region: opt_complete!(preceded!(
        tag!(">"),
        wsc!(symbol)
    ))
    >>
    lma_region: opt_complete!(preceded!(
        tag!("AT>"),
        wsc!(symbol)
    ))
    >>
    fillexp: opt_complete!(preceded!(
        tag!("="),
        wsc!(expression)
    ))
    >>
    opt_complete!(tag!(","))
    >>
    (SectionCommand::OutputSection {
        name: name.into(),
        vma_address: vma.map(Box::new),
        s_type: if s_type1.is_some() { s_type1 } else { s_type2 },
        lma_address: lma.map(Box::new),
        section_align: section_align.map(Box::new),
        align_with_input: align_with_input.is_some(),
        subsection_align: subsection_align.map(Box::new),
        constraint: constraint,
        content: content,
        region: region.map(String::from),
        lma_region: lma_region.map(String::from),
        fillexp: fillexp.map(Box::new),
    })
));

named!(pub section_command<&str, SectionCommand>, alt_complete!(
    statement_sc | output_sc | command_sc
));

#[cfg(test)]
mod tests {
    use sections::*;

    #[test]
    fn test_section_command() {
        assert_fail!(section_pattern("EXCLUDE_FILE (*a)"));
        assert_fail!(input_osc("EXCLUDE_FILE (*a)"));
        assert_done!(section_pattern("EXCLUDE_FILE ( *a *b ) .c"));
        assert_done!(input_osc("EXCLUDE_FILE ( *a *b ) *c"));

        assert_fail!(input_osc("EXCLUDE_FILE ( EXCLUDE_FILE ( *a *b ) *c ) .d"));
        assert_done!(input_osc("EXCLUDE_FILE ( *a ) *b ( .c )"));
        assert_done!(input_osc("EXCLUDE_FILE ( *a ) *b ( .c .d )"));
        assert_done!(input_osc("EXCLUDE_FILE ( *a ) *b ( .c EXCLUDE_FILE ( *a ) .d )"));

        assert_done!(output_section_command("[A-Z]*(.data)"));
        assert_done!(output_section_command("LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)"));
        assert_done!(output_section_command("EXCLUDE_FILE (*crtend.o *otherfile.o) *(.ctors)"));
        assert_done!(output_section_command("*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)"));
        assert_done!(output_section_command("*(EXCLUDE_FILE (*a) .text EXCLUDE_FILE (*b) .c)"));
        assert_done!(output_section_command("KEEP(SORT_BY_NAME(*)(.ctors))"));
        assert_done!(output_section_command("PROVIDE (__init_array_end = .);"));
        assert_done!(output_section_command("LONG(0);"));
        assert_done!(output_section_command("SORT(CONSTRUCTORS)"));
        assert_done!(output_section_command("*"));

        assert_done!(statement_osc("ASSERT(SIZEOF(.upper)==0,\"Test\");"));
        assert_done!(output_section_command("ASSERT(SIZEOF(.upper)==0,\"Test\");"));
        assert_done!(output_section_command("FILL(0xff);"));

        assert_done!(output_sc("/DISCARD/ : { *(.note.GNU-stack) }"));
        assert_done!(output_sc(".DATA : { [A-Z]*(.data) }"));
        assert_done!(output_sc(".infoD     : {} > INFOD"));

        assert_done!(output_sc(".a:{*(.b .c)*(.d .e)}"));

    }
}