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
118
119
120
121
122
123
124
125
126
use proc_macro::{TokenStream};

use makepad_micro_proc_macro::{TokenBuilder, TokenParser, error};

const LIVE_ID_SEED:u64 = 0xd6e8_feb8_6659_fd93;

const fn from_bytes(seed:u64, id_bytes: &[u8], start: usize, end: usize) -> u64 {
    let mut x = seed;
    let mut i = start;
    while i < end {
        x = x.overflowing_add(id_bytes[i] as u64).0;
        x ^= x >> 32;
        x = x.overflowing_mul(0xd6e8_feb8_6659_fd93).0;
        x ^= x >> 32;
        x = x.overflowing_mul(0xd6e8_feb8_6659_fd93).0;
        x ^= x >> 32;
        i += 1;
    }
    // mark high bit as meaning that this is a hash id
    return (x & 0x7fff_ffff_ffff_ffff) | 0x8000_0000_0000_0000
}

const fn from_str_unchecked(id_str: &str) -> u64 {
    let bytes = id_str.as_bytes();
    from_bytes(LIVE_ID_SEED, bytes, 0, bytes.len())
}

mod derive_from_live_id;
use crate::derive_from_live_id::*;

#[proc_macro] 
pub fn live_id(item: TokenStream) -> TokenStream {
    let mut tb = TokenBuilder::new(); 

    let mut parser = TokenParser::new(item);
    if let Some(name) = parser.eat_any_ident() {
        let id = from_str_unchecked(&name);
        tb.add("LiveId (").suf_u64(id).add(")");
        tb.end()
    }
    else if let Some(punct) = parser.eat_any_punct(){
        let id = from_str_unchecked(&punct);
        tb.add("LiveId (").suf_u64(id).add(")");
        tb.end()
    }
    else if let Some(v) = parser.eat_literal(){
        if let Ok(v) = v.to_string().parse::<u64>(){
            tb.add("LiveId (").suf_u64(v).add(")");
            return tb.end()
        }
        else{
            parser.unexpected()
        }
    }
    else{
        parser.unexpected()
    }
}

#[proc_macro] 
pub fn id(item: TokenStream) -> TokenStream {
    let mut tb = TokenBuilder::new(); 
    let mut parser = TokenParser::new(item);
    fn parse(parser:&mut TokenParser, tb:&mut TokenBuilder)->Result<(),TokenStream>{
        tb.add("&[");
        loop{
            let ident = parser.expect_any_ident()?;
            let id = from_str_unchecked(&ident);
            tb.add("LiveId (").suf_u64(id).add("),");
            if parser.eat_eot(){
                tb.add("]");
                return Ok(())
            }
            parser.expect_punct_alone('.')?
        }
    }
    if let Err(e) = parse(&mut parser, &mut tb){
        return e
    };
    tb.end()
}

// absolutely a very bad idea but lets see if we can do this.
#[proc_macro]
pub fn id_num(item: TokenStream) -> TokenStream {
    let mut tb = TokenBuilder::new(); 

    let mut parser = TokenParser::new(item);
    if let Some(name) = parser.eat_any_ident() {
        if !parser.eat_punct_alone(','){
            return error("please add a number")
        }
        // then eat the next bit
        let arg = parser.eat_level();
        let id = from_str_unchecked(&name);
        tb.add("LiveId::from_num_unchecked(").suf_u64(id).add(",").stream(Some(arg)).add(")");
        tb.end()
    }
    else{
        parser.unexpected()
    }
}


#[proc_macro]
pub fn id_from_str(item: TokenStream) -> TokenStream {
    let mut tb = TokenBuilder::new(); 

    let mut parser = TokenParser::new(item);
    if let Some(name) = parser.eat_any_ident() {
        tb.add("LiveId::from_str(").string(&name).add(")");
        tb.end()
    }
    else if let Some(punct) = parser.eat_any_punct(){
        tb.add("LiveId::from_str(").string(&punct).add(")");
        tb.end()
    }
    else{
        parser.unexpected()
    }
}

#[proc_macro_derive(FromLiveId)]
pub fn derive_from_live_id(input: TokenStream) -> TokenStream {
    derive_from_live_id_impl(input)
}