1use crate::error::CodegenError;
7use solscript_ast::{self as ast, StateMutability, Visibility};
8
9#[derive(Debug, Clone)]
11pub struct SolanaProgram {
12 pub name: String,
13 pub state: ProgramState,
14 pub mappings: Vec<MappingDef>,
15 pub modifiers: Vec<ModifierDefinition>,
16 pub instructions: Vec<Instruction>,
17 pub events: Vec<Event>,
18 pub errors: Vec<ProgramError>,
19 pub structs: Vec<StructDef>,
20 pub enums: Vec<EnumDef>,
21 pub tests: Vec<TestFunction>,
23}
24
25#[derive(Debug, Clone)]
27pub struct TestFunction {
28 pub name: String,
29 pub body: Vec<Statement>,
30 pub should_fail: Option<String>,
32}
33
34#[derive(Debug, Clone)]
36pub struct EnumDef {
37 pub name: String,
38 pub variants: Vec<String>,
39}
40
41#[derive(Debug, Clone)]
43pub struct StructDef {
44 pub name: String,
45 pub fields: Vec<StructField>,
46}
47
48#[derive(Debug, Clone)]
50pub struct StructField {
51 pub name: String,
52 pub ty: SolanaType,
53}
54
55#[derive(Debug, Clone)]
57pub struct ModifierDefinition {
58 pub name: String,
59 pub params: Vec<InstructionParam>,
60 pub body: Vec<Statement>,
61}
62
63#[derive(Debug, Clone)]
65pub struct MappingDef {
66 pub name: String,
67 pub key_ty: SolanaType,
68 pub value_ty: SolanaType,
69 pub is_public: bool,
70}
71
72#[derive(Debug, Clone)]
74pub struct ProgramState {
75 pub fields: Vec<StateField>,
76}
77
78#[derive(Debug, Clone)]
80pub struct StateField {
81 pub name: String,
82 pub ty: SolanaType,
83 pub is_public: bool,
84}
85
86#[derive(Debug, Clone)]
88pub struct Instruction {
89 pub name: String,
90 pub params: Vec<InstructionParam>,
91 pub returns: Option<SolanaType>,
92 pub body: Vec<Statement>,
93 pub is_public: bool,
94 pub is_view: bool,
95 pub is_payable: bool,
96 pub uses_token_program: bool,
97 pub uses_sol_transfer: bool,
98 pub modifiers: Vec<ModifierCall>,
99 pub mapping_accesses: Vec<MappingAccess>,
101 pub closes_state: bool,
103}
104
105#[derive(Debug, Clone)]
107pub struct MappingAccess {
108 pub mapping_name: String,
110 pub key_exprs: Vec<Expression>,
112 pub is_write: bool,
114 pub should_close: bool,
116 pub account_name: String,
118}
119
120#[derive(Debug, Clone)]
122pub struct InstructionParam {
123 pub name: String,
124 pub ty: SolanaType,
125}
126
127#[derive(Debug, Clone)]
129pub struct ModifierCall {
130 pub name: String,
131 pub args: Vec<Expression>,
132}
133
134#[derive(Debug, Clone)]
136pub struct Event {
137 pub name: String,
138 pub fields: Vec<EventField>,
139}
140
141#[derive(Debug, Clone)]
143pub struct EventField {
144 pub name: String,
145 pub ty: SolanaType,
146 pub indexed: bool,
147}
148
149#[derive(Debug, Clone)]
151pub struct ProgramError {
152 pub name: String,
153 pub fields: Vec<ErrorField>,
154}
155
156#[derive(Debug, Clone)]
158pub struct ErrorField {
159 pub name: String,
160 pub ty: SolanaType,
161}
162
163#[derive(Debug, Clone)]
165pub enum SolanaType {
166 U8,
167 U16,
168 U32,
169 U64,
170 U128,
171 I8,
172 I16,
173 I32,
174 I64,
175 I128,
176 Bool,
177 Pubkey, Signer, String,
180 Bytes,
181 FixedBytes(usize), Array(Box<SolanaType>, usize),
183 Vec(Box<SolanaType>),
184 Option(Box<SolanaType>),
185 Mapping(Box<SolanaType>, Box<SolanaType>),
187 Custom(String),
189}
190
191#[derive(Debug, Clone)]
193pub enum Statement {
194 VarDecl {
195 name: String,
196 ty: SolanaType,
197 value: Option<Expression>,
198 },
199 Assign {
200 target: Expression,
201 value: Expression,
202 },
203 If {
204 condition: Expression,
205 then_block: Vec<Statement>,
206 else_block: Option<Vec<Statement>>,
207 },
208 While {
209 condition: Expression,
210 body: Vec<Statement>,
211 },
212 For {
213 init: Option<Box<Statement>>,
214 condition: Option<Expression>,
215 update: Option<Expression>,
216 body: Vec<Statement>,
217 },
218 Return(Option<Expression>),
219 Emit {
220 event: String,
221 args: Vec<Expression>,
222 },
223 Require {
224 condition: Expression,
225 message: Option<String>,
226 },
227 RevertWithError {
229 error_name: String,
230 args: Vec<Expression>,
231 },
232 Delete(Expression),
234 Selfdestruct {
236 recipient: Expression,
237 },
238 Expr(Expression),
239 Placeholder,
241}
242
243#[derive(Debug, Clone)]
245pub enum Expression {
246 Literal(Literal),
247 Var(String),
248 StateAccess(String), MappingAccess {
251 mapping_name: String,
252 keys: Vec<Expression>,
254 account_name: String,
256 },
257 MsgSender, MsgValue, BlockTimestamp, ClockSlot, ClockEpoch, ClockUnixTimestamp, RentMinimumBalance {
266 data_len: Box<Expression>,
267 },
268 RentIsExempt {
269 lamports: Box<Expression>,
270 data_len: Box<Expression>,
271 },
272 Binary {
273 op: BinaryOp,
274 left: Box<Expression>,
275 right: Box<Expression>,
276 },
277 Unary {
278 op: UnaryOp,
279 expr: Box<Expression>,
280 },
281 Call {
282 func: String,
283 args: Vec<Expression>,
284 },
285 MethodCall {
286 receiver: Box<Expression>,
287 method: String,
288 args: Vec<Expression>,
289 },
290 InterfaceCast {
292 interface_name: String,
294 program_id: Box<Expression>,
296 },
297 CpiCall {
299 program: Box<Expression>,
301 interface_name: String,
303 method: String,
305 args: Vec<Expression>,
307 },
308 TokenTransfer {
310 from: Box<Expression>,
312 to: Box<Expression>,
314 authority: Box<Expression>,
316 amount: Box<Expression>,
318 },
319 TokenMint {
321 mint: Box<Expression>,
323 to: Box<Expression>,
325 authority: Box<Expression>,
327 amount: Box<Expression>,
329 },
330 TokenBurn {
332 from: Box<Expression>,
334 mint: Box<Expression>,
336 authority: Box<Expression>,
338 amount: Box<Expression>,
340 },
341 SolTransfer {
343 to: Box<Expression>,
345 amount: Box<Expression>,
347 },
348 GetATA {
350 owner: Box<Expression>,
352 mint: Box<Expression>,
354 },
355 Index {
356 expr: Box<Expression>,
357 index: Box<Expression>,
358 },
359 Field {
360 expr: Box<Expression>,
361 field: String,
362 },
363 Ternary {
364 condition: Box<Expression>,
365 then_expr: Box<Expression>,
366 else_expr: Box<Expression>,
367 },
368 Assert {
370 condition: Box<Expression>,
371 message: Option<String>,
372 },
373 AssertEq {
375 left: Box<Expression>,
376 right: Box<Expression>,
377 message: Option<String>,
378 },
379 AssertNe {
381 left: Box<Expression>,
382 right: Box<Expression>,
383 message: Option<String>,
384 },
385 AssertGt {
387 left: Box<Expression>,
388 right: Box<Expression>,
389 message: Option<String>,
390 },
391 AssertGe {
393 left: Box<Expression>,
394 right: Box<Expression>,
395 message: Option<String>,
396 },
397 AssertLt {
399 left: Box<Expression>,
400 right: Box<Expression>,
401 message: Option<String>,
402 },
403 AssertLe {
405 left: Box<Expression>,
406 right: Box<Expression>,
407 message: Option<String>,
408 },
409}
410
411#[derive(Debug, Clone)]
413pub enum Literal {
414 Bool(bool),
415 Int(i128),
416 Uint(u128),
417 String(String),
418 Pubkey(String), ZeroAddress, ZeroBytes(usize), }
422
423#[derive(Debug, Clone, Copy)]
425pub enum BinaryOp {
426 Add,
427 Sub,
428 Mul,
429 Div,
430 Rem,
431 Eq,
432 Ne,
433 Lt,
434 Le,
435 Gt,
436 Ge,
437 And,
438 Or,
439 BitAnd,
440 BitOr,
441 BitXor,
442 Shl,
443 Shr,
444}
445
446#[derive(Debug, Clone, Copy)]
448pub enum UnaryOp {
449 Neg,
450 Not,
451 BitNot,
452}
453
454pub fn lower_to_ir(program: &ast::Program) -> Result<Vec<SolanaProgram>, CodegenError> {
456 let mut programs = Vec::new();
457 let mut events = Vec::new();
458 let mut errors = Vec::new();
459 let mut structs = Vec::new();
460 let mut enums = Vec::new();
461
462 let mut interface_names: std::collections::HashSet<String> = std::collections::HashSet::new();
464 for item in &program.items {
465 match item {
466 ast::Item::Event(e) => {
467 events.push(lower_event(e)?);
468 }
469 ast::Item::Error(e) => {
470 errors.push(lower_error(e)?);
471 }
472 ast::Item::Struct(s) => {
473 structs.push(lower_struct(s)?);
474 }
475 ast::Item::Enum(e) => {
476 enums.push(lower_enum(e));
477 }
478 ast::Item::Interface(i) => {
479 interface_names.insert(i.name.name.to_string());
480 }
481 ast::Item::Contract(c) => {
482 for member in &c.members {
484 match member {
485 ast::ContractMember::Event(e) => {
486 events.push(lower_event(e)?);
487 }
488 ast::ContractMember::Error(e) => {
489 errors.push(lower_error(e)?);
490 }
491 ast::ContractMember::Struct(s) => {
492 structs.push(lower_struct(s)?);
493 }
494 ast::ContractMember::Enum(e) => {
495 enums.push(lower_enum(e));
496 }
497 _ => {}
498 }
499 }
500 }
501 _ => {}
502 }
503 }
504
505 let contracts: std::collections::HashMap<String, &ast::ContractDef> = program
507 .items
508 .iter()
509 .filter_map(|item| {
510 if let ast::Item::Contract(c) = item {
511 Some((c.name.name.to_string(), c))
512 } else {
513 None
514 }
515 })
516 .collect();
517
518 for item in &program.items {
520 if let ast::Item::Contract(contract) = item {
521 if contract.is_abstract {
523 continue;
524 }
525 let prog = lower_contract(
526 contract,
527 &events,
528 &errors,
529 &structs,
530 &enums,
531 &contracts,
532 &interface_names,
533 )?;
534 programs.push(prog);
535 }
536 }
537
538 Ok(programs)
539}
540
541struct LoweringContext {
543 state_fields: std::collections::HashSet<String>,
544 mapping_names: std::collections::HashSet<String>,
545 mappings: Vec<MappingDef>,
546 interface_names: std::collections::HashSet<String>,
547}
548
549impl LoweringContext {
550 fn new() -> Self {
551 Self {
552 state_fields: std::collections::HashSet::new(),
553 mapping_names: std::collections::HashSet::new(),
554 mappings: Vec::new(),
555 interface_names: std::collections::HashSet::new(),
556 }
557 }
558
559 fn is_state_field(&self, name: &str) -> bool {
560 self.state_fields.contains(name)
561 }
562
563 fn is_interface(&self, name: &str) -> bool {
564 self.interface_names.contains(name)
565 }
566
567 fn is_mapping(&self, name: &str) -> bool {
568 self.mapping_names.contains(name)
569 }
570}
571
572struct MappingAccessCollector {
574 accesses: Vec<MappingAccess>,
575 counter: usize,
576 uses_token_program: bool,
577 uses_sol_transfer: bool,
578}
579
580impl MappingAccessCollector {
581 fn new() -> Self {
582 Self {
583 accesses: Vec::new(),
584 counter: 0,
585 uses_token_program: false,
586 uses_sol_transfer: false,
587 }
588 }
589
590 fn mark_uses_token_program(&mut self) {
591 self.uses_token_program = true;
592 }
593
594 fn mark_uses_sol_transfer(&mut self) {
595 self.uses_sol_transfer = true;
596 }
597
598 fn record_access(
600 &mut self,
601 mapping_name: &str,
602 keys: Vec<Expression>,
603 is_write: bool,
604 should_close: bool,
605 ) -> String {
606 let account_name = format!("{}_entry_{}", to_snake_case(mapping_name), self.counter);
608 self.counter += 1;
609
610 self.accesses.push(MappingAccess {
611 mapping_name: mapping_name.to_string(),
612 key_exprs: keys,
613 is_write,
614 should_close,
615 account_name: account_name.clone(),
616 });
617
618 account_name
619 }
620}
621
622fn to_snake_case(s: &str) -> String {
623 let mut result = String::new();
624 let mut prev_upper = false;
625
626 for (i, c) in s.chars().enumerate() {
627 if c.is_uppercase() {
628 if i > 0 && !prev_upper {
629 result.push('_');
630 }
631 result.push(c.to_lowercase().next().unwrap());
632 prev_upper = true;
633 } else {
634 result.push(c);
635 prev_upper = false;
636 }
637 }
638
639 result
640}
641
642fn lower_contract(
643 contract: &ast::ContractDef,
644 events: &[Event],
645 errors: &[ProgramError],
646 structs: &[StructDef],
647 enums: &[EnumDef],
648 all_contracts: &std::collections::HashMap<String, &ast::ContractDef>,
649 interface_names: &std::collections::HashSet<String>,
650) -> Result<SolanaProgram, CodegenError> {
651 let name = contract.name.name.to_string();
652
653 let mut all_members: Vec<&ast::ContractMember> = Vec::new();
656
657 for base in &contract.bases {
659 let base_name = base.segments.first().map(|s| s.name.as_str()).unwrap_or("");
660 if let Some(base_contract) = all_contracts.get(base_name) {
661 for member in &base_contract.members {
663 if !matches!(member, ast::ContractMember::Constructor(_)) {
664 all_members.push(member);
665 }
666 }
667 }
668 }
669
670 for member in &contract.members {
672 all_members.push(member);
673 }
674
675 let mut fields = Vec::new();
677 let mut ctx = LoweringContext::new();
678 ctx.interface_names = interface_names.clone();
679 let mut seen_fields = std::collections::HashSet::new();
680
681 for member in &all_members {
682 if let ast::ContractMember::StateVar(var) = member {
683 let field_name = var.name.name.to_string();
684
685 if seen_fields.contains(&field_name) {
687 continue;
688 }
689 seen_fields.insert(field_name.clone());
690
691 let field_ty = lower_type(&var.ty)?;
692 let is_public = matches!(var.visibility, Some(Visibility::Public));
693
694 if let SolanaType::Mapping(key_ty, value_ty) = field_ty {
696 ctx.mapping_names.insert(field_name.clone());
697 ctx.mappings.push(MappingDef {
698 name: field_name,
699 key_ty: (*key_ty).clone(),
700 value_ty: (*value_ty).clone(),
701 is_public,
702 });
703 } else {
704 ctx.state_fields.insert(field_name.clone());
705 fields.push(StateField {
706 name: field_name,
707 ty: field_ty,
708 is_public,
709 });
710 }
711 }
712 }
713
714 let mut modifiers = Vec::new();
716 let mut instructions = Vec::new();
717 let mut seen_modifiers = std::collections::HashSet::new();
718 let mut seen_functions = std::collections::HashSet::new();
719
720 for member in all_members.iter().rev() {
722 if let ast::ContractMember::Modifier(modifier) = member {
723 let mod_name = modifier.name.name.to_string();
724 if !seen_modifiers.contains(&mod_name) {
725 seen_modifiers.insert(mod_name);
726 modifiers.push(lower_modifier(modifier, &ctx)?);
727 }
728 }
729 }
730
731 for member in all_members.iter().rev() {
733 match member {
734 ast::ContractMember::StateVar(_) => {
735 }
737 ast::ContractMember::Function(func) => {
738 let func_name = func.name.name.to_string();
739 if func.body.is_some() && !seen_functions.contains(&func_name) {
741 seen_functions.insert(func_name);
742 instructions.push(lower_function(func, &ctx)?);
743 }
744 }
745 ast::ContractMember::Constructor(_) => {
746 }
748 ast::ContractMember::Modifier(_) => {
749 }
751 ast::ContractMember::Event(_) | ast::ContractMember::Error(_) => {
752 }
754 ast::ContractMember::Struct(_) | ast::ContractMember::Enum(_) => {
755 }
757 }
758 }
759
760 for member in &contract.members {
762 if let ast::ContractMember::Constructor(ctor) = member {
763 instructions.insert(0, lower_constructor(ctor, &ctx)?);
764 break;
765 }
766 }
767
768 let mut tests = Vec::new();
770 for member in all_members.iter() {
771 if let ast::ContractMember::Function(func) = member {
772 if has_test_attribute(&func.attributes) {
773 tests.push(lower_test_function(func, &ctx)?);
774 }
775 }
776 }
777
778 Ok(SolanaProgram {
779 name,
780 state: ProgramState { fields },
781 mappings: ctx.mappings,
782 modifiers,
783 instructions,
784 events: events.to_vec(),
785 errors: errors.to_vec(),
786 structs: structs.to_vec(),
787 enums: enums.to_vec(),
788 tests,
789 })
790}
791
792fn has_test_attribute(attrs: &[ast::Attribute]) -> bool {
794 attrs.iter().any(|a| a.name.name.as_str() == "test")
795}
796
797fn get_should_fail_message(attrs: &[ast::Attribute]) -> Option<String> {
799 for attr in attrs {
800 if attr.name.name.as_str() == "should_fail" {
801 if let Some(arg) = attr.args.first() {
802 if let ast::AttributeValue::Literal(ast::Literal::String(s, _)) = &arg.value {
803 return Some(s.to_string());
804 }
805 }
806 return Some(String::new());
808 }
809 }
810 None
811}
812
813fn lower_test_function(
815 func: &ast::FnDef,
816 ctx: &LoweringContext,
817) -> Result<TestFunction, CodegenError> {
818 let name = func.name.name.to_string();
819 let mut collector = MappingAccessCollector::new();
820
821 let body = if let Some(block) = &func.body {
822 lower_block(block, ctx, &mut collector)?
823 } else {
824 Vec::new()
825 };
826
827 let should_fail = get_should_fail_message(&func.attributes);
828
829 Ok(TestFunction {
830 name,
831 body,
832 should_fail,
833 })
834}
835
836fn lower_function(func: &ast::FnDef, ctx: &LoweringContext) -> Result<Instruction, CodegenError> {
837 let name = func.name.name.to_string();
838 let mut collector = MappingAccessCollector::new();
839
840 let params: Vec<InstructionParam> = func
841 .params
842 .iter()
843 .map(|p| {
844 Ok(InstructionParam {
845 name: p.name.name.to_string(),
846 ty: lower_type(&p.ty)?,
847 })
848 })
849 .collect::<Result<Vec<_>, CodegenError>>()?;
850
851 let returns = if func.return_params.is_empty() {
852 None
853 } else if func.return_params.len() == 1 {
854 Some(lower_type(&func.return_params[0].ty)?)
855 } else {
856 return Err(CodegenError::UnsupportedFeature(
857 "Multiple return values".to_string(),
858 ));
859 };
860
861 let is_public = matches!(
862 func.visibility,
863 Some(Visibility::Public) | Some(Visibility::External)
864 );
865 let is_view = func
866 .state_mutability
867 .iter()
868 .any(|m| matches!(m, StateMutability::View | StateMutability::Pure));
869 let is_payable = func
870 .state_mutability
871 .iter()
872 .any(|m| matches!(m, StateMutability::Payable));
873
874 let mut modifiers = Vec::new();
875 for m in &func.modifiers {
876 let args: Vec<Expression> = m
877 .args
878 .iter()
879 .map(|a| lower_expr(&a.value, ctx, &mut collector))
880 .collect::<Result<Vec<_>, _>>()?;
881 modifiers.push(ModifierCall {
882 name: m.name.name.to_string(),
883 args,
884 });
885 }
886
887 let body = lower_block(func.body.as_ref().unwrap(), ctx, &mut collector)?;
889
890 let closes_state = body_contains_selfdestruct(&body);
892
893 Ok(Instruction {
894 name,
895 params,
896 returns,
897 body,
898 is_public,
899 is_view,
900 is_payable,
901 uses_token_program: collector.uses_token_program,
902 uses_sol_transfer: collector.uses_sol_transfer,
903 modifiers,
904 mapping_accesses: collector.accesses,
905 closes_state,
906 })
907}
908
909fn body_contains_selfdestruct(stmts: &[Statement]) -> bool {
911 for stmt in stmts {
912 match stmt {
913 Statement::Selfdestruct { .. } => return true,
914 Statement::If {
915 then_block,
916 else_block,
917 ..
918 } => {
919 if body_contains_selfdestruct(then_block) {
920 return true;
921 }
922 if let Some(else_stmts) = else_block {
923 if body_contains_selfdestruct(else_stmts) {
924 return true;
925 }
926 }
927 }
928 Statement::While { body, .. } => {
929 if body_contains_selfdestruct(body) {
930 return true;
931 }
932 }
933 Statement::For { body, .. } => {
934 if body_contains_selfdestruct(body) {
935 return true;
936 }
937 }
938 _ => {}
939 }
940 }
941 false
942}
943
944fn lower_constructor(
945 ctor: &ast::ConstructorDef,
946 ctx: &LoweringContext,
947) -> Result<Instruction, CodegenError> {
948 let mut collector = MappingAccessCollector::new();
949
950 let params: Vec<InstructionParam> = ctor
951 .params
952 .iter()
953 .map(|p| {
954 Ok(InstructionParam {
955 name: p.name.name.to_string(),
956 ty: lower_type(&p.ty)?,
957 })
958 })
959 .collect::<Result<Vec<_>, CodegenError>>()?;
960
961 let body = lower_block(&ctor.body, ctx, &mut collector)?;
962
963 Ok(Instruction {
964 name: "initialize".to_string(),
965 params,
966 returns: None,
967 body,
968 is_public: true,
969 is_view: false,
970 is_payable: ctor.modifiers.iter().any(|m| m.name.name == "payable"),
971 uses_token_program: collector.uses_token_program,
972 uses_sol_transfer: collector.uses_sol_transfer,
973 modifiers: Vec::new(),
974 mapping_accesses: collector.accesses,
975 closes_state: false, })
977}
978
979fn lower_modifier(
980 modifier: &ast::ModifierDef,
981 ctx: &LoweringContext,
982) -> Result<ModifierDefinition, CodegenError> {
983 let mut collector = MappingAccessCollector::new();
984
985 let params: Vec<InstructionParam> = modifier
986 .params
987 .iter()
988 .map(|p| {
989 Ok(InstructionParam {
990 name: p.name.name.to_string(),
991 ty: lower_type(&p.ty)?,
992 })
993 })
994 .collect::<Result<Vec<_>, CodegenError>>()?;
995
996 let body = lower_block(&modifier.body, ctx, &mut collector)?;
997
998 Ok(ModifierDefinition {
999 name: modifier.name.name.to_string(),
1000 params,
1001 body,
1002 })
1003}
1004
1005fn lower_event(event: &ast::EventDef) -> Result<Event, CodegenError> {
1006 let fields: Vec<EventField> = event
1007 .params
1008 .iter()
1009 .map(|p| {
1010 Ok(EventField {
1011 name: p.name.name.to_string(),
1012 ty: lower_type(&p.ty)?,
1013 indexed: p.indexed,
1014 })
1015 })
1016 .collect::<Result<Vec<_>, CodegenError>>()?;
1017
1018 Ok(Event {
1019 name: event.name.name.to_string(),
1020 fields,
1021 })
1022}
1023
1024fn lower_error(error: &ast::ErrorDef) -> Result<ProgramError, CodegenError> {
1025 let fields: Vec<ErrorField> = error
1026 .params
1027 .iter()
1028 .map(|p| {
1029 Ok(ErrorField {
1030 name: p.name.name.to_string(),
1031 ty: lower_type(&p.ty)?,
1032 })
1033 })
1034 .collect::<Result<Vec<_>, CodegenError>>()?;
1035
1036 Ok(ProgramError {
1037 name: error.name.name.to_string(),
1038 fields,
1039 })
1040}
1041
1042fn lower_struct(s: &ast::StructDef) -> Result<StructDef, CodegenError> {
1043 let fields: Vec<StructField> = s
1044 .fields
1045 .iter()
1046 .map(|f| {
1047 Ok(StructField {
1048 name: f.name.name.to_string(),
1049 ty: lower_type(&f.ty)?,
1050 })
1051 })
1052 .collect::<Result<Vec<_>, CodegenError>>()?;
1053
1054 Ok(StructDef {
1055 name: s.name.name.to_string(),
1056 fields,
1057 })
1058}
1059
1060fn lower_enum(e: &ast::EnumDef) -> EnumDef {
1061 EnumDef {
1062 name: e.name.name.to_string(),
1063 variants: e.variants.iter().map(|v| v.name.name.to_string()).collect(),
1064 }
1065}
1066
1067fn lower_type(ty: &ast::TypeExpr) -> Result<SolanaType, CodegenError> {
1068 match ty {
1069 ast::TypeExpr::Path(path) => {
1070 let name = path.name();
1071 match name.as_str() {
1072 "uint8" | "u8" => Ok(SolanaType::U8),
1073 "uint16" | "u16" => Ok(SolanaType::U16),
1074 "uint32" | "u32" => Ok(SolanaType::U32),
1075 "uint64" | "u64" => Ok(SolanaType::U64),
1076 "uint128" | "u128" => Ok(SolanaType::U128),
1077 "uint256" | "uint" => Ok(SolanaType::U128), "int8" | "i8" => Ok(SolanaType::I8),
1079 "int16" | "i16" => Ok(SolanaType::I16),
1080 "int32" | "i32" => Ok(SolanaType::I32),
1081 "int64" | "i64" => Ok(SolanaType::I64),
1082 "int128" | "i128" => Ok(SolanaType::I128),
1083 "int256" | "int" => Ok(SolanaType::I128),
1084 "bool" => Ok(SolanaType::Bool),
1085 "address" => Ok(SolanaType::Pubkey),
1086 "signer" => Ok(SolanaType::Signer),
1087 "string" => Ok(SolanaType::String),
1088 "bytes" => Ok(SolanaType::Bytes),
1089 s if s.starts_with("bytes") => {
1091 if let Ok(n) = s[5..].parse::<usize>() {
1092 if (1..=32).contains(&n) {
1093 Ok(SolanaType::FixedBytes(n))
1094 } else {
1095 Err(CodegenError::UnsupportedFeature(format!(
1096 "Invalid bytes size: {}",
1097 n
1098 )))
1099 }
1100 } else {
1101 Ok(SolanaType::Custom(s.to_string()))
1102 }
1103 }
1104 other => Ok(SolanaType::Custom(other.to_string())),
1105 }
1106 }
1107 ast::TypeExpr::Array(arr) => {
1108 let elem = lower_type(&ast::TypeExpr::Path(arr.element.clone()))?;
1109 if arr.sizes.len() != 1 {
1110 return Err(CodegenError::UnsupportedFeature(
1111 "Multi-dimensional arrays".to_string(),
1112 ));
1113 }
1114 match &arr.sizes[0] {
1115 Some(size) => Ok(SolanaType::Array(Box::new(elem), *size as usize)),
1116 None => Ok(SolanaType::Vec(Box::new(elem))),
1117 }
1118 }
1119 ast::TypeExpr::Mapping(mapping) => {
1120 let key = lower_type(&mapping.key)?;
1121 let value = lower_type(&mapping.value)?;
1122 Ok(SolanaType::Mapping(Box::new(key), Box::new(value)))
1123 }
1124 ast::TypeExpr::Tuple(_) => Err(CodegenError::UnsupportedFeature("Tuple types".to_string())),
1125 }
1126}
1127
1128fn lower_block(
1129 block: &ast::Block,
1130 ctx: &LoweringContext,
1131 collector: &mut MappingAccessCollector,
1132) -> Result<Vec<Statement>, CodegenError> {
1133 block
1134 .stmts
1135 .iter()
1136 .map(|s| lower_stmt(s, ctx, collector))
1137 .collect()
1138}
1139
1140fn lower_stmt(
1141 stmt: &ast::Stmt,
1142 ctx: &LoweringContext,
1143 collector: &mut MappingAccessCollector,
1144) -> Result<Statement, CodegenError> {
1145 match stmt {
1146 ast::Stmt::VarDecl(v) => Ok(Statement::VarDecl {
1147 name: v.name.name.to_string(),
1148 ty: lower_type(&v.ty)?,
1149 value: v
1150 .initializer
1151 .as_ref()
1152 .map(|e| lower_expr(e, ctx, collector))
1153 .transpose()?,
1154 }),
1155 ast::Stmt::Return(r) => Ok(Statement::Return(
1156 r.value
1157 .as_ref()
1158 .map(|e| lower_expr(e, ctx, collector))
1159 .transpose()?,
1160 )),
1161 ast::Stmt::If(i) => lower_if_stmt(i, ctx, collector),
1162 ast::Stmt::While(w) => Ok(Statement::While {
1163 condition: lower_expr(&w.condition, ctx, collector)?,
1164 body: lower_block(&w.body, ctx, collector)?,
1165 }),
1166 ast::Stmt::For(f) => lower_for_stmt(f, ctx, collector),
1167 ast::Stmt::Emit(e) => Ok(Statement::Emit {
1168 event: e.event.name.to_string(),
1169 args: e
1170 .args
1171 .iter()
1172 .map(|a| lower_expr(&a.value, ctx, collector))
1173 .collect::<Result<Vec<_>, _>>()?,
1174 }),
1175 ast::Stmt::Require(r) => Ok(Statement::Require {
1176 condition: lower_expr(&r.condition, ctx, collector)?,
1177 message: r.message.as_ref().map(|s| s.to_string()),
1178 }),
1179 ast::Stmt::Revert(r) => match &r.kind {
1180 ast::RevertKind::Message(msg) => Ok(Statement::Require {
1181 condition: Expression::Literal(Literal::Bool(false)),
1182 message: msg
1183 .as_ref()
1184 .map(|s| s.to_string())
1185 .or_else(|| Some("Reverted".to_string())),
1186 }),
1187 ast::RevertKind::Error { name, args } => {
1188 let lowered_args: Vec<Expression> = args
1189 .iter()
1190 .map(|a| lower_expr(&a.value, ctx, collector))
1191 .collect::<Result<Vec<_>, _>>()?;
1192 Ok(Statement::RevertWithError {
1193 error_name: name.name.to_string(),
1194 args: lowered_args,
1195 })
1196 }
1197 },
1198 ast::Stmt::Delete(d) => {
1199 let target = lower_expr(&d.target, ctx, collector)?;
1200 if let Expression::MappingAccess { account_name, .. } = &target {
1202 for access in &mut collector.accesses {
1204 if &access.account_name == account_name {
1205 access.should_close = true;
1206 break;
1207 }
1208 }
1209 }
1210 Ok(Statement::Delete(target))
1211 }
1212 ast::Stmt::Selfdestruct(s) => Ok(Statement::Selfdestruct {
1213 recipient: lower_expr(&s.recipient, ctx, collector)?,
1214 }),
1215 ast::Stmt::Expr(e) => Ok(Statement::Expr(lower_expr(&e.expr, ctx, collector)?)),
1216 ast::Stmt::Placeholder(_) => Ok(Statement::Placeholder),
1217 }
1218}
1219
1220fn lower_if_stmt(
1221 i: &ast::IfStmt,
1222 ctx: &LoweringContext,
1223 collector: &mut MappingAccessCollector,
1224) -> Result<Statement, CodegenError> {
1225 let condition = lower_expr(&i.condition, ctx, collector)?;
1226 let then_block = lower_block(&i.then_block, ctx, collector)?;
1227 let else_block = match &i.else_branch {
1228 Some(ast::ElseBranch::Else(block)) => Some(lower_block(block, ctx, collector)?),
1229 Some(ast::ElseBranch::ElseIf(elif)) => Some(vec![lower_if_stmt(elif, ctx, collector)?]),
1230 None => None,
1231 };
1232
1233 Ok(Statement::If {
1234 condition,
1235 then_block,
1236 else_block,
1237 })
1238}
1239
1240fn lower_for_stmt(
1241 f: &ast::ForStmt,
1242 ctx: &LoweringContext,
1243 collector: &mut MappingAccessCollector,
1244) -> Result<Statement, CodegenError> {
1245 let init = match &f.init {
1246 Some(ast::ForInit::VarDecl(v)) => Some(Box::new(Statement::VarDecl {
1247 name: v.name.name.to_string(),
1248 ty: lower_type(&v.ty)?,
1249 value: v
1250 .initializer
1251 .as_ref()
1252 .map(|e| lower_expr(e, ctx, collector))
1253 .transpose()?,
1254 })),
1255 Some(ast::ForInit::Expr(e)) => {
1256 Some(Box::new(Statement::Expr(lower_expr(e, ctx, collector)?)))
1257 }
1258 None => None,
1259 };
1260
1261 Ok(Statement::For {
1262 init,
1263 condition: f
1264 .condition
1265 .as_ref()
1266 .map(|e| lower_expr(e, ctx, collector))
1267 .transpose()?,
1268 update: f
1269 .update
1270 .as_ref()
1271 .map(|e| lower_expr(e, ctx, collector))
1272 .transpose()?,
1273 body: lower_block(&f.body, ctx, collector)?,
1274 })
1275}
1276
1277fn extract_mapping_access<'a>(
1280 base: &'a ast::Expr,
1281 index: &'a ast::Expr,
1282 ctx: &LoweringContext,
1283) -> Result<Option<(String, Vec<&'a ast::Expr>)>, CodegenError> {
1284 if let ast::Expr::Ident(ident) = base {
1286 let name = ident.name.to_string();
1287 if ctx.is_mapping(&name) {
1288 return Ok(Some((name, vec![index])));
1289 }
1290 }
1291
1292 if let ast::Expr::Index(inner) = base {
1294 if let Some((mapping_name, mut keys)) =
1295 extract_mapping_access(&inner.expr, &inner.index, ctx)?
1296 {
1297 keys.push(index);
1299 return Ok(Some((mapping_name, keys)));
1300 }
1301 }
1302
1303 Ok(None)
1304}
1305
1306fn lower_expr(
1307 expr: &ast::Expr,
1308 ctx: &LoweringContext,
1309 collector: &mut MappingAccessCollector,
1310) -> Result<Expression, CodegenError> {
1311 match expr {
1312 ast::Expr::Literal(lit) => lower_literal(lit),
1313 ast::Expr::Ident(ident) => {
1314 let name = ident.name.to_string();
1315 match name.as_str() {
1316 "msg" => Ok(Expression::Var("msg".to_string())),
1317 "block" => Ok(Expression::Var("block".to_string())),
1318 "tx" => Ok(Expression::Var("tx".to_string())),
1319 _ => {
1320 if ctx.is_state_field(&name) {
1322 Ok(Expression::StateAccess(name))
1323 } else {
1324 Ok(Expression::Var(name))
1325 }
1326 }
1327 }
1328 }
1329 ast::Expr::Binary(b) => Ok(Expression::Binary {
1330 op: lower_binary_op(&b.op),
1331 left: Box::new(lower_expr(&b.left, ctx, collector)?),
1332 right: Box::new(lower_expr(&b.right, ctx, collector)?),
1333 }),
1334 ast::Expr::Unary(u) => Ok(Expression::Unary {
1335 op: lower_unary_op(&u.op),
1336 expr: Box::new(lower_expr(&u.expr, ctx, collector)?),
1337 }),
1338 ast::Expr::Call(c) => {
1339 if let ast::Expr::Ident(ident) = &c.callee {
1340 let func_name = ident.name.to_string();
1341
1342 match func_name.as_str() {
1344 "assert" => {
1345 let condition = lower_expr(&c.args[0].value, ctx, collector)?;
1346 let message = if c.args.len() > 1 {
1347 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[1].value
1348 {
1349 Some(s.to_string())
1350 } else {
1351 None
1352 }
1353 } else {
1354 None
1355 };
1356 return Ok(Expression::Assert {
1357 condition: Box::new(condition),
1358 message,
1359 });
1360 }
1361 "assertEq" => {
1362 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1363 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1364 let message = if c.args.len() > 2 {
1365 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1366 {
1367 Some(s.to_string())
1368 } else {
1369 None
1370 }
1371 } else {
1372 None
1373 };
1374 return Ok(Expression::AssertEq {
1375 left: Box::new(left),
1376 right: Box::new(right),
1377 message,
1378 });
1379 }
1380 "assertNe" => {
1381 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1382 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1383 let message = if c.args.len() > 2 {
1384 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1385 {
1386 Some(s.to_string())
1387 } else {
1388 None
1389 }
1390 } else {
1391 None
1392 };
1393 return Ok(Expression::AssertNe {
1394 left: Box::new(left),
1395 right: Box::new(right),
1396 message,
1397 });
1398 }
1399 "assertGt" => {
1400 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1401 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1402 let message = if c.args.len() > 2 {
1403 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1404 {
1405 Some(s.to_string())
1406 } else {
1407 None
1408 }
1409 } else {
1410 None
1411 };
1412 return Ok(Expression::AssertGt {
1413 left: Box::new(left),
1414 right: Box::new(right),
1415 message,
1416 });
1417 }
1418 "assertGe" => {
1419 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1420 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1421 let message = if c.args.len() > 2 {
1422 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1423 {
1424 Some(s.to_string())
1425 } else {
1426 None
1427 }
1428 } else {
1429 None
1430 };
1431 return Ok(Expression::AssertGe {
1432 left: Box::new(left),
1433 right: Box::new(right),
1434 message,
1435 });
1436 }
1437 "assertLt" => {
1438 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1439 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1440 let message = if c.args.len() > 2 {
1441 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1442 {
1443 Some(s.to_string())
1444 } else {
1445 None
1446 }
1447 } else {
1448 None
1449 };
1450 return Ok(Expression::AssertLt {
1451 left: Box::new(left),
1452 right: Box::new(right),
1453 message,
1454 });
1455 }
1456 "assertLe" => {
1457 let left = lower_expr(&c.args[0].value, ctx, collector)?;
1458 let right = lower_expr(&c.args[1].value, ctx, collector)?;
1459 let message = if c.args.len() > 2 {
1460 if let ast::Expr::Literal(ast::Literal::String(s, _)) = &c.args[2].value
1461 {
1462 Some(s.to_string())
1463 } else {
1464 None
1465 }
1466 } else {
1467 None
1468 };
1469 return Ok(Expression::AssertLe {
1470 left: Box::new(left),
1471 right: Box::new(right),
1472 message,
1473 });
1474 }
1475 _ => {}
1476 }
1477
1478 if func_name == "address" && c.args.len() == 1 {
1480 if let ast::Expr::Literal(ast::Literal::Int(0, _)) = &c.args[0].value {
1481 return Ok(Expression::Literal(Literal::ZeroAddress));
1482 }
1483 }
1484
1485 if func_name.starts_with("bytes") && c.args.len() == 1 {
1487 if let Ok(n) = func_name[5..].parse::<usize>() {
1488 if (1..=32).contains(&n) {
1489 if let ast::Expr::Literal(ast::Literal::Int(0, _)) = &c.args[0].value {
1490 return Ok(Expression::Literal(Literal::ZeroBytes(n)));
1491 }
1492 }
1493 }
1494 }
1495
1496 if ctx.is_interface(&func_name) && c.args.len() == 1 {
1498 let program_id = lower_expr(&c.args[0].value, ctx, collector)?;
1499 return Ok(Expression::InterfaceCast {
1500 interface_name: func_name,
1501 program_id: Box::new(program_id),
1502 });
1503 }
1504
1505 if func_name == "transfer" && c.args.len() == 2 {
1507 collector.mark_uses_sol_transfer();
1508 let to = lower_expr(&c.args[0].value, ctx, collector)?;
1509 let amount = lower_expr(&c.args[1].value, ctx, collector)?;
1510 return Ok(Expression::SolTransfer {
1511 to: Box::new(to),
1512 amount: Box::new(amount),
1513 });
1514 }
1515
1516 Ok(Expression::Call {
1517 func: func_name,
1518 args: c
1519 .args
1520 .iter()
1521 .map(|a| lower_expr(&a.value, ctx, collector))
1522 .collect::<Result<Vec<_>, _>>()?,
1523 })
1524 } else {
1525 Err(CodegenError::UnsupportedFeature(
1526 "Complex call expressions".to_string(),
1527 ))
1528 }
1529 }
1530 ast::Expr::MethodCall(m) => {
1531 let receiver = lower_expr(&m.receiver, ctx, collector)?;
1532 let method = m.method.name.to_string();
1533 let args: Vec<Expression> = m
1534 .args
1535 .iter()
1536 .map(|a| lower_expr(&a.value, ctx, collector))
1537 .collect::<Result<Vec<_>, _>>()?;
1538
1539 if let Expression::InterfaceCast {
1541 interface_name,
1542 program_id,
1543 } = receiver
1544 {
1545 return Ok(Expression::CpiCall {
1546 program: program_id,
1547 interface_name,
1548 method,
1549 args,
1550 });
1551 }
1552
1553 if let Expression::Var(ref name) = receiver {
1555 match (name.as_str(), method.as_str()) {
1556 ("msg", "sender") => return Ok(Expression::MsgSender),
1557 ("msg", "value") => return Ok(Expression::MsgValue),
1558 ("block", "timestamp") => return Ok(Expression::BlockTimestamp),
1559 ("rent", "minimumBalance") if args.len() == 1 => {
1561 return Ok(Expression::RentMinimumBalance {
1562 data_len: Box::new(args[0].clone()),
1563 });
1564 }
1565 ("rent", "isExempt") if args.len() == 2 => {
1566 return Ok(Expression::RentIsExempt {
1567 lamports: Box::new(args[0].clone()),
1568 data_len: Box::new(args[1].clone()),
1569 });
1570 }
1571 ("token", "transfer") if args.len() == 4 => {
1573 collector.mark_uses_token_program();
1574 return Ok(Expression::TokenTransfer {
1575 from: Box::new(args[0].clone()),
1576 to: Box::new(args[1].clone()),
1577 authority: Box::new(args[2].clone()),
1578 amount: Box::new(args[3].clone()),
1579 });
1580 }
1581 ("token", "mint") if args.len() == 4 => {
1583 collector.mark_uses_token_program();
1584 return Ok(Expression::TokenMint {
1585 mint: Box::new(args[0].clone()),
1586 to: Box::new(args[1].clone()),
1587 authority: Box::new(args[2].clone()),
1588 amount: Box::new(args[3].clone()),
1589 });
1590 }
1591 ("token", "burn") if args.len() == 4 => {
1593 collector.mark_uses_token_program();
1594 return Ok(Expression::TokenBurn {
1595 from: Box::new(args[0].clone()),
1596 mint: Box::new(args[1].clone()),
1597 authority: Box::new(args[2].clone()),
1598 amount: Box::new(args[3].clone()),
1599 });
1600 }
1601 ("token", "getATA") if args.len() == 2 => {
1603 collector.mark_uses_token_program();
1604 return Ok(Expression::GetATA {
1605 owner: Box::new(args[0].clone()),
1606 mint: Box::new(args[1].clone()),
1607 });
1608 }
1609 _ => {}
1610 }
1611 }
1612
1613 Ok(Expression::MethodCall {
1614 receiver: Box::new(receiver),
1615 method,
1616 args,
1617 })
1618 }
1619 ast::Expr::FieldAccess(f) => {
1620 let lowered_expr = lower_expr(&f.expr, ctx, collector)?;
1621 let field = f.field.name.to_string();
1622
1623 if let Expression::Var(ref name) = lowered_expr {
1625 match (name.as_str(), field.as_str()) {
1626 ("msg", "sender") => return Ok(Expression::MsgSender),
1627 ("msg", "value") => return Ok(Expression::MsgValue),
1628 ("block", "timestamp") => return Ok(Expression::BlockTimestamp),
1629 ("block", "number") => return Ok(Expression::BlockTimestamp), ("clock", "timestamp") => return Ok(Expression::ClockUnixTimestamp),
1632 ("clock", "unix_timestamp") => return Ok(Expression::ClockUnixTimestamp),
1633 ("clock", "slot") => return Ok(Expression::ClockSlot),
1634 ("clock", "epoch") => return Ok(Expression::ClockEpoch),
1635 _ => {}
1636 }
1637 }
1638
1639 Ok(Expression::Field {
1640 expr: Box::new(lowered_expr),
1641 field,
1642 })
1643 }
1644 ast::Expr::Index(i) => {
1645 if let Some((mapping_name, keys)) = extract_mapping_access(&i.expr, &i.index, ctx)? {
1647 let lowered_keys: Vec<Expression> = keys
1648 .into_iter()
1649 .map(|k| lower_expr(k, ctx, collector))
1650 .collect::<Result<Vec<_>, _>>()?;
1651
1652 let account_name =
1654 collector.record_access(&mapping_name, lowered_keys.clone(), true, false);
1655 return Ok(Expression::MappingAccess {
1656 mapping_name,
1657 keys: lowered_keys,
1658 account_name,
1659 });
1660 }
1661
1662 Ok(Expression::Index {
1664 expr: Box::new(lower_expr(&i.expr, ctx, collector)?),
1665 index: Box::new(lower_expr(&i.index, ctx, collector)?),
1666 })
1667 }
1668 ast::Expr::Ternary(t) => Ok(Expression::Ternary {
1669 condition: Box::new(lower_expr(&t.condition, ctx, collector)?),
1670 then_expr: Box::new(lower_expr(&t.then_expr, ctx, collector)?),
1671 else_expr: Box::new(lower_expr(&t.else_expr, ctx, collector)?),
1672 }),
1673 ast::Expr::Assign(a) => {
1674 let target = lower_expr(&a.target, ctx, collector)?;
1675 let value = lower_expr(&a.value, ctx, collector)?;
1676
1677 let final_value = match a.op {
1679 ast::AssignOp::Assign => value,
1680 ast::AssignOp::AddAssign => Expression::Binary {
1681 op: BinaryOp::Add,
1682 left: Box::new(target.clone()),
1683 right: Box::new(value),
1684 },
1685 ast::AssignOp::SubAssign => Expression::Binary {
1686 op: BinaryOp::Sub,
1687 left: Box::new(target.clone()),
1688 right: Box::new(value),
1689 },
1690 ast::AssignOp::MulAssign => Expression::Binary {
1691 op: BinaryOp::Mul,
1692 left: Box::new(target.clone()),
1693 right: Box::new(value),
1694 },
1695 ast::AssignOp::DivAssign => Expression::Binary {
1696 op: BinaryOp::Div,
1697 left: Box::new(target.clone()),
1698 right: Box::new(value),
1699 },
1700 ast::AssignOp::RemAssign => Expression::Binary {
1701 op: BinaryOp::Rem,
1702 left: Box::new(target.clone()),
1703 right: Box::new(value),
1704 },
1705 ast::AssignOp::BitAndAssign => Expression::Binary {
1706 op: BinaryOp::BitAnd,
1707 left: Box::new(target.clone()),
1708 right: Box::new(value),
1709 },
1710 ast::AssignOp::BitOrAssign => Expression::Binary {
1711 op: BinaryOp::BitOr,
1712 left: Box::new(target.clone()),
1713 right: Box::new(value),
1714 },
1715 ast::AssignOp::BitXorAssign => Expression::Binary {
1716 op: BinaryOp::BitXor,
1717 left: Box::new(target.clone()),
1718 right: Box::new(value),
1719 },
1720 };
1721
1722 Ok(Expression::MethodCall {
1725 receiver: Box::new(target),
1726 method: "__assign__".to_string(),
1727 args: vec![final_value],
1728 })
1729 }
1730 ast::Expr::Array(a) => {
1731 if a.elements.is_empty() {
1733 Ok(Expression::Call {
1734 func: "Vec::new".to_string(),
1735 args: vec![],
1736 })
1737 } else {
1738 Ok(Expression::Call {
1739 func: "vec!".to_string(),
1740 args: a
1741 .elements
1742 .iter()
1743 .map(|e| lower_expr(e, ctx, collector))
1744 .collect::<Result<Vec<_>, _>>()?,
1745 })
1746 }
1747 }
1748 ast::Expr::Paren(e) => lower_expr(e, ctx, collector),
1749 ast::Expr::If(_) => Err(CodegenError::UnsupportedFeature(
1750 "If expressions".to_string(),
1751 )),
1752 ast::Expr::Tuple(_) => Err(CodegenError::UnsupportedFeature(
1753 "Tuple expressions".to_string(),
1754 )),
1755 ast::Expr::New(_) => Err(CodegenError::UnsupportedFeature(
1756 "New expressions (use CPI instead)".to_string(),
1757 )),
1758 }
1759}
1760
1761fn lower_literal(lit: &ast::Literal) -> Result<Expression, CodegenError> {
1762 match lit {
1763 ast::Literal::Bool(b, _) => Ok(Expression::Literal(Literal::Bool(*b))),
1764 ast::Literal::Int(n, _) => Ok(Expression::Literal(Literal::Uint(*n))),
1765 ast::Literal::HexInt(s, _) => {
1766 let n = u128::from_str_radix(s.trim_start_matches("0x"), 16)
1767 .map_err(|_| CodegenError::TypeConversion(format!("Invalid hex: {}", s)))?;
1768 Ok(Expression::Literal(Literal::Uint(n)))
1769 }
1770 ast::Literal::String(s, _) => Ok(Expression::Literal(Literal::String(s.to_string()))),
1771 ast::Literal::HexString(s, _) => Ok(Expression::Literal(Literal::String(s.to_string()))),
1772 ast::Literal::Address(s, _) => Ok(Expression::Literal(Literal::Pubkey(s.to_string()))),
1773 }
1774}
1775
1776fn lower_binary_op(op: &ast::BinaryOp) -> BinaryOp {
1777 match op {
1778 ast::BinaryOp::Add => BinaryOp::Add,
1779 ast::BinaryOp::Sub => BinaryOp::Sub,
1780 ast::BinaryOp::Mul => BinaryOp::Mul,
1781 ast::BinaryOp::Div => BinaryOp::Div,
1782 ast::BinaryOp::Rem => BinaryOp::Rem,
1783 ast::BinaryOp::Exp => BinaryOp::Mul, ast::BinaryOp::Eq => BinaryOp::Eq,
1785 ast::BinaryOp::Ne => BinaryOp::Ne,
1786 ast::BinaryOp::Lt => BinaryOp::Lt,
1787 ast::BinaryOp::Le => BinaryOp::Le,
1788 ast::BinaryOp::Gt => BinaryOp::Gt,
1789 ast::BinaryOp::Ge => BinaryOp::Ge,
1790 ast::BinaryOp::And => BinaryOp::And,
1791 ast::BinaryOp::Or => BinaryOp::Or,
1792 ast::BinaryOp::BitAnd => BinaryOp::BitAnd,
1793 ast::BinaryOp::BitOr => BinaryOp::BitOr,
1794 ast::BinaryOp::BitXor => BinaryOp::BitXor,
1795 ast::BinaryOp::Shl => BinaryOp::Shl,
1796 ast::BinaryOp::Shr => BinaryOp::Shr,
1797 }
1798}
1799
1800fn lower_unary_op(op: &ast::UnaryOp) -> UnaryOp {
1801 match op {
1802 ast::UnaryOp::Neg => UnaryOp::Neg,
1803 ast::UnaryOp::Not => UnaryOp::Not,
1804 ast::UnaryOp::BitNot => UnaryOp::BitNot,
1805 ast::UnaryOp::PreInc | ast::UnaryOp::PostInc => UnaryOp::Neg, ast::UnaryOp::PreDec | ast::UnaryOp::PostDec => UnaryOp::Neg, }
1808}