dynasm/
parse_helpers.rs

1//! This file contains parsing helpers used by multiple parsing backends
2use syn::parse;
3use std::convert::TryInto;
4use syn::ext::IdentExt;
5
6/**
7 * Jump types
8 */
9
10pub trait ParseOpt: Sized {
11    fn parse(input: parse::ParseStream) -> parse::Result<Option<Self>>;
12}
13
14pub trait ParseOptExt {
15    /// Parses a syntax tree node of type `T`, advancing the position of our
16    /// parse stream past it if it was found.
17    fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>>;
18}
19
20impl<'a> ParseOptExt for parse::ParseBuffer<'a> {
21    fn parse_opt<T: ParseOpt>(&self) -> parse::Result<Option<T>> {
22        T::parse(self)
23    }
24}
25
26/// Tries to parse an ident that has a specific name as a keyword. Returns true if it worked.
27pub fn eat_pseudo_keyword(input: parse::ParseStream, kw: &str) -> bool {
28    input.step(|cursor| {
29        if let Some((ident, rest)) = cursor.ident() {
30            if ident == kw {
31                return Ok(((), rest));
32            }
33        }
34        Err(cursor.error("expected identifier"))
35    }).is_ok()
36}
37
38/// parses an ident, but instead of syn's Parse impl it does also parse keywords as idents
39pub fn parse_ident_or_rust_keyword(input: parse::ParseStream) -> parse::Result<syn::Ident> {
40    syn::Ident::parse_any(input)
41}
42
43/// checks if an expression is simply an ident, and if so, returns a clone of it.
44pub fn as_ident(expr: &syn::Expr) -> Option<&syn::Ident> {
45    let path = match *expr {
46        syn::Expr::Path(syn::ExprPath {ref path, qself: None, ..}) => path,
47        _ => return None
48    };
49
50    path.get_ident()
51}
52
53/// checks if an expression is a simple literal, allowing us to perform compile-time analysis of an expression
54pub fn as_lit(expr: &syn::Expr) -> Option<&syn::Lit> {
55    // strip any wrapping Group nodes due to delimiting
56    let mut inner = expr;
57    while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
58        inner = expr;
59    }
60
61    match inner {
62        syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some(lit),
63        _ => None
64    }
65}
66
67/// checks if an expression is a literal with possible negation
68pub fn as_lit_with_negation(expr: &syn::Expr) -> Option<(&syn::Lit, bool)> {
69    // strip any wrapping Group nodes due to delimiting
70    let mut inner = expr;
71    while let syn::Expr::Group(syn::ExprGroup { expr, .. }) = inner {
72        inner = expr;
73    }
74
75    match inner {
76        syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, false)),
77        syn::Expr::Unary(syn::ExprUnary { op: syn::UnOp::Neg(_), ref expr, .. } ) => {
78            match &**expr {
79                syn::Expr::Lit(syn::ExprLit { ref lit, .. } ) => Some((lit, true)),
80                _ => None
81            }
82        }
83        _ => None
84    }
85}
86
87/// checks if an expression is a constant number literal
88pub fn as_unsigned_number(expr: &syn::Expr) -> Option<u64> {
89    match as_lit(expr)?  {
90        syn::Lit::Int(i) => i.base10_parse().ok(),
91        _ => None
92    }
93}
94
95/// checks if an expression is a signed number literal
96pub fn as_signed_number(expr: &syn::Expr) -> Option<i64> {
97    // FIXME: this possibly panics on --0x8000_0000_0000_0000
98    let (expr, negated) = as_lit_with_negation(expr)?;
99    match expr {
100        syn::Lit::Int(i) => if let Ok(value) = i.base10_parse::<u64>() {
101            let value: i64 = value.try_into().ok()?;
102            Some (if negated {-value} else {value})
103        } else {
104            None
105        },
106        _ => None
107    }
108}
109
110/// checks if an expression is a constant float literal
111pub fn as_float(expr: &syn::Expr) -> Option<f64> {
112    let (expr, negated) = as_lit_with_negation(expr)?;
113    match expr {
114        syn::Lit::Float(i) => i.base10_parse::<f64>().ok().map(|i| if negated { -i } else { i } ),
115        _ => None
116    }
117}