singe-ptx 0.1.0-alpha.3

CUDA PTX parser, AST, and instruction metadata utilities.
Documentation
use nom::{
    Parser,
    bytes::complete::tag,
    character::complete::char,
    combinator::opt,
    multi::{many0, separated_list1},
};

use crate::{
    ast::{AddressSize, Module, TargetDirective, VersionDirective},
    parser::{
        common::{PResult, comma_sep, decimal_integer, identifier, lexeme, with_span, ws},
        directive::top_level_item,
    },
};

fn non_negative_component(
    value: i64,
    input: &str,
) -> Result<u32, nom::Err<nom::error::Error<&str>>> {
    u32::try_from(value)
        .map_err(|_| nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Digit)))
}

fn version<'a>(source: &'a str, input: &'a str) -> PResult<'a, VersionDirective> {
    let (input, _) = ws(input)?;
    let (input, (span, (_, _, major, _, minor))) = with_span(
        source,
        (
            tag(".version"),
            ws,
            decimal_integer,
            char('.'),
            decimal_integer,
        ),
    )
    .parse(input)?;
    Ok((
        input,
        VersionDirective {
            span,
            major: non_negative_component(major, input)?,
            minor: non_negative_component(minor, input)?,
        },
    ))
}

fn target_specifier(input: &str) -> PResult<'_, String> {
    let (input, _) = ws(input)?;
    let (input, name) = identifier(input)?;
    Ok((input, name.to_string()))
}

fn target<'a>(source: &'a str, input: &'a str) -> PResult<'a, TargetDirective> {
    let (input, _) = ws(input)?;
    let (input, (span, (_, specifiers))) = with_span(
        source,
        (tag(".target"), separated_list1(comma_sep, target_specifier)),
    )
    .parse(input)?;
    Ok((input, TargetDirective { span, specifiers }))
}

fn address_size(input: &str) -> PResult<'_, AddressSize> {
    let (input, _) = lexeme(tag(".address_size")).parse(input)?;
    let (input, _) = ws(input)?;
    let (input, size) = decimal_integer(input)?;
    match size {
        32 => Ok((input, AddressSize::Bits32)),
        64 => Ok((input, AddressSize::Bits64)),
        _ => Err(nom::Err::Error(nom::error::Error::new(
            input,
            nom::error::ErrorKind::Tag,
        ))),
    }
}

pub fn module(input: &str) -> PResult<'_, Module> {
    let source = input;
    let (input, ver) = version(source, input)?;
    let (input, tgt) = target(source, input)?;
    let (input, addr) = opt(address_size).parse(input)?;
    let (input, items) = many0(lexeme(|input| top_level_item(source, input))).parse(input)?;
    let (input, _) = ws(input)?;
    Ok((
        input,
        Module {
            span: crate::ast::Span::create(0, source.len() - input.len()),
            version: ver,
            target: tgt,
            address_size: addr.unwrap_or(AddressSize::Bits32),
            items,
        },
    ))
}