1use swc_common::sync::Lrc;
7use swc_common::{FileName, SourceMap};
8use swc_ecma_ast as swc;
9use swc_ecma_ast::{
10 Accessibility, AssignExpr, AssignOp, AssignTarget, CallExpr, Callee, Class, ClassDecl,
11 ClassMember, Decl, EsVersion, Expr, ForStmt, IfStmt, Lit, MemberExpr as SwcMemberExpr,
12 MemberProp, ModuleDecl, ModuleItem, Param, ParamOrTsParamProp, Pat, PropName, ReturnStmt,
13 SimpleAssignTarget, Stmt, SuperProp, TsEntityName, TsKeywordTypeKind, TsLit,
14 TsParamPropParam, TsType, UnaryExpr as SwcUnaryExpr, UpdateExpr, UpdateOp, VarDecl,
15 VarDeclKind, VarDeclOrExpr,
16};
17use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsSyntax};
18
19use super::ast::{
20 BinaryOp, ContractNode, Expression, MethodNode, ParamNode, PrimitiveTypeName,
21 PropertyNode, SourceLocation, Statement, TypeNode, UnaryOp, Visibility,
22};
23
24pub struct ParseResult {
30 pub contract: Option<ContractNode>,
31 pub errors: Vec<String>,
32}
33
34pub fn parse(source: &str, file_name: Option<&str>) -> ParseResult {
36 let mut errors: Vec<String> = Vec::new();
37 let file = file_name.unwrap_or("contract.ts");
38
39 let cm: Lrc<SourceMap> = Lrc::new(SourceMap::default());
41 let fm = cm.new_source_file(Lrc::new(FileName::Custom(file.to_string())), source.to_string());
42 let lexer = Lexer::new(
43 Syntax::Typescript(TsSyntax {
44 tsx: false,
45 decorators: false,
46 ..Default::default()
47 }),
48 EsVersion::Es2022,
49 StringInput::from(&*fm),
50 None,
51 );
52 let mut parser = Parser::new_from(lexer);
53
54 let module = match parser.parse_module() {
55 Ok(m) => m,
56 Err(e) => {
57 errors.push(format!("Parse error: {:?}", e));
58 return ParseResult {
59 contract: None,
60 errors,
61 };
62 }
63 };
64
65 for e in parser.take_errors() {
67 errors.push(format!("Parse error: {:?}", e));
68 }
69
70 let mut contract_class: Option<&ClassDecl> = None;
72 let mut detected_parent_class: &str = "SmartContract";
73
74 for item in &module.body {
75 if let ModuleItem::Stmt(Stmt::Decl(Decl::Class(class_decl))) = item {
76 if let Some(super_class) = &class_decl.class.super_class {
77 if let Some(base_name) = get_base_class_name(super_class) {
78 if contract_class.is_some() {
79 errors.push(
80 "Only one SmartContract subclass is allowed per file".to_string(),
81 );
82 }
83 contract_class = Some(class_decl);
84 detected_parent_class = base_name;
85 }
86 }
87 }
88 }
89
90 for item in &module.body {
92 if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) = item {
93 if let Decl::Class(class_decl) = &export_decl.decl {
94 if let Some(super_class) = &class_decl.class.super_class {
95 if let Some(base_name) = get_base_class_name(super_class) {
96 if contract_class.is_some() {
97 errors.push(
98 "Only one SmartContract subclass is allowed per file".to_string(),
99 );
100 }
101 contract_class = Some(class_decl);
102 detected_parent_class = base_name;
103 }
104 }
105 }
106 }
107 }
108
109 let class_decl = match contract_class {
110 Some(c) => c,
111 None => {
112 errors.push("No class extending SmartContract or StatefulSmartContract found".to_string());
113 return ParseResult {
114 contract: None,
115 errors,
116 };
117 }
118 };
119
120 let contract_name = class_decl.ident.sym.to_string();
121 let class = &class_decl.class;
122
123 let properties = parse_properties(class, file, &mut errors);
125
126 let constructor_node = parse_constructor(class, file, &mut errors);
128
129 let methods = parse_methods(class, file, &mut errors);
131
132 let contract = ContractNode {
133 name: contract_name,
134 parent_class: detected_parent_class.to_string(),
135 properties,
136 constructor: constructor_node,
137 methods,
138 source_file: file.to_string(),
139 };
140
141 ParseResult {
142 contract: Some(contract),
143 errors,
144 }
145}
146
147fn get_base_class_name(expr: &Expr) -> Option<&str> {
152 match expr {
153 Expr::Ident(ident) => {
154 let name = ident.sym.as_ref();
155 if name == "SmartContract" || name == "StatefulSmartContract" {
156 Some(name)
157 } else {
158 None
159 }
160 }
161 _ => None,
162 }
163}
164
165fn loc(file: &str, line: usize, column: usize) -> SourceLocation {
166 SourceLocation {
167 file: file.to_string(),
168 line,
169 column,
170 }
171}
172
173fn default_loc(file: &str) -> SourceLocation {
174 loc(file, 1, 0)
175}
176
177fn parse_properties(class: &Class, file: &str, errors: &mut Vec<String>) -> Vec<PropertyNode> {
182 let mut result = Vec::new();
183
184 for member in &class.body {
185 if let ClassMember::ClassProp(prop) = member {
186 let name = match &prop.key {
187 PropName::Ident(ident) => ident.sym.to_string(),
188 _ => {
189 errors.push("Property must have an identifier name".to_string());
190 continue;
191 }
192 };
193
194 let readonly = prop.readonly;
195
196 let prop_type = if let Some(ref ann) = prop.type_ann {
197 parse_type_node(&ann.type_ann, file, errors)
198 } else {
199 errors.push(format!(
200 "Property '{}' must have an explicit type annotation",
201 name
202 ));
203 TypeNode::Custom("unknown".to_string())
204 };
205
206 let initializer = prop.value.as_ref().map(|v| parse_expression(v, file, errors));
208
209 result.push(PropertyNode {
210 name,
211 prop_type,
212 readonly,
213 initializer,
214 source_location: default_loc(file),
215 });
216 }
217 }
218
219 result
220}
221
222fn parse_constructor(class: &Class, file: &str, errors: &mut Vec<String>) -> MethodNode {
227 for member in &class.body {
228 if let ClassMember::Constructor(ctor) = member {
229 let params = parse_constructor_params(&ctor.params, file, errors);
230 let body = if let Some(ref block) = ctor.body {
231 parse_block_stmts(&block.stmts, file, errors)
232 } else {
233 Vec::new()
234 };
235
236 return MethodNode {
237 name: "constructor".to_string(),
238 params,
239 body,
240 visibility: Visibility::Public,
241 source_location: default_loc(file),
242 };
243 }
244 }
245
246 errors.push("Contract must have a constructor".to_string());
247 MethodNode {
248 name: "constructor".to_string(),
249 params: Vec::new(),
250 body: Vec::new(),
251 visibility: Visibility::Public,
252 source_location: default_loc(file),
253 }
254}
255
256fn parse_constructor_params(
257 params: &[ParamOrTsParamProp],
258 file: &str,
259 errors: &mut Vec<String>,
260) -> Vec<ParamNode> {
261 let mut result = Vec::new();
262
263 for param in params {
264 match param {
265 ParamOrTsParamProp::Param(p) => {
266 if let Some(ast_param) = parse_param_pat(&p.pat, file, errors) {
267 result.push(ast_param);
268 }
269 }
270 ParamOrTsParamProp::TsParamProp(ts_param) => {
271 match &ts_param.param {
272 TsParamPropParam::Ident(ident) => {
273 let name = ident.id.sym.to_string();
274 let param_type = if let Some(ref ann) = ident.type_ann {
275 parse_type_node(&ann.type_ann, file, errors)
276 } else {
277 errors.push(format!(
278 "Parameter '{}' must have an explicit type annotation",
279 name
280 ));
281 TypeNode::Custom("unknown".to_string())
282 };
283 result.push(ParamNode { name, param_type });
284 }
285 TsParamPropParam::Assign(_) => {
286 errors.push("Default parameter values are not supported".to_string());
287 }
288 }
289 }
290 }
291 }
292
293 result
294}
295
296fn parse_methods(class: &Class, file: &str, errors: &mut Vec<String>) -> Vec<MethodNode> {
301 let mut result = Vec::new();
302
303 for member in &class.body {
304 if let ClassMember::Method(method) = member {
305 let name = match &method.key {
306 PropName::Ident(ident) => ident.sym.to_string(),
307 _ => {
308 errors.push("Method must have an identifier name".to_string());
309 continue;
310 }
311 };
312
313 let params = parse_method_params(&method.function.params, file, errors);
314
315 let visibility = if method.accessibility == Some(Accessibility::Public) {
316 Visibility::Public
317 } else {
318 Visibility::Private
319 };
320
321 let body = if let Some(ref block) = method.function.body {
322 parse_block_stmts(&block.stmts, file, errors)
323 } else {
324 Vec::new()
325 };
326
327 result.push(MethodNode {
328 name,
329 params,
330 body,
331 visibility,
332 source_location: default_loc(file),
333 });
334 }
335 }
336
337 result
338}
339
340fn parse_method_params(
341 params: &[Param],
342 file: &str,
343 errors: &mut Vec<String>,
344) -> Vec<ParamNode> {
345 let mut result = Vec::new();
346 for param in params {
347 if let Some(ast_param) = parse_param_pat(¶m.pat, file, errors) {
348 result.push(ast_param);
349 }
350 }
351 result
352}
353
354fn parse_param_pat(
355 pat: &Pat,
356 file: &str,
357 errors: &mut Vec<String>,
358) -> Option<ParamNode> {
359 match pat {
360 Pat::Ident(ident) => {
361 let name = ident.id.sym.to_string();
362 let param_type = if let Some(ref ann) = ident.type_ann {
363 parse_type_node(&ann.type_ann, file, errors)
364 } else {
365 errors.push(format!(
366 "Parameter '{}' must have an explicit type annotation",
367 name
368 ));
369 TypeNode::Custom("unknown".to_string())
370 };
371 Some(ParamNode { name, param_type })
372 }
373 _ => {
374 errors.push("Unsupported parameter pattern".to_string());
375 None
376 }
377 }
378}
379
380fn parse_type_node(ts_type: &TsType, file: &str, errors: &mut Vec<String>) -> TypeNode {
385 match ts_type {
386 TsType::TsKeywordType(kw) => match kw.kind {
388 TsKeywordTypeKind::TsBigIntKeyword => TypeNode::Primitive(PrimitiveTypeName::Bigint),
389 TsKeywordTypeKind::TsBooleanKeyword => {
390 TypeNode::Primitive(PrimitiveTypeName::Boolean)
391 }
392 TsKeywordTypeKind::TsVoidKeyword => TypeNode::Primitive(PrimitiveTypeName::Void),
393 TsKeywordTypeKind::TsNumberKeyword => {
394 errors.push("'number' type is not allowed in Rúnar contracts; use 'bigint' instead".to_string());
395 TypeNode::Primitive(PrimitiveTypeName::Bigint)
396 }
397 TsKeywordTypeKind::TsStringKeyword => {
398 errors.push("'string' type is not allowed in Rúnar contracts; use 'ByteString' instead".to_string());
399 TypeNode::Primitive(PrimitiveTypeName::ByteString)
400 }
401 _ => {
402 errors.push(format!("Unsupported keyword type: {:?}", kw.kind));
403 TypeNode::Custom("unknown".to_string())
404 }
405 },
406
407 TsType::TsTypeRef(type_ref) => {
409 let type_name = ts_entity_name_to_string(&type_ref.type_name);
410
411 if type_name == "FixedArray" {
413 if let Some(ref type_params) = type_ref.type_params {
414 let params = &type_params.params;
415 if params.len() != 2 {
416 errors.push(
417 "FixedArray requires exactly 2 type arguments: FixedArray<T, N>"
418 .to_string(),
419 );
420 return TypeNode::Custom(type_name);
421 }
422
423 let element = parse_type_node(¶ms[0], file, errors);
424
425 let length = extract_type_literal_number(¶ms[1]);
427 if let Some(len) = length {
428 return TypeNode::FixedArray {
429 element: Box::new(element),
430 length: len,
431 };
432 } else {
433 errors.push(
434 "FixedArray size must be a non-negative integer literal".to_string(),
435 );
436 return TypeNode::Custom(type_name);
437 }
438 } else {
439 errors.push("FixedArray requires type arguments".to_string());
440 return TypeNode::Custom(type_name);
441 }
442 }
443
444 if let Some(prim) = PrimitiveTypeName::from_str(&type_name) {
446 return TypeNode::Primitive(prim);
447 }
448
449 TypeNode::Custom(type_name)
451 }
452
453 _ => {
454 errors.push("Unsupported type annotation".to_string());
455 TypeNode::Custom("unknown".to_string())
456 }
457 }
458}
459
460fn ts_entity_name_to_string(entity: &TsEntityName) -> String {
461 match entity {
462 TsEntityName::Ident(ident) => ident.sym.to_string(),
463 TsEntityName::TsQualifiedName(qual) => {
464 format!(
465 "{}.{}",
466 ts_entity_name_to_string(&qual.left),
467 qual.right.sym
468 )
469 }
470 }
471}
472
473fn extract_type_literal_number(ts_type: &TsType) -> Option<usize> {
475 match ts_type {
476 TsType::TsLitType(lit) => match &lit.lit {
477 TsLit::Number(n) => Some(n.value as usize),
478 _ => None,
479 },
480 _ => None,
481 }
482}
483
484fn parse_block_stmts(stmts: &[Stmt], file: &str, errors: &mut Vec<String>) -> Vec<Statement> {
489 let mut result = Vec::new();
490 for stmt in stmts {
491 if let Some(parsed) = parse_statement(stmt, file, errors) {
492 result.push(parsed);
493 }
494 }
495 result
496}
497
498fn parse_statement(stmt: &Stmt, file: &str, errors: &mut Vec<String>) -> Option<Statement> {
499 match stmt {
500 Stmt::Decl(Decl::Var(var_decl)) => parse_variable_statement(var_decl, file, errors),
501
502 Stmt::Expr(expr_stmt) => parse_expression_statement(&expr_stmt.expr, file, errors),
503
504 Stmt::If(if_stmt) => Some(parse_if_statement(if_stmt, file, errors)),
505
506 Stmt::For(for_stmt) => Some(parse_for_statement(for_stmt, file, errors)),
507
508 Stmt::Return(ret_stmt) => Some(parse_return_statement(ret_stmt, file, errors)),
509
510 Stmt::Block(block) => {
511 let stmts = parse_block_stmts(&block.stmts, file, errors);
513 if stmts.is_empty() {
516 None
517 } else {
518 errors.push("Standalone block statements are not supported; use if/for".to_string());
519 None
520 }
521 }
522
523 _ => {
524 errors.push(format!("Unsupported statement kind: {:?}", stmt));
525 None
526 }
527 }
528}
529
530fn parse_variable_statement(
531 var_decl: &VarDecl,
532 file: &str,
533 errors: &mut Vec<String>,
534) -> Option<Statement> {
535 if var_decl.decls.is_empty() {
536 return None;
537 }
538
539 let decl = &var_decl.decls[0];
540 let name = match &decl.name {
541 Pat::Ident(ident) => ident.id.sym.to_string(),
542 _ => {
543 errors.push("Destructuring patterns are not supported in variable declarations".to_string());
544 return None;
545 }
546 };
547
548 let is_const = var_decl.kind == VarDeclKind::Const;
549
550 let init = if let Some(ref init_expr) = decl.init {
551 parse_expression(init_expr, file, errors)
552 } else {
553 errors.push(format!("Variable '{}' must have an initializer", name));
554 Expression::BigIntLiteral { value: 0 }
555 };
556
557 let var_type = if let Pat::Ident(ident) = &decl.name {
558 if let Some(ref ann) = ident.type_ann {
559 Some(parse_type_node(&ann.type_ann, file, errors))
560 } else {
561 None
562 }
563 } else {
564 None
565 };
566
567 Some(Statement::VariableDecl {
568 name,
569 var_type,
570 mutable: !is_const,
571 init,
572 source_location: default_loc(file),
573 })
574}
575
576fn parse_expression_statement(
577 expr: &Expr,
578 file: &str,
579 errors: &mut Vec<String>,
580) -> Option<Statement> {
581 if let Expr::Assign(assign) = expr {
583 return Some(parse_assignment_expr(assign, file, errors));
584 }
585
586 let expression = parse_expression(expr, file, errors);
587 Some(Statement::ExpressionStatement {
588 expression,
589 source_location: default_loc(file),
590 })
591}
592
593fn parse_assignment_expr(
594 assign: &AssignExpr,
595 file: &str,
596 errors: &mut Vec<String>,
597) -> Statement {
598 let target = parse_assign_target(&assign.left, file, errors);
599
600 match assign.op {
601 AssignOp::Assign => {
602 let value = parse_expression(&assign.right, file, errors);
603 Statement::Assignment {
604 target,
605 value,
606 source_location: default_loc(file),
607 }
608 }
609 op => {
611 let bin_op = match op {
612 AssignOp::AddAssign => Some(BinaryOp::Add),
613 AssignOp::SubAssign => Some(BinaryOp::Sub),
614 AssignOp::MulAssign => Some(BinaryOp::Mul),
615 AssignOp::DivAssign => Some(BinaryOp::Div),
616 AssignOp::ModAssign => Some(BinaryOp::Mod),
617 _ => {
618 errors.push(format!("Unsupported compound assignment operator: {:?}", op));
619 None
620 }
621 };
622
623 if let Some(bin_op) = bin_op {
624 let right = parse_expression(&assign.right, file, errors);
625 let target_for_rhs = parse_assign_target(&assign.left, file, errors);
626 let value = Expression::BinaryExpr {
627 op: bin_op,
628 left: Box::new(target_for_rhs),
629 right: Box::new(right),
630 };
631 Statement::Assignment {
632 target,
633 value,
634 source_location: default_loc(file),
635 }
636 } else {
637 let value = parse_expression(&assign.right, file, errors);
638 Statement::Assignment {
639 target,
640 value,
641 source_location: default_loc(file),
642 }
643 }
644 }
645 }
646}
647
648fn parse_assign_target(
649 target: &AssignTarget,
650 file: &str,
651 errors: &mut Vec<String>,
652) -> Expression {
653 match target {
654 AssignTarget::Simple(simple) => match simple {
655 SimpleAssignTarget::Ident(ident) => Expression::Identifier {
656 name: ident.id.sym.to_string(),
657 },
658 SimpleAssignTarget::Member(member) => {
659 parse_member_expression(member, file, errors)
660 }
661 _ => {
662 errors.push("Unsupported assignment target".to_string());
663 Expression::Identifier {
664 name: "_error".to_string(),
665 }
666 }
667 },
668 AssignTarget::Pat(_) => {
669 errors.push("Destructuring assignment is not supported".to_string());
670 Expression::Identifier {
671 name: "_error".to_string(),
672 }
673 }
674 }
675}
676
677fn parse_if_statement(if_stmt: &IfStmt, file: &str, errors: &mut Vec<String>) -> Statement {
678 let condition = parse_expression(&if_stmt.test, file, errors);
679 let then_branch = parse_stmt_or_block(&if_stmt.cons, file, errors);
680
681 let else_branch = if_stmt
682 .alt
683 .as_ref()
684 .map(|alt| parse_stmt_or_block(alt, file, errors));
685
686 Statement::IfStatement {
687 condition,
688 then_branch,
689 else_branch,
690 source_location: default_loc(file),
691 }
692}
693
694fn parse_stmt_or_block(
695 stmt: &Stmt,
696 file: &str,
697 errors: &mut Vec<String>,
698) -> Vec<Statement> {
699 match stmt {
700 Stmt::Block(block) => parse_block_stmts(&block.stmts, file, errors),
701 _ => {
702 if let Some(s) = parse_statement(stmt, file, errors) {
703 vec![s]
704 } else {
705 Vec::new()
706 }
707 }
708 }
709}
710
711fn parse_for_statement(for_stmt: &ForStmt, file: &str, errors: &mut Vec<String>) -> Statement {
712 let init = if let Some(ref init_expr) = for_stmt.init {
714 match init_expr {
715 VarDeclOrExpr::VarDecl(var_decl) => {
716 if let Some(stmt) = parse_variable_statement(var_decl, file, errors) {
717 stmt
718 } else {
719 make_default_for_init(file)
720 }
721 }
722 VarDeclOrExpr::Expr(_) => {
723 errors.push(
724 "For loop must have a variable declaration initializer".to_string(),
725 );
726 make_default_for_init(file)
727 }
728 }
729 } else {
730 errors.push("For loop must have an initializer".to_string());
731 make_default_for_init(file)
732 };
733
734 let condition = if let Some(ref cond) = for_stmt.test {
736 parse_expression(cond, file, errors)
737 } else {
738 errors.push("For loop must have a condition".to_string());
739 Expression::BoolLiteral { value: false }
740 };
741
742 let update = if let Some(ref upd) = for_stmt.update {
744 parse_for_update(upd, file, errors)
745 } else {
746 errors.push("For loop must have an update expression".to_string());
747 Statement::ExpressionStatement {
748 expression: Expression::BigIntLiteral { value: 0 },
749 source_location: default_loc(file),
750 }
751 };
752
753 let body = parse_stmt_or_block(&for_stmt.body, file, errors);
755
756 Statement::ForStatement {
757 init: Box::new(init),
758 condition,
759 update: Box::new(update),
760 body,
761 source_location: default_loc(file),
762 }
763}
764
765fn parse_for_update(
766 expr: &Expr,
767 file: &str,
768 errors: &mut Vec<String>,
769) -> Statement {
770 match expr {
771 Expr::Update(update) => {
772 let operand = parse_expression(&update.arg, file, errors);
773 let is_increment = update.op == UpdateOp::PlusPlus;
774 let expression = if is_increment {
775 Expression::IncrementExpr {
776 operand: Box::new(operand),
777 prefix: update.prefix,
778 }
779 } else {
780 Expression::DecrementExpr {
781 operand: Box::new(operand),
782 prefix: update.prefix,
783 }
784 };
785 Statement::ExpressionStatement {
786 expression,
787 source_location: default_loc(file),
788 }
789 }
790 _ => {
791 let expression = parse_expression(expr, file, errors);
792 Statement::ExpressionStatement {
793 expression,
794 source_location: default_loc(file),
795 }
796 }
797 }
798}
799
800fn parse_return_statement(
801 ret_stmt: &ReturnStmt,
802 file: &str,
803 errors: &mut Vec<String>,
804) -> Statement {
805 let value = ret_stmt
806 .arg
807 .as_ref()
808 .map(|e| parse_expression(e, file, errors));
809
810 Statement::ReturnStatement {
811 value,
812 source_location: default_loc(file),
813 }
814}
815
816fn make_default_for_init(file: &str) -> Statement {
817 Statement::VariableDecl {
818 name: "_i".to_string(),
819 var_type: None,
820 mutable: true,
821 init: Expression::BigIntLiteral { value: 0 },
822 source_location: default_loc(file),
823 }
824}
825
826fn parse_expression(expr: &Expr, file: &str, errors: &mut Vec<String>) -> Expression {
831 match expr {
832 Expr::Bin(bin) => parse_binary_expression(bin, file, errors),
833
834 Expr::Unary(unary) => parse_unary_expression(unary, file, errors),
835
836 Expr::Update(update) => parse_update_expression(update, file, errors),
837
838 Expr::Call(call) => parse_call_expression(call, file, errors),
839
840 Expr::Member(member) => parse_member_expression(member, file, errors),
841
842 Expr::SuperProp(super_prop) => {
843 match &super_prop.prop {
845 SuperProp::Ident(ident) => Expression::MemberExpr {
846 object: Box::new(Expression::Identifier {
847 name: "super".to_string(),
848 }),
849 property: ident.sym.to_string(),
850 },
851 SuperProp::Computed(comp) => {
852 let _ = parse_expression(&comp.expr, file, errors);
853 errors.push("Computed super property access not supported".to_string());
854 Expression::Identifier {
855 name: "super".to_string(),
856 }
857 }
858 }
859 }
860
861 Expr::Ident(ident) => Expression::Identifier {
862 name: ident.sym.to_string(),
863 },
864
865 Expr::Lit(Lit::BigInt(bigint)) => {
866 let val = bigint_to_i64(bigint);
868 Expression::BigIntLiteral { value: val }
869 }
870
871 Expr::Lit(Lit::Num(num)) => {
872 Expression::BigIntLiteral {
874 value: num.value as i64,
875 }
876 }
877
878 Expr::Lit(Lit::Bool(b)) => Expression::BoolLiteral { value: b.value },
879
880 Expr::Lit(Lit::Str(s)) => {
881 Expression::ByteStringLiteral {
883 value: s.value.to_string(),
884 }
885 }
886
887 Expr::Tpl(tpl) => {
888 if tpl.exprs.is_empty() && tpl.quasis.len() == 1 {
890 Expression::ByteStringLiteral {
891 value: tpl.quasis[0].raw.to_string(),
892 }
893 } else {
894 errors.push("Template literals with expressions are not supported".to_string());
895 Expression::ByteStringLiteral {
896 value: String::new(),
897 }
898 }
899 }
900
901 Expr::Cond(cond) => {
902 let condition = parse_expression(&cond.test, file, errors);
903 let consequent = parse_expression(&cond.cons, file, errors);
904 let alternate = parse_expression(&cond.alt, file, errors);
905 Expression::TernaryExpr {
906 condition: Box::new(condition),
907 consequent: Box::new(consequent),
908 alternate: Box::new(alternate),
909 }
910 }
911
912 Expr::Paren(paren) => parse_expression(&paren.expr, file, errors),
913
914 Expr::This(_) => Expression::Identifier {
915 name: "this".to_string(),
916 },
917
918 Expr::TsAs(as_expr) => {
919 parse_expression(&as_expr.expr, file, errors)
921 }
922
923 Expr::TsNonNull(nn) => {
924 parse_expression(&nn.expr, file, errors)
926 }
927
928 Expr::Assign(assign) => {
929 errors.push("Assignment expressions in expression context are not recommended".to_string());
932 let value = parse_expression(&assign.right, file, errors);
933 value
934 }
935
936 _ => {
937 errors.push(format!("Unsupported expression: {:?}", expr));
938 Expression::BigIntLiteral { value: 0 }
939 }
940 }
941}
942
943fn parse_binary_expression(
944 bin: &swc::BinExpr,
945 file: &str,
946 errors: &mut Vec<String>,
947) -> Expression {
948 let left = parse_expression(&bin.left, file, errors);
949 let right = parse_expression(&bin.right, file, errors);
950
951 let op = match bin.op {
952 swc::BinaryOp::Add => BinaryOp::Add,
953 swc::BinaryOp::Sub => BinaryOp::Sub,
954 swc::BinaryOp::Mul => BinaryOp::Mul,
955 swc::BinaryOp::Div => BinaryOp::Div,
956 swc::BinaryOp::Mod => BinaryOp::Mod,
957 swc::BinaryOp::EqEqEq => BinaryOp::StrictEq,
958 swc::BinaryOp::NotEqEq => BinaryOp::StrictNe,
959 swc::BinaryOp::Lt => BinaryOp::Lt,
960 swc::BinaryOp::LtEq => BinaryOp::Le,
961 swc::BinaryOp::Gt => BinaryOp::Gt,
962 swc::BinaryOp::GtEq => BinaryOp::Ge,
963 swc::BinaryOp::LogicalAnd => BinaryOp::And,
964 swc::BinaryOp::LogicalOr => BinaryOp::Or,
965 swc::BinaryOp::BitAnd => BinaryOp::BitAnd,
966 swc::BinaryOp::BitOr => BinaryOp::BitOr,
967 swc::BinaryOp::BitXor => BinaryOp::BitXor,
968 swc::BinaryOp::EqEq => {
969 errors.push("Use === instead of == for equality comparison".to_string());
970 BinaryOp::StrictEq
971 }
972 swc::BinaryOp::NotEq => {
973 errors.push("Use !== instead of != for inequality comparison".to_string());
974 BinaryOp::StrictNe
975 }
976 _ => {
977 errors.push(format!("Unsupported binary operator: {:?}", bin.op));
978 BinaryOp::Add
979 }
980 };
981
982 Expression::BinaryExpr {
983 op,
984 left: Box::new(left),
985 right: Box::new(right),
986 }
987}
988
989fn parse_unary_expression(
990 unary: &SwcUnaryExpr,
991 file: &str,
992 errors: &mut Vec<String>,
993) -> Expression {
994 let operand = parse_expression(&unary.arg, file, errors);
995
996 let op = match unary.op {
997 swc::UnaryOp::Bang => UnaryOp::Not,
998 swc::UnaryOp::Minus => UnaryOp::Neg,
999 swc::UnaryOp::Tilde => UnaryOp::BitNot,
1000 _ => {
1001 errors.push(format!("Unsupported unary operator: {:?}", unary.op));
1002 UnaryOp::Neg
1003 }
1004 };
1005
1006 Expression::UnaryExpr {
1007 op,
1008 operand: Box::new(operand),
1009 }
1010}
1011
1012fn parse_update_expression(
1013 update: &UpdateExpr,
1014 file: &str,
1015 errors: &mut Vec<String>,
1016) -> Expression {
1017 let operand = parse_expression(&update.arg, file, errors);
1018
1019 if update.op == UpdateOp::PlusPlus {
1020 Expression::IncrementExpr {
1021 operand: Box::new(operand),
1022 prefix: update.prefix,
1023 }
1024 } else {
1025 Expression::DecrementExpr {
1026 operand: Box::new(operand),
1027 prefix: update.prefix,
1028 }
1029 }
1030}
1031
1032fn parse_call_expression(
1033 call: &CallExpr,
1034 file: &str,
1035 errors: &mut Vec<String>,
1036) -> Expression {
1037 let callee = match &call.callee {
1038 Callee::Expr(e) => parse_expression(e, file, errors),
1039 Callee::Super(_) => Expression::Identifier {
1040 name: "super".to_string(),
1041 },
1042 Callee::Import(_) => {
1043 errors.push("Dynamic import is not supported".to_string());
1044 Expression::Identifier {
1045 name: "_error".to_string(),
1046 }
1047 }
1048 };
1049
1050 let args: Vec<Expression> = call
1051 .args
1052 .iter()
1053 .map(|arg| parse_expression(&arg.expr, file, errors))
1054 .collect();
1055
1056 Expression::CallExpr {
1057 callee: Box::new(callee),
1058 args,
1059 }
1060}
1061
1062fn parse_member_expression(
1063 member: &SwcMemberExpr,
1064 file: &str,
1065 errors: &mut Vec<String>,
1066) -> Expression {
1067 let prop_name = match &member.prop {
1068 MemberProp::Ident(ident) => ident.sym.to_string(),
1069 MemberProp::Computed(comp) => {
1070 let object = parse_expression(&member.obj, file, errors);
1072 let index = parse_expression(&comp.expr, file, errors);
1073 return Expression::IndexAccess {
1074 object: Box::new(object),
1075 index: Box::new(index),
1076 };
1077 }
1078 MemberProp::PrivateName(_priv_name) => {
1079 errors.push("Private field access (#field) is not supported".to_string());
1080 "_private".to_string()
1081 }
1082 };
1083
1084 if let Expr::This(_) = &*member.obj {
1086 return Expression::PropertyAccess {
1087 property: prop_name,
1088 };
1089 }
1090
1091 let object = parse_expression(&member.obj, file, errors);
1093 Expression::MemberExpr {
1094 object: Box::new(object),
1095 property: prop_name,
1096 }
1097}
1098
1099fn bigint_to_i64(bigint_lit: &swc::BigInt) -> i64 {
1105 use std::str::FromStr;
1108 let s = bigint_lit.value.to_string();
1109 i64::from_str(&s).unwrap_or(0)
1110}
1111
1112pub fn parse_source(source: &str, file_name: Option<&str>) -> ParseResult {
1125 let name = file_name.unwrap_or("contract.ts");
1126 if name.ends_with(".runar.sol") {
1127 return super::parser_sol::parse_solidity(source, file_name);
1128 }
1129 if name.ends_with(".runar.move") {
1130 return super::parser_move::parse_move(source, file_name);
1131 }
1132 if name.ends_with(".runar.rs") {
1133 return super::parser_rustmacro::parse_rust_dsl(source, file_name);
1134 }
1135 if name.ends_with(".runar.py") {
1136 return super::parser_python::parse_python(source, file_name);
1137 }
1138 parse(source, file_name)
1140}