dynasm/
parse_helpers.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! This file contains parsing helpers used by multiple parsing backends
use syn::parse;
use std::convert::TryInto;
use syn::ext::IdentExt;

/**
 * Jump types
 */

pub trait ParseOpt: Sized {
    fn parse(input: parse::ParseStream) -> parse::Result<Option<Self>>;
}

pub trait ParseOptExt {
    /// Parses a syntax tree node of type `T`, advancing the position of our
    /// parse stream past it if it was found.
    fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>>;
}

impl<'a> ParseOptExt for parse::ParseBuffer<'a> {
    fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>> {
        T::parse(self)
    }
}

/// Tries to parse an ident that has a specific name as a keyword. Returns true if it worked.
pub fn eat_pseudo_keyword(input: parse::ParseStream, kw: &str) -> bool {
    input.step(|cursor| {
        if let Some((ident, rest)) = cursor.ident() {
            if ident == kw {
                return Ok(((), rest));
            }
        }
        Err(cursor.error("expected identifier"))
    }).is_ok()
}

/// parses an ident, but instead of syn's Parse impl it does also parse keywords as idents
pub fn parse_ident_or_rust_keyword(input: parse::ParseStream) -> parse::Result<syn::Ident> {
    syn::Ident::parse_any(input)
}

/// checks if an expression is simply an ident, and if so, returns a clone of it.
pub fn as_ident(expr: &syn::Expr) -> Option<&syn::Ident> {
    let path = match *expr {
        syn::Expr::Path(syn::ExprPath {ref path, qself: None, ..}) => path,
        _ => return None
    };

    path.get_ident()
}

/// checks if an expression is a simple literal, allowing us to perform compile-time analysis of an expression
pub fn as_lit(expr: &syn::Expr) -> Option<&syn::Lit> {
    // strip any wrapping Group nodes due to delimiting
    let mut inner = expr;
    while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
        inner = expr;
    }

    match inner {
        syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some(lit),
        _ => None
    }
}

/// checks if an expression is a literal with possible negation
pub fn as_lit_with_negation(expr: &syn::Expr) -> Option<(&syn::Lit, bool)> {
    // strip any wrapping Group nodes due to delimiting
    let mut inner = expr;
    while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
        inner = expr;
    }

    match inner {
        syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, false)),
        syn::Expr::Unary(syn::ExprUnary { op: syn::UnOp::Neg(_), ref expr, .. } ) => {
            match &**expr {
                syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, true)),
                _ => None
            }
        }
        _ => None
    }
}

/// checks if an expression is a constant number literal
pub fn as_unsigned_number(expr: &syn::Expr) -> Option<u64> {
    match as_lit(expr)?  {
        syn::Lit::Int(i) => i.base10_parse().ok(),
        _ => None
    }
}

/// checks if an expression is a signed number literal
pub fn as_signed_number(expr: &syn::Expr) -> Option<i64> {
    // FIXME: this possibly panics on --0x8000_0000_0000_0000
    let (expr, negated) = as_lit_with_negation(expr)?;
    match expr {
        syn::Lit::Int(i) => if let Ok(value) = i.base10_parse::<u64>() {
            let value: i64 = value.try_into().ok()?;
            Some (if negated {-value} else {value})
        } else {
            None
        },
        _ => None
    }
}

/// checks if an expression is a constant float literal
pub fn as_float(expr: &syn::Expr) -> Option<f64> {
    let (expr, negated) = as_lit_with_negation(expr)?;
    match expr {
        syn::Lit::Float(i) => i.base10_parse::<f64>().ok().map(|i| if negated { -i } else { i } ),
        _ => None
    }
}