-
Notifications
You must be signed in to change notification settings - Fork 562
/
Copy pathconsteval_int.rs
160 lines (153 loc) · 6.14 KB
/
consteval_int.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use std::sync::Arc;
use cairo_lang_defs::plugin::{
DynGeneratedFileAuxData, MacroPlugin, PluginDiagnostic, PluginGeneratedFile, PluginResult,
};
use cairo_lang_semantic::plugin::{AsDynMacroPlugin, SemanticPlugin, TrivialPluginAuxData};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{ast, Terminal, TypedSyntaxNode};
use num_bigint::BigInt;
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct ConstevalIntMacroPlugin;
impl AsDynMacroPlugin for ConstevalIntMacroPlugin {
fn as_dyn_macro_plugin<'a>(self: Arc<Self>) -> Arc<dyn MacroPlugin + 'a>
where
Self: 'a,
{
self
}
}
impl SemanticPlugin for ConstevalIntMacroPlugin {}
impl MacroPlugin for ConstevalIntMacroPlugin {
fn generate_code(&self, db: &dyn SyntaxGroup, item_ast: ast::Item) -> PluginResult {
match item_ast {
ast::Item::Constant(constant_ast) => handle_constant(db, &constant_ast),
_ => PluginResult::default(),
}
}
}
fn handle_constant(db: &dyn SyntaxGroup, constant_ast: &ast::ItemConstant) -> PluginResult {
let constant_value = constant_ast.value(db);
if let ast::Expr::InlineMacro(inline_macro) = constant_value {
if inline_macro.path(db).as_syntax_node().get_text(db) != "consteval_int" {
return PluginResult::default();
}
let mut diagnostics = vec![];
let constant_expression =
extract_consteval_macro_expression(db, &inline_macro, &mut diagnostics);
if constant_expression.is_none() {
return PluginResult { diagnostics, ..Default::default() };
}
let new_value = rec_compute(db, &constant_expression.unwrap(), &mut diagnostics);
if new_value.is_none() {
return PluginResult { diagnostics, ..Default::default() };
}
return PluginResult {
code: Some(PluginGeneratedFile {
name: "computed_constants".into(),
content: format!(
"const {}{}= {};",
constant_ast.name(db).text(db),
constant_ast.type_clause(db).as_syntax_node().get_text(db),
new_value.unwrap()
),
aux_data: DynGeneratedFileAuxData(Arc::new(TrivialPluginAuxData {})),
}),
diagnostics,
remove_original_item: true,
};
}
PluginResult::default()
}
fn extract_consteval_macro_expression(
db: &dyn SyntaxGroup,
macro_ast: &ast::ExprInlineMacro,
diagnostics: &mut Vec<PluginDiagnostic>,
) -> Option<ast::Expr> {
let args = macro_ast.arguments(db).args(db).elements(db);
if args.len() != 1 {
diagnostics.push(PluginDiagnostic {
stable_ptr: macro_ast.stable_ptr().untyped(),
message: "consteval_int macro must have a single unnamed argument.".to_string(),
});
return None;
}
match args[0].arg_clause(db) {
ast::ArgClause::Unnamed(arg) => Some(arg.value(db)),
_ => {
diagnostics.push(PluginDiagnostic {
stable_ptr: macro_ast.stable_ptr().untyped(),
message: "consteval_int macro must have a single unnamed argument.".to_string(),
});
None
}
}
}
fn rec_compute(
db: &dyn SyntaxGroup,
value: &ast::Expr,
diagnostics: &mut Vec<PluginDiagnostic>,
) -> Option<BigInt> {
match value {
ast::Expr::Literal(lit) => lit.numeric_value(db),
ast::Expr::Binary(bin_expr) => match bin_expr.op(db) {
ast::BinaryOperator::Plus(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
+ rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Mul(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
* rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Minus(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
- rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Div(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
/ rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Mod(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
% rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::And(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
& rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Or(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
| rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
ast::BinaryOperator::Xor(_) => Some(
rec_compute(db, &bin_expr.lhs(db), diagnostics)?
^ rec_compute(db, &bin_expr.rhs(db), diagnostics)?,
),
_ => {
diagnostics.push(PluginDiagnostic {
stable_ptr: bin_expr.stable_ptr().untyped(),
message: "Unsupported binary operator in consteval_int macro".to_string(),
});
None
}
},
ast::Expr::Unary(un_expr) => match un_expr.op(db) {
ast::UnaryOperator::Minus(_) => Some(-rec_compute(db, &un_expr.expr(db), diagnostics)?),
_ => {
diagnostics.push(PluginDiagnostic {
stable_ptr: un_expr.stable_ptr().untyped(),
message: "Unsupported unary operator in consteval_int macro".to_string(),
});
None
}
},
ast::Expr::Parenthesized(paren_expr) => rec_compute(db, &paren_expr.expr(db), diagnostics),
_ => {
diagnostics.push(PluginDiagnostic {
stable_ptr: value.stable_ptr().untyped(),
message: "Unsupported expression in consteval_int macro".to_string(),
});
None
}
}
}