1extern crate proc_macro;
8
9mod parse;
10
11use self::parse::{Int, LiteralError};
12use parse::Uint;
13use proc_macro::{Delimiter, Literal, Span, TokenStream, TokenTree};
14
15#[proc_macro]
16pub fn int(input: TokenStream) -> TokenStream {
17 match IntLiteral::generate(input) {
18 Ok(value) => value.into_tokens(),
19 Err(err) => err.into_tokens(),
20 }
21}
22
23#[proc_macro]
24pub fn uint(input: TokenStream) -> TokenStream {
25 match UintLiteral::generate(input) {
26 Ok(value) => value.into_tokens(),
27 Err(err) => err.into_tokens(),
28 }
29}
30
31struct IntLiteral(Int, String);
32
33impl IntLiteral {
34 fn generate(input: TokenStream) -> Result<Self, CompileError> {
35 let input = Input::parse(input)?;
36 Ok(Self(Int::from_literal(&input.value)?, input.crate_name))
37 }
38
39 fn into_tokens(self) -> TokenStream {
40 let (hi, lo) = self.0.into_words();
41 format!("{}::I256::from_words({hi}, {lo})", self.1)
42 .parse()
43 .unwrap()
44 }
45}
46
47struct UintLiteral(Uint, String);
48
49impl UintLiteral {
50 fn generate(input: TokenStream) -> Result<Self, CompileError> {
51 let input = Input::parse(input)?;
52 Ok(Self(Uint::from_literal(&input.value)?, input.crate_name))
53 }
54
55 fn into_tokens(self) -> TokenStream {
56 let (hi, lo) = self.0.into_words();
57 format!("{}::U256::from_words({hi}, {lo})", self.1)
58 .parse()
59 .unwrap()
60 }
61}
62
63struct Input {
64 value: String,
65 span: Span,
66 crate_name: String,
67}
68
69impl Input {
70 fn parse(input: TokenStream) -> Result<Self, CompileError> {
71 let mut result = Input {
72 value: String::new(),
73 span: Span::call_site(),
74 crate_name: "::ethnum".to_owned(),
75 };
76 ParserState::start().input(input, &mut result)?.end()?;
77
78 Ok(result)
79 }
80}
81
82enum ParserState {
83 String,
84 CommaOrEof,
85 Crate,
86 EqualCrateName,
87 CrateName,
88 Eof,
89}
90
91impl ParserState {
92 fn start() -> Self {
93 Self::String
94 }
95
96 fn input(self, input: TokenStream, result: &mut Input) -> Result<Self, CompileError> {
97 input
98 .into_iter()
99 .try_fold(self, |state, token| state.next(token, result))
100 }
101
102 fn next(self, token: TokenTree, result: &mut Input) -> Result<Self, CompileError> {
103 match (&self, &token) {
104 (_, TokenTree::Group(g)) if g.delimiter() == Delimiter::None => {
110 self.input(g.stream(), result)
111 }
112
113 (Self::String, TokenTree::Literal(l)) => match parse_string(l) {
114 Some(value) => {
115 result.value = value;
116 result.span = token.span();
117 Ok(Self::CommaOrEof)
118 }
119 None => Err(self.unexpected(Some(token))),
120 },
121
122 (Self::CommaOrEof, TokenTree::Punct(p)) if p.as_char() == ',' => Ok(Self::Crate),
123 (Self::Crate, TokenTree::Ident(c)) if c.to_string() == "crate" => {
124 Ok(Self::EqualCrateName)
125 }
126 (Self::EqualCrateName, TokenTree::Punct(p)) if p.as_char() == '=' => {
127 Ok(Self::CrateName)
128 }
129 (Self::CrateName, TokenTree::Literal(l)) => match parse_string(l) {
130 Some(value) => {
131 result.crate_name = value;
132 Ok(Self::Eof)
133 }
134 None => Err(self.unexpected(Some(token))),
135 },
136
137 _ => Err(self.unexpected(Some(token))),
138 }
139 }
140
141 fn end(self) -> Result<(), CompileError> {
142 match self {
143 Self::CommaOrEof | Self::Eof => Ok(()),
144 _ => Err(self.unexpected(None)),
145 }
146 }
147
148 fn unexpected(self, token: Option<TokenTree>) -> CompileError {
149 let expected = match self {
150 Self::String => "string literal",
151 Self::CommaOrEof => "`,` or <eof>",
152 Self::Crate => "`crate` identifier",
153 Self::EqualCrateName => "`=`",
154 Self::CrateName => "crate name string literal",
155 Self::Eof => "<eof>",
156 };
157 let (value, span) = match token {
158 Some(TokenTree::Group(g)) => {
159 let delim = match g.delimiter() {
160 Delimiter::Parenthesis => "(",
161 Delimiter::Brace => "{",
162 Delimiter::Bracket => "[",
163 Delimiter::None => "Ø",
164 };
165 (delim.to_string(), Some(g.span_open()))
166 }
167 Some(t) => (t.to_string(), Some(t.span())),
168 None => ("<eof>".to_owned(), None),
169 };
170
171 CompileError {
172 message: format!("expected {expected} but found `{value}`"),
173 span,
174 }
175 }
176}
177
178struct CompileError {
179 message: String,
180 span: Option<Span>,
181}
182
183impl CompileError {
184 fn into_tokens(self) -> TokenStream {
185 let error = format!("compile_error!({:?})", self.message)
186 .parse::<TokenStream>()
187 .unwrap();
188
189 match self.span {
190 Some(span) => error
191 .into_iter()
192 .map(|mut token| {
193 token.set_span(span);
194 token
195 })
196 .collect(),
197 None => error,
198 }
199 }
200}
201
202impl From<LiteralError> for CompileError {
203 fn from(err: LiteralError) -> Self {
204 Self {
205 message: err.to_string(),
206 span: None,
207 }
208 }
209}
210
211fn parse_string(literal: &Literal) -> Option<String> {
212 Some(
213 literal
214 .to_string()
215 .strip_prefix('"')?
216 .strip_suffix('"')?
217 .to_owned(),
218 )
219}