1use super::ast::{
44 BinaryOp, ContractNode, Expression, MethodNode, ParamNode, PrimitiveTypeName, PropertyNode,
45 SourceLocation, Statement, TypeNode, UnaryOp, Visibility,
46};
47use super::parser::ParseResult;
48use std::collections::HashMap;
49
50pub fn parse_ruby(source: &str, file_name: Option<&str>) -> ParseResult {
56 use super::diagnostic::Diagnostic;
57
58 let file = file_name.unwrap_or("contract.runar.rb");
59 let mut str_errors: Vec<String> = Vec::new();
60
61 let tokens = tokenize(source);
62 let mut parser = RbParser::new(tokens, file, &mut str_errors);
63
64 let contract = parser.parse_contract();
65
66 let errors = str_errors
67 .into_iter()
68 .map(|msg| Diagnostic::error(msg, None))
69 .collect();
70
71 ParseResult { contract, errors }
72}
73
74fn snake_to_camel(name: &str) -> String {
80 let stripped = name.trim_start_matches('_');
82 if stripped.is_empty() {
83 return name.to_string();
84 }
85 let mut result = String::new();
86 let mut capitalize_next = false;
87
88 for ch in stripped.chars() {
89 if ch == '_' {
90 capitalize_next = true;
91 } else if capitalize_next {
92 result.push(ch.to_ascii_uppercase());
93 capitalize_next = false;
94 } else {
95 result.push(ch);
96 }
97 }
98
99 result
100}
101
102fn map_builtin_name(name: &str) -> String {
104 match name {
106 "verify_wots" => return "verifyWOTS".to_string(),
107 "verify_slh_dsa_sha2_128s" => return "verifySLHDSA_SHA2_128s".to_string(),
108 "verify_slh_dsa_sha2_128f" => return "verifySLHDSA_SHA2_128f".to_string(),
109 "verify_slh_dsa_sha2_192s" => return "verifySLHDSA_SHA2_192s".to_string(),
110 "verify_slh_dsa_sha2_192f" => return "verifySLHDSA_SHA2_192f".to_string(),
111 "verify_slh_dsa_sha2_256s" => return "verifySLHDSA_SHA2_256s".to_string(),
112 "verify_slh_dsa_sha2_256f" => return "verifySLHDSA_SHA2_256f".to_string(),
113 "verify_rabin_sig" => return "verifyRabinSig".to_string(),
114 "check_sig" => return "checkSig".to_string(),
115 "check_multi_sig" => return "checkMultiSig".to_string(),
116 "check_preimage" => return "checkPreimage".to_string(),
117 "hash160" => return "hash160".to_string(),
118 "hash256" => return "hash256".to_string(),
119 "sha256" => return "sha256".to_string(),
120 "ripemd160" => return "ripemd160".to_string(),
121 "num2bin" => return "num2bin".to_string(),
122 "reverse_bytes" => return "reverseBytes".to_string(),
123 "extract_locktime" => return "extractLocktime".to_string(),
124 "extract_output_hash" => return "extractOutputHash".to_string(),
125 "extract_amount" => return "extractAmount".to_string(),
126 "extract_version" => return "extractVersion".to_string(),
127 "extract_sequence" => return "extractSequence".to_string(),
128 "ec_add" => return "ecAdd".to_string(),
129 "ec_mul" => return "ecMul".to_string(),
130 "ec_mul_gen" => return "ecMulGen".to_string(),
131 "ec_negate" => return "ecNegate".to_string(),
132 "ec_on_curve" => return "ecOnCurve".to_string(),
133 "ec_mod_reduce" => return "ecModReduce".to_string(),
134 "ec_encode_compressed" => return "ecEncodeCompressed".to_string(),
135 "ec_make_point" => return "ecMakePoint".to_string(),
136 "ec_point_x" => return "ecPointX".to_string(),
137 "ec_point_y" => return "ecPointY".to_string(),
138 "mul_div" => return "mulDiv".to_string(),
139 "percent_of" => return "percentOf".to_string(),
140 "safe_div" => return "safediv".to_string(),
141 "safe_mod" => return "safemod".to_string(),
142 "div_mod" => return "divmod".to_string(),
143 "bin2num" => return "bin2num".to_string(),
144 "add_output" => return "addOutput".to_string(),
145 "add_raw_output" => return "addRawOutput".to_string(),
146 "get_state_script" => return "getStateScript".to_string(),
147 "sha256_compress" => return "sha256Compress".to_string(),
149 "sha256_finalize" => return "sha256Finalize".to_string(),
150 "extract_nsequence" => return "extractNSequence".to_string(),
152 "extract_hash_prevouts" => return "extractHashPrevouts".to_string(),
153 "extract_hash_sequence" => return "extractHashSequence".to_string(),
154 "extract_outpoint" => return "extractOutpoint".to_string(),
155 "extract_script_code" => return "extractScriptCode".to_string(),
156 "extract_input_index" => return "extractInputIndex".to_string(),
157 "extract_sig_hash_type" => return "extractSigHashType".to_string(),
158 "extract_outputs" => return "extractOutputs".to_string(),
159 "EC_P" => return "EC_P".to_string(),
161 "EC_N" => return "EC_N".to_string(),
162 "EC_G" => return "EC_G".to_string(),
163 _ => {}
164 }
165
166 match name {
168 "bool" | "abs" | "min" | "max" | "len" | "pow" | "cat" | "within" | "safediv"
169 | "safemod" | "clamp" | "sign" | "sqrt" | "gcd" | "divmod" | "log2" | "substr" => {
170 return name.to_string();
171 }
172 _ => {}
173 }
174
175 snake_to_camel(name)
177}
178
179fn map_rb_type(name: &str) -> &str {
181 match name {
182 "Bigint" | "Integer" | "Int" => "bigint",
183 "Boolean" => "boolean",
184 "ByteString" => "ByteString",
185 "PubKey" => "PubKey",
186 "Sig" => "Sig",
187 "Addr" => "Addr",
188 "Sha256" => "Sha256",
189 "Ripemd160" => "Ripemd160",
190 "SigHashPreimage" => "SigHashPreimage",
191 "RabinSig" => "RabinSig",
192 "RabinPubKey" => "RabinPubKey",
193 "Point" => "Point",
194 _ => name,
195 }
196}
197
198#[derive(Debug, Clone, PartialEq)]
203enum Token {
204 Class,
206 Def,
207 If,
208 Elsif,
209 Else,
210 Unless,
211 For,
212 In,
213 End,
214 Return,
215 TrueLit,
216 FalseLit,
217 NilLit,
218 And,
219 Or,
220 Not,
221 Super,
222 Require,
223 Assert,
224 Do,
225
226 Ident(String),
228 NumberLit(i64),
229 HexStringLit(String), StringLit(String), Symbol(String), Ivar(String), Plus,
236 Minus,
237 Star,
238 Slash,
239 Percent,
240 DoubleStar, EqEq, NotEq, Lt,
244 Le,
245 Gt,
246 Ge,
247 LShift, RShift, AndAnd, OrOr, BitAnd, BitOr, BitXor, Tilde, Bang, Eq, PlusEq, MinusEq, StarEq, SlashEq, PercentEq, DotDotDot, DotDot, Question, ColonColon, LParen,
269 RParen,
270 LBracket,
271 RBracket,
272 Colon,
273 Comma,
274 Dot,
275
276 Newline,
278
279 Eof,
281}
282
283fn tokenize(source: &str) -> Vec<Token> {
284 let mut tokens = Vec::new();
285 let lines: Vec<&str> = source.split('\n').collect();
286 let mut paren_depth: usize = 0;
287
288 for raw_line in &lines {
289 let line = if raw_line.ends_with('\r') {
291 &raw_line[..raw_line.len() - 1]
292 } else {
293 raw_line
294 };
295
296 let stripped = line.trim_start();
298 if stripped.is_empty() || stripped.starts_with('#') {
299 continue;
300 }
301
302 let chars: Vec<char> = line.chars().collect();
303 let start_offset = chars.len() - stripped.len();
304 let mut pos = start_offset;
305
306 while pos < chars.len() {
307 let ch = chars[pos];
308
309 if ch == ' ' || ch == '\t' {
311 pos += 1;
312 continue;
313 }
314
315 if ch == '#' {
317 break; }
319
320 if ch == '@' {
322 pos += 1;
323 let start = pos;
324 while pos < chars.len()
325 && (chars[pos].is_ascii_alphanumeric() || chars[pos] == '_')
326 {
327 pos += 1;
328 }
329 if pos > start {
330 let name: String = chars[start..pos].iter().collect();
331 tokens.push(Token::Ivar(name));
332 } else {
333 tokens.push(Token::Ident("@".to_string()));
335 }
336 continue;
337 }
338
339 if ch == '.'
341 && pos + 2 < chars.len()
342 && chars[pos + 1] == '.'
343 && chars[pos + 2] == '.'
344 {
345 tokens.push(Token::DotDotDot);
346 pos += 3;
347 continue;
348 }
349
350 if ch == '.' && pos + 1 < chars.len() && chars[pos + 1] == '.' {
352 tokens.push(Token::DotDot);
353 pos += 2;
354 continue;
355 }
356
357 if ch == '*' && pos + 1 < chars.len() && chars[pos + 1] == '*' {
359 tokens.push(Token::DoubleStar);
360 pos += 2;
361 continue;
362 }
363 if ch == ':' && pos + 1 < chars.len() && chars[pos + 1] == ':' {
364 tokens.push(Token::ColonColon);
365 pos += 2;
366 continue;
367 }
368 if ch == '=' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
369 tokens.push(Token::EqEq);
370 pos += 2;
371 continue;
372 }
373 if ch == '!' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
374 tokens.push(Token::NotEq);
375 pos += 2;
376 continue;
377 }
378 if ch == '<' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
379 tokens.push(Token::Le);
380 pos += 2;
381 continue;
382 }
383 if ch == '>' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
384 tokens.push(Token::Ge);
385 pos += 2;
386 continue;
387 }
388 if ch == '<' && pos + 1 < chars.len() && chars[pos + 1] == '<' {
389 tokens.push(Token::LShift);
390 pos += 2;
391 continue;
392 }
393 if ch == '>' && pos + 1 < chars.len() && chars[pos + 1] == '>' {
394 tokens.push(Token::RShift);
395 pos += 2;
396 continue;
397 }
398 if ch == '&' && pos + 1 < chars.len() && chars[pos + 1] == '&' {
399 tokens.push(Token::AndAnd);
400 pos += 2;
401 continue;
402 }
403 if ch == '|' && pos + 1 < chars.len() && chars[pos + 1] == '|' {
404 tokens.push(Token::OrOr);
405 pos += 2;
406 continue;
407 }
408 if ch == '+' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
409 tokens.push(Token::PlusEq);
410 pos += 2;
411 continue;
412 }
413 if ch == '-' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
414 tokens.push(Token::MinusEq);
415 pos += 2;
416 continue;
417 }
418 if ch == '*' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
419 tokens.push(Token::StarEq);
420 pos += 2;
421 continue;
422 }
423 if ch == '/' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
424 tokens.push(Token::SlashEq);
425 pos += 2;
426 continue;
427 }
428 if ch == '%' && pos + 1 < chars.len() && chars[pos + 1] == '=' {
429 tokens.push(Token::PercentEq);
430 pos += 2;
431 continue;
432 }
433
434 if ch == '(' {
436 paren_depth += 1;
437 tokens.push(Token::LParen);
438 pos += 1;
439 continue;
440 }
441 if ch == ')' {
442 if paren_depth > 0 {
443 paren_depth -= 1;
444 }
445 tokens.push(Token::RParen);
446 pos += 1;
447 continue;
448 }
449 if ch == '[' {
450 paren_depth += 1;
451 tokens.push(Token::LBracket);
452 pos += 1;
453 continue;
454 }
455 if ch == ']' {
456 if paren_depth > 0 {
457 paren_depth -= 1;
458 }
459 tokens.push(Token::RBracket);
460 pos += 1;
461 continue;
462 }
463
464 if ch == ':'
466 && pos + 1 < chars.len()
467 && (chars[pos + 1].is_ascii_alphabetic() || chars[pos + 1] == '_')
468 {
469 pos += 1; let start = pos;
471 while pos < chars.len()
472 && (chars[pos].is_ascii_alphanumeric() || chars[pos] == '_')
473 {
474 pos += 1;
475 }
476 let name: String = chars[start..pos].iter().collect();
477 tokens.push(Token::Symbol(name));
478 continue;
479 }
480
481 match ch {
483 '+' => {
484 tokens.push(Token::Plus);
485 pos += 1;
486 continue;
487 }
488 '-' => {
489 tokens.push(Token::Minus);
490 pos += 1;
491 continue;
492 }
493 '*' => {
494 tokens.push(Token::Star);
495 pos += 1;
496 continue;
497 }
498 '/' => {
499 tokens.push(Token::Slash);
500 pos += 1;
501 continue;
502 }
503 '%' => {
504 tokens.push(Token::Percent);
505 pos += 1;
506 continue;
507 }
508 '<' => {
509 tokens.push(Token::Lt);
510 pos += 1;
511 continue;
512 }
513 '>' => {
514 tokens.push(Token::Gt);
515 pos += 1;
516 continue;
517 }
518 '!' => {
519 tokens.push(Token::Bang);
520 pos += 1;
521 continue;
522 }
523 '~' => {
524 tokens.push(Token::Tilde);
525 pos += 1;
526 continue;
527 }
528 '&' => {
529 tokens.push(Token::BitAnd);
530 pos += 1;
531 continue;
532 }
533 '|' => {
534 tokens.push(Token::BitOr);
535 pos += 1;
536 continue;
537 }
538 '^' => {
539 tokens.push(Token::BitXor);
540 pos += 1;
541 continue;
542 }
543 '=' => {
544 tokens.push(Token::Eq);
545 pos += 1;
546 continue;
547 }
548 ':' => {
549 tokens.push(Token::Colon);
550 pos += 1;
551 continue;
552 }
553 ',' => {
554 tokens.push(Token::Comma);
555 pos += 1;
556 continue;
557 }
558 '.' => {
559 tokens.push(Token::Dot);
560 pos += 1;
561 continue;
562 }
563 '?' => {
564 tokens.push(Token::Question);
565 pos += 1;
566 continue;
567 }
568 _ => {}
569 }
570
571 if ch == '\'' {
573 pos += 1;
574 let mut val = String::new();
575 while pos < chars.len() && chars[pos] != '\'' {
576 if chars[pos] == '\\' && pos + 1 < chars.len() {
577 pos += 1; val.push(chars[pos]);
579 pos += 1;
580 } else {
581 val.push(chars[pos]);
582 pos += 1;
583 }
584 }
585 if pos < chars.len() {
586 pos += 1; }
588 tokens.push(Token::HexStringLit(val));
589 continue;
590 }
591
592 if ch == '"' {
594 pos += 1;
595 let mut val = String::new();
596 while pos < chars.len() && chars[pos] != '"' {
597 if chars[pos] == '\\' && pos + 1 < chars.len() {
598 pos += 1; val.push(chars[pos]);
600 pos += 1;
601 } else {
602 val.push(chars[pos]);
603 pos += 1;
604 }
605 }
606 if pos < chars.len() {
607 pos += 1; }
609 tokens.push(Token::StringLit(val));
610 continue;
611 }
612
613 if ch.is_ascii_digit() {
615 let mut num_str = String::new();
616 if ch == '0'
617 && pos + 1 < chars.len()
618 && (chars[pos + 1] == 'x' || chars[pos + 1] == 'X')
619 {
620 num_str.push_str("0x");
622 pos += 2;
623 while pos < chars.len()
624 && (chars[pos].is_ascii_hexdigit() || chars[pos] == '_')
625 {
626 if chars[pos] != '_' {
627 num_str.push(chars[pos]);
628 }
629 pos += 1;
630 }
631 } else {
632 while pos < chars.len()
633 && (chars[pos].is_ascii_digit() || chars[pos] == '_')
634 {
635 if chars[pos] != '_' {
636 num_str.push(chars[pos]);
637 }
638 pos += 1;
639 }
640 }
641 let val = if num_str.starts_with("0x") || num_str.starts_with("0X") {
642 i64::from_str_radix(&num_str[2..], 16).unwrap_or(0)
643 } else {
644 num_str.parse::<i64>().unwrap_or(0)
645 };
646 tokens.push(Token::NumberLit(val));
647 continue;
648 }
649
650 if ch.is_ascii_alphabetic() || ch == '_' {
652 let start = pos;
653 while pos < chars.len()
654 && (chars[pos].is_ascii_alphanumeric() || chars[pos] == '_')
655 {
656 pos += 1;
657 }
658 if pos < chars.len() && (chars[pos] == '?' || chars[pos] == '!') {
660 pos += 1;
661 }
662 let word: String = chars[start..pos].iter().collect();
663 let tok = match word.as_str() {
664 "class" => Token::Class,
665 "def" => Token::Def,
666 "if" => Token::If,
667 "elsif" => Token::Elsif,
668 "else" => Token::Else,
669 "unless" => Token::Unless,
670 "for" => Token::For,
671 "in" => Token::In,
672 "end" => Token::End,
673 "return" => Token::Return,
674 "true" => Token::TrueLit,
675 "false" => Token::FalseLit,
676 "nil" => Token::NilLit,
677 "and" => Token::And,
678 "or" => Token::Or,
679 "not" => Token::Not,
680 "super" => Token::Super,
681 "require" => Token::Require,
682 "assert" => Token::Assert,
683 "do" => Token::Do,
684 _ => Token::Ident(word),
685 };
686 tokens.push(tok);
687 continue;
688 }
689
690 pos += 1;
692 }
693
694 if paren_depth == 0 {
696 tokens.push(Token::Newline);
697 }
698 }
699
700 tokens.push(Token::Eof);
701 tokens
702}
703
704struct RbParser<'a> {
709 tokens: Vec<Token>,
710 pos: usize,
711 file: &'a str,
712 errors: &'a mut Vec<String>,
713 declared_locals: std::collections::HashSet<String>,
715}
716
717impl<'a> RbParser<'a> {
718 fn new(tokens: Vec<Token>, file: &'a str, errors: &'a mut Vec<String>) -> Self {
719 Self {
720 tokens,
721 pos: 0,
722 file,
723 errors,
724 declared_locals: std::collections::HashSet::new(),
725 }
726 }
727
728 fn peek(&self) -> &Token {
729 self.tokens.get(self.pos).unwrap_or(&Token::Eof)
730 }
731
732 fn advance(&mut self) -> Token {
733 let t = self.tokens.get(self.pos).cloned().unwrap_or(Token::Eof);
734 self.pos += 1;
735 t
736 }
737
738 fn expect(&mut self, expected: &Token) -> bool {
739 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
740 self.advance();
741 true
742 } else {
743 self.errors.push(format!(
744 "Expected {:?}, got {:?}",
745 expected,
746 self.peek()
747 ));
748 false
749 }
750 }
751
752 fn match_tok(&mut self, expected: &Token) -> bool {
753 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
754 self.advance();
755 true
756 } else {
757 false
758 }
759 }
760
761 fn expect_ident(&mut self) -> String {
762 match self.advance() {
763 Token::Ident(name) => name,
764 other => {
765 self.errors
766 .push(format!("Expected identifier, got {:?}", other));
767 "_error".to_string()
768 }
769 }
770 }
771
772 fn check_ident(&self, name: &str) -> bool {
774 matches!(self.peek(), Token::Ident(ref n) if n == name)
775 }
776
777 fn loc(&self) -> SourceLocation {
778 SourceLocation {
779 file: self.file.to_string(),
780 line: 1,
781 column: 0,
782 }
783 }
784
785 fn skip_newlines(&mut self) {
787 while *self.peek() == Token::Newline {
788 self.advance();
789 }
790 }
791
792 fn parse_contract(&mut self) -> Option<ContractNode> {
797 self.skip_newlines();
798
799 while *self.peek() == Token::Require {
801 self.parse_require_line();
802 self.skip_newlines();
803 }
804
805 self.skip_newlines();
806
807 if *self.peek() != Token::Class {
808 self.errors
809 .push("Expected 'class' declaration".to_string());
810 return None;
811 }
812
813 self.advance(); let contract_name = self.expect_ident();
815
816 self.expect(&Token::Lt);
818
819 let first_part = self.expect_ident();
821 let parent_class = if *self.peek() == Token::ColonColon {
822 self.advance(); self.expect_ident()
824 } else {
825 first_part
826 };
827
828 self.skip_newlines();
829
830 if parent_class != "SmartContract" && parent_class != "StatefulSmartContract" {
831 self.errors.push(format!(
832 "Unknown parent class: {}",
833 parent_class
834 ));
835 return None;
836 }
837
838 let mut properties = Vec::new();
839 let mut methods = Vec::new();
840 let mut constructor: Option<MethodNode> = None;
841
842 let mut pending_visibility: Option<Visibility> = None;
844 let mut pending_param_types: Option<HashMap<String, TypeNode>> = None;
845
846 while *self.peek() != Token::End && *self.peek() != Token::Eof {
847 self.skip_newlines();
848 if *self.peek() == Token::End || *self.peek() == Token::Eof {
849 break;
850 }
851
852 if self.check_ident("prop") {
854 if let Some(prop) = self.parse_prop(&parent_class) {
855 properties.push(prop);
856 }
857 self.skip_newlines();
858 continue;
859 }
860
861 if self.check_ident("runar_public") {
863 self.advance(); pending_visibility = Some(Visibility::Public);
865 pending_param_types = self.parse_optional_param_types();
866 self.skip_newlines();
867 continue;
868 }
869
870 if self.check_ident("params") {
872 self.advance(); pending_param_types = self.parse_optional_param_types();
874 self.skip_newlines();
875 continue;
876 }
877
878 if *self.peek() == Token::Def {
880 let method = self.parse_method_def(
881 &pending_visibility,
882 &pending_param_types,
883 );
884 if method.name == "constructor" {
885 constructor = Some(method);
886 } else {
887 methods.push(method);
888 }
889 pending_visibility = None;
890 pending_param_types = None;
891 self.skip_newlines();
892 continue;
893 }
894
895 self.advance();
897 }
898
899 self.match_tok(&Token::End); let mut constructor =
903 constructor.unwrap_or_else(|| build_constructor(&properties, self.file));
904
905 for param in &mut constructor.params {
909 if matches!(¶m.param_type, TypeNode::Custom(n) if n == "unknown") {
910 if let Some(prop) = properties.iter().find(|p| p.name == param.name) {
911 param.param_type = prop.prop_type.clone();
912 }
913 }
914 }
915
916 let mut method_names: std::collections::HashSet<String> =
920 methods.iter().map(|m| m.name.clone()).collect();
921 method_names.insert("addOutput".to_string());
923 method_names.insert("addRawOutput".to_string());
924 method_names.insert("getStateScript".to_string());
925 for method in &mut methods {
926 rewrite_bare_method_calls(&mut method.body, &method_names);
927 }
928
929 for method in &mut methods {
939 if method.visibility == Visibility::Private && !method.body.is_empty() {
940 let last_idx = method.body.len() - 1;
941 if let Statement::ExpressionStatement { expression, source_location } = &method.body[last_idx] {
942 let ret = Statement::ReturnStatement {
943 value: Some(expression.clone()),
944 source_location: source_location.clone(),
945 };
946 method.body[last_idx] = ret;
947 }
948 }
949 }
950
951 Some(ContractNode {
952 name: contract_name,
953 parent_class,
954 properties,
955 constructor,
956 methods,
957 source_file: self.file.to_string(),
958 })
959 }
960
961 fn parse_require_line(&mut self) {
962 self.advance(); while *self.peek() != Token::Newline && *self.peek() != Token::Eof {
965 self.advance();
966 }
967 self.skip_newlines();
968 }
969
970 fn parse_optional_param_types(&mut self) -> Option<HashMap<String, TypeNode>> {
973 if *self.peek() == Token::Newline
975 || *self.peek() == Token::Eof
976 || *self.peek() == Token::Def
977 {
978 return None;
979 }
980
981 let mut param_types = HashMap::new();
982
983 while *self.peek() != Token::Newline && *self.peek() != Token::Eof {
985 let raw_name = match self.advance() {
987 Token::Ident(name) => name,
988 _ => break,
989 };
990
991 if !self.expect(&Token::Colon) {
993 break;
994 }
995
996 let type_node = self.parse_type();
998
999 param_types.insert(raw_name, type_node);
1000
1001 if !self.match_tok(&Token::Comma) {
1003 break;
1004 }
1005 }
1006
1007 if param_types.is_empty() {
1008 None
1009 } else {
1010 Some(param_types)
1011 }
1012 }
1013
1014 fn parse_prop(&mut self, parent_class: &str) -> Option<PropertyNode> {
1019 self.advance(); let raw_name = match self.peek().clone() {
1023 Token::Symbol(name) => {
1024 self.advance();
1025 name
1026 }
1027 _ => {
1028 self.errors.push(format!(
1029 "Expected symbol after 'prop', got {:?}",
1030 self.peek()
1031 ));
1032 while *self.peek() != Token::Newline && *self.peek() != Token::Eof {
1034 self.advance();
1035 }
1036 return None;
1037 }
1038 };
1039
1040 self.expect(&Token::Comma);
1041
1042 let type_node = self.parse_type();
1044
1045 let mut is_readonly = false;
1048 let mut initializer: Option<Expression> = None;
1049 while *self.peek() == Token::Comma {
1050 self.advance(); if self.check_ident("readonly") {
1052 self.advance(); self.expect(&Token::Colon);
1054 if *self.peek() == Token::TrueLit {
1055 self.advance();
1056 is_readonly = true;
1057 } else if *self.peek() == Token::FalseLit {
1058 self.advance();
1059 is_readonly = false;
1060 }
1061 } else if self.check_ident("default") {
1062 self.advance(); self.expect(&Token::Colon);
1064 initializer = Some(self.parse_unary());
1065 } else {
1066 break;
1068 }
1069 }
1070
1071 if parent_class == "SmartContract" {
1073 is_readonly = true;
1074 }
1075
1076 while *self.peek() != Token::Newline
1078 && *self.peek() != Token::Eof
1079 && *self.peek() != Token::End
1080 {
1081 self.advance();
1082 }
1083
1084 Some(PropertyNode {
1085 name: snake_to_camel(&raw_name),
1086 prop_type: type_node,
1087 readonly: is_readonly,
1088 initializer,
1089 source_location: self.loc(),
1090 })
1091 }
1092
1093 fn parse_type(&mut self) -> TypeNode {
1098 let raw_name = self.expect_ident();
1099
1100 if raw_name == "FixedArray" && *self.peek() == Token::LBracket {
1102 self.advance(); let element = self.parse_type();
1104 self.expect(&Token::Comma);
1105 let length = match self.advance() {
1106 Token::NumberLit(n) => n as usize,
1107 _ => {
1108 self.errors
1109 .push("FixedArray requires numeric length".to_string());
1110 0
1111 }
1112 };
1113 self.expect(&Token::RBracket);
1114 return TypeNode::FixedArray {
1115 element: Box::new(element),
1116 length,
1117 };
1118 }
1119
1120 let mapped = map_rb_type(&raw_name);
1121 if let Some(prim) = PrimitiveTypeName::from_str(mapped) {
1122 TypeNode::Primitive(prim)
1123 } else {
1124 TypeNode::Custom(mapped.to_string())
1125 }
1126 }
1127
1128 fn parse_method_def(
1133 &mut self,
1134 pending_visibility: &Option<Visibility>,
1135 pending_param_types: &Option<HashMap<String, TypeNode>>,
1136 ) -> MethodNode {
1137 self.expect(&Token::Def);
1138
1139 let raw_name = match self.advance() {
1140 Token::Ident(name) => name,
1141 other => {
1142 self.errors
1143 .push(format!("Expected method name, got {:?}", other));
1144 "_error".to_string()
1145 }
1146 };
1147
1148 self.declared_locals.clear();
1150
1151 let params = if *self.peek() == Token::LParen {
1153 self.advance(); let p = self.parse_params(pending_param_types);
1155 self.expect(&Token::RParen);
1156 p
1157 } else {
1158 Vec::new()
1159 };
1160
1161 self.skip_newlines();
1162
1163 let body = self.parse_statements();
1165
1166 self.expect(&Token::End);
1167
1168 if raw_name == "initialize" {
1170 return MethodNode {
1171 name: "constructor".to_string(),
1172 params,
1173 body,
1174 visibility: Visibility::Public,
1175 source_location: self.loc(),
1176 };
1177 }
1178
1179 let is_public = pending_visibility.as_ref() == Some(&Visibility::Public);
1180 let method_name = snake_to_camel(&raw_name);
1181
1182 MethodNode {
1183 name: method_name,
1184 params,
1185 body,
1186 visibility: if is_public {
1187 Visibility::Public
1188 } else {
1189 Visibility::Private
1190 },
1191 source_location: self.loc(),
1192 }
1193 }
1194
1195 fn parse_params(
1196 &mut self,
1197 param_types: &Option<HashMap<String, TypeNode>>,
1198 ) -> Vec<ParamNode> {
1199 let mut params = Vec::new();
1200
1201 while *self.peek() != Token::RParen && *self.peek() != Token::Eof {
1202 let raw_name = self.expect_ident();
1203 let camel_name = snake_to_camel(&raw_name);
1204
1205 let param_type = if let Some(ref types) = param_types {
1207 types
1208 .get(&raw_name)
1209 .cloned()
1210 .unwrap_or_else(|| TypeNode::Custom("unknown".to_string()))
1211 } else {
1212 TypeNode::Custom("unknown".to_string())
1213 };
1214
1215 params.push(ParamNode {
1216 name: camel_name,
1217 param_type,
1218 });
1219
1220 if !self.match_tok(&Token::Comma) {
1221 break;
1222 }
1223 }
1224
1225 params
1226 }
1227
1228 fn parse_statements(&mut self) -> Vec<Statement> {
1233 let mut stmts = Vec::new();
1234
1235 while *self.peek() != Token::End
1236 && *self.peek() != Token::Elsif
1237 && *self.peek() != Token::Else
1238 && *self.peek() != Token::Eof
1239 {
1240 self.skip_newlines();
1241 if *self.peek() == Token::End
1242 || *self.peek() == Token::Elsif
1243 || *self.peek() == Token::Else
1244 || *self.peek() == Token::Eof
1245 {
1246 break;
1247 }
1248
1249 if let Some(stmt) = self.parse_statement() {
1250 stmts.push(stmt);
1251 }
1252 self.skip_newlines();
1253 }
1254
1255 stmts
1256 }
1257
1258 fn parse_statement(&mut self) -> Option<Statement> {
1259 match self.peek().clone() {
1260 Token::Assert => Some(self.parse_assert_statement()),
1262
1263 Token::If => Some(self.parse_if_statement()),
1265
1266 Token::Unless => Some(self.parse_unless_statement()),
1268
1269 Token::For => Some(self.parse_for_statement()),
1271
1272 Token::Return => Some(self.parse_return_statement()),
1274
1275 Token::Super => Some(self.parse_super_call()),
1277
1278 Token::Ivar(_) => Some(self.parse_ivar_statement()),
1280
1281 Token::Ident(_) => Some(self.parse_ident_statement()),
1283
1284 _ => {
1285 self.advance();
1286 None
1287 }
1288 }
1289 }
1290
1291 fn parse_assert_statement(&mut self) -> Statement {
1292 self.advance(); let expr = self.parse_expression();
1294 Statement::ExpressionStatement {
1295 expression: Expression::CallExpr {
1296 callee: Box::new(Expression::Identifier {
1297 name: "assert".to_string(),
1298 }),
1299 args: vec![expr],
1300 },
1301 source_location: self.loc(),
1302 }
1303 }
1304
1305 fn parse_if_statement(&mut self) -> Statement {
1306 self.advance(); let condition = self.parse_expression();
1308 self.match_tok(&Token::Newline);
1310 self.skip_newlines();
1311
1312 let then_branch = self.parse_statements();
1313
1314 let else_branch = if *self.peek() == Token::Elsif {
1315 Some(vec![self.parse_elsif_statement()])
1317 } else if *self.peek() == Token::Else {
1318 self.advance(); self.skip_newlines();
1320 let stmts = self.parse_statements();
1321 Some(stmts)
1322 } else {
1323 None
1324 };
1325
1326 self.expect(&Token::End);
1327
1328 Statement::IfStatement {
1329 condition,
1330 then_branch,
1331 else_branch,
1332 source_location: self.loc(),
1333 }
1334 }
1335
1336 fn parse_elsif_statement(&mut self) -> Statement {
1337 self.advance(); let condition = self.parse_expression();
1339 self.skip_newlines();
1340
1341 let then_branch = self.parse_statements();
1342
1343 let else_branch = if *self.peek() == Token::Elsif {
1344 Some(vec![self.parse_elsif_statement()])
1345 } else if *self.peek() == Token::Else {
1346 self.advance(); self.skip_newlines();
1348 let stmts = self.parse_statements();
1349 Some(stmts)
1350 } else {
1351 None
1352 };
1353
1354 Statement::IfStatement {
1358 condition,
1359 then_branch,
1360 else_branch,
1361 source_location: self.loc(),
1362 }
1363 }
1364
1365 fn parse_unless_statement(&mut self) -> Statement {
1366 self.advance(); let raw_condition = self.parse_expression();
1368 self.skip_newlines();
1369
1370 let body = self.parse_statements();
1371
1372 self.expect(&Token::End);
1373
1374 let condition = Expression::UnaryExpr {
1376 op: UnaryOp::Not,
1377 operand: Box::new(raw_condition),
1378 };
1379
1380 Statement::IfStatement {
1381 condition,
1382 then_branch: body,
1383 else_branch: None,
1384 source_location: self.loc(),
1385 }
1386 }
1387
1388 fn parse_for_statement(&mut self) -> Statement {
1389 self.advance(); let raw_var = self.expect_ident();
1392 let var_name = snake_to_camel(&raw_var);
1393
1394 self.expect(&Token::In);
1395
1396 let start_expr = self.parse_expression();
1398
1399 let is_exclusive = if *self.peek() == Token::DotDotDot {
1401 self.advance();
1402 true
1403 } else if *self.peek() == Token::DotDot {
1404 self.advance();
1405 false
1406 } else {
1407 self.errors
1408 .push("Expected range operator '..' or '...' in for loop".to_string());
1409 true };
1411
1412 let end_expr = self.parse_expression();
1413
1414 self.match_tok(&Token::Do);
1416 self.skip_newlines();
1417
1418 let body = self.parse_statements();
1419 self.expect(&Token::End);
1420
1421 let init = Statement::VariableDecl {
1423 name: var_name.clone(),
1424 var_type: Some(TypeNode::Primitive(PrimitiveTypeName::Bigint)),
1425 mutable: true,
1426 init: start_expr,
1427 source_location: self.loc(),
1428 };
1429
1430 let cmp_op = if is_exclusive {
1431 BinaryOp::Lt
1432 } else {
1433 BinaryOp::Le
1434 };
1435
1436 let condition = Expression::BinaryExpr {
1437 op: cmp_op,
1438 left: Box::new(Expression::Identifier {
1439 name: var_name.clone(),
1440 }),
1441 right: Box::new(end_expr),
1442 };
1443
1444 let update = Statement::ExpressionStatement {
1445 expression: Expression::IncrementExpr {
1446 operand: Box::new(Expression::Identifier { name: var_name }),
1447 prefix: false,
1448 },
1449 source_location: self.loc(),
1450 };
1451
1452 Statement::ForStatement {
1453 init: Box::new(init),
1454 condition,
1455 update: Box::new(update),
1456 body,
1457 source_location: self.loc(),
1458 }
1459 }
1460
1461 fn parse_return_statement(&mut self) -> Statement {
1462 self.advance(); let value = if *self.peek() != Token::Newline
1464 && *self.peek() != Token::End
1465 && *self.peek() != Token::Eof
1466 {
1467 Some(self.parse_expression())
1468 } else {
1469 None
1470 };
1471 Statement::ReturnStatement {
1472 value,
1473 source_location: self.loc(),
1474 }
1475 }
1476
1477 fn parse_super_call(&mut self) -> Statement {
1478 self.advance(); self.expect(&Token::LParen);
1481 let mut args = Vec::new();
1482 while *self.peek() != Token::RParen && *self.peek() != Token::Eof {
1483 args.push(self.parse_expression());
1484 if !self.match_tok(&Token::Comma) {
1485 break;
1486 }
1487 }
1488 self.expect(&Token::RParen);
1489
1490 Statement::ExpressionStatement {
1491 expression: Expression::CallExpr {
1492 callee: Box::new(Expression::Identifier {
1493 name: "super".to_string(),
1494 }),
1495 args,
1496 },
1497 source_location: self.loc(),
1498 }
1499 }
1500
1501 fn parse_ivar_statement(&mut self) -> Statement {
1502 let raw_name = match self.advance() {
1504 Token::Ivar(name) => name,
1505 _ => "_error".to_string(),
1506 };
1507 let prop_name = snake_to_camel(&raw_name);
1508 let target = Expression::PropertyAccess {
1509 property: prop_name,
1510 };
1511
1512 if self.match_tok(&Token::Eq) {
1514 let value = self.parse_expression();
1515 return Statement::Assignment {
1516 target,
1517 value,
1518 source_location: self.loc(),
1519 };
1520 }
1521
1522 let compound_op = match self.peek() {
1524 Token::PlusEq => Some(BinaryOp::Add),
1525 Token::MinusEq => Some(BinaryOp::Sub),
1526 Token::StarEq => Some(BinaryOp::Mul),
1527 Token::SlashEq => Some(BinaryOp::Div),
1528 Token::PercentEq => Some(BinaryOp::Mod),
1529 _ => None,
1530 };
1531
1532 if let Some(bin_op) = compound_op {
1533 self.advance();
1534 let rhs = self.parse_expression();
1535 let value = Expression::BinaryExpr {
1536 op: bin_op,
1537 left: Box::new(target.clone()),
1538 right: Box::new(rhs),
1539 };
1540 return Statement::Assignment {
1541 target,
1542 value,
1543 source_location: self.loc(),
1544 };
1545 }
1546
1547 let expr = self.parse_postfix_from(target);
1549
1550 Statement::ExpressionStatement {
1551 expression: expr,
1552 source_location: self.loc(),
1553 }
1554 }
1555
1556 fn parse_ident_statement(&mut self) -> Statement {
1557 let raw_name = match self.peek().clone() {
1558 Token::Ident(ref name) => name.clone(),
1559 _ => "_error".to_string(),
1560 };
1561
1562 if self
1564 .tokens
1565 .get(self.pos + 1)
1566 .map_or(false, |t| *t == Token::Eq)
1567 {
1568 self.advance(); self.advance(); let value = self.parse_expression();
1571 let camel_name = snake_to_camel(&raw_name);
1572
1573 if self.declared_locals.contains(&camel_name) {
1574 return Statement::Assignment {
1576 target: Expression::Identifier { name: camel_name },
1577 value,
1578 source_location: self.loc(),
1579 };
1580 } else {
1581 self.declared_locals.insert(camel_name.clone());
1583 return Statement::VariableDecl {
1584 name: camel_name,
1585 var_type: None,
1586 mutable: true,
1587 init: value,
1588 source_location: self.loc(),
1589 };
1590 }
1591 }
1592
1593 let expr = self.parse_expression();
1595
1596 if self.match_tok(&Token::Eq) {
1598 let value = self.parse_expression();
1599 return Statement::Assignment {
1600 target: expr,
1601 value,
1602 source_location: self.loc(),
1603 };
1604 }
1605
1606 let compound_op = match self.peek() {
1608 Token::PlusEq => Some(BinaryOp::Add),
1609 Token::MinusEq => Some(BinaryOp::Sub),
1610 Token::StarEq => Some(BinaryOp::Mul),
1611 Token::SlashEq => Some(BinaryOp::Div),
1612 Token::PercentEq => Some(BinaryOp::Mod),
1613 _ => None,
1614 };
1615
1616 if let Some(bin_op) = compound_op {
1617 self.advance();
1618 let rhs = self.parse_expression();
1619 let value = Expression::BinaryExpr {
1620 op: bin_op,
1621 left: Box::new(expr.clone()),
1622 right: Box::new(rhs),
1623 };
1624 return Statement::Assignment {
1625 target: expr,
1626 value,
1627 source_location: self.loc(),
1628 };
1629 }
1630
1631 Statement::ExpressionStatement {
1633 expression: expr,
1634 source_location: self.loc(),
1635 }
1636 }
1637
1638 fn parse_expression(&mut self) -> Expression {
1643 self.parse_ternary()
1644 }
1645
1646 fn parse_ternary(&mut self) -> Expression {
1648 let expr = self.parse_or();
1649
1650 if *self.peek() == Token::Question {
1651 self.advance(); let consequent = self.parse_expression();
1653 self.expect(&Token::Colon);
1654 let alternate = self.parse_expression();
1655 Expression::TernaryExpr {
1656 condition: Box::new(expr),
1657 consequent: Box::new(consequent),
1658 alternate: Box::new(alternate),
1659 }
1660 } else {
1661 expr
1662 }
1663 }
1664
1665 fn parse_or(&mut self) -> Expression {
1666 let mut left = self.parse_and();
1667 while *self.peek() == Token::Or || *self.peek() == Token::OrOr {
1668 self.advance();
1669 let right = self.parse_and();
1670 left = Expression::BinaryExpr {
1671 op: BinaryOp::Or,
1672 left: Box::new(left),
1673 right: Box::new(right),
1674 };
1675 }
1676 left
1677 }
1678
1679 fn parse_and(&mut self) -> Expression {
1680 let mut left = self.parse_not();
1681 while *self.peek() == Token::And || *self.peek() == Token::AndAnd {
1682 self.advance();
1683 let right = self.parse_not();
1684 left = Expression::BinaryExpr {
1685 op: BinaryOp::And,
1686 left: Box::new(left),
1687 right: Box::new(right),
1688 };
1689 }
1690 left
1691 }
1692
1693 fn parse_not(&mut self) -> Expression {
1694 if *self.peek() == Token::Not || *self.peek() == Token::Bang {
1695 self.advance();
1696 let operand = self.parse_not();
1697 Expression::UnaryExpr {
1698 op: UnaryOp::Not,
1699 operand: Box::new(operand),
1700 }
1701 } else {
1702 self.parse_bit_or()
1703 }
1704 }
1705
1706 fn parse_bit_or(&mut self) -> Expression {
1707 let mut left = self.parse_bit_xor();
1708 while *self.peek() == Token::BitOr {
1709 self.advance();
1710 let right = self.parse_bit_xor();
1711 left = Expression::BinaryExpr {
1712 op: BinaryOp::BitOr,
1713 left: Box::new(left),
1714 right: Box::new(right),
1715 };
1716 }
1717 left
1718 }
1719
1720 fn parse_bit_xor(&mut self) -> Expression {
1721 let mut left = self.parse_bit_and();
1722 while *self.peek() == Token::BitXor {
1723 self.advance();
1724 let right = self.parse_bit_and();
1725 left = Expression::BinaryExpr {
1726 op: BinaryOp::BitXor,
1727 left: Box::new(left),
1728 right: Box::new(right),
1729 };
1730 }
1731 left
1732 }
1733
1734 fn parse_bit_and(&mut self) -> Expression {
1735 let mut left = self.parse_equality();
1736 while *self.peek() == Token::BitAnd {
1737 self.advance();
1738 let right = self.parse_equality();
1739 left = Expression::BinaryExpr {
1740 op: BinaryOp::BitAnd,
1741 left: Box::new(left),
1742 right: Box::new(right),
1743 };
1744 }
1745 left
1746 }
1747
1748 fn parse_equality(&mut self) -> Expression {
1749 let mut left = self.parse_comparison();
1750 loop {
1751 match self.peek() {
1752 Token::EqEq => {
1753 self.advance();
1754 let right = self.parse_comparison();
1755 left = Expression::BinaryExpr {
1757 op: BinaryOp::StrictEq,
1758 left: Box::new(left),
1759 right: Box::new(right),
1760 };
1761 }
1762 Token::NotEq => {
1763 self.advance();
1764 let right = self.parse_comparison();
1765 left = Expression::BinaryExpr {
1767 op: BinaryOp::StrictNe,
1768 left: Box::new(left),
1769 right: Box::new(right),
1770 };
1771 }
1772 _ => break,
1773 }
1774 }
1775 left
1776 }
1777
1778 fn parse_comparison(&mut self) -> Expression {
1779 let mut left = self.parse_shift();
1780 loop {
1781 match self.peek() {
1782 Token::Lt => {
1783 self.advance();
1784 let right = self.parse_shift();
1785 left = Expression::BinaryExpr {
1786 op: BinaryOp::Lt,
1787 left: Box::new(left),
1788 right: Box::new(right),
1789 };
1790 }
1791 Token::Le => {
1792 self.advance();
1793 let right = self.parse_shift();
1794 left = Expression::BinaryExpr {
1795 op: BinaryOp::Le,
1796 left: Box::new(left),
1797 right: Box::new(right),
1798 };
1799 }
1800 Token::Gt => {
1801 self.advance();
1802 let right = self.parse_shift();
1803 left = Expression::BinaryExpr {
1804 op: BinaryOp::Gt,
1805 left: Box::new(left),
1806 right: Box::new(right),
1807 };
1808 }
1809 Token::Ge => {
1810 self.advance();
1811 let right = self.parse_shift();
1812 left = Expression::BinaryExpr {
1813 op: BinaryOp::Ge,
1814 left: Box::new(left),
1815 right: Box::new(right),
1816 };
1817 }
1818 _ => break,
1819 }
1820 }
1821 left
1822 }
1823
1824 fn parse_shift(&mut self) -> Expression {
1825 let mut left = self.parse_additive();
1826 loop {
1827 match self.peek() {
1828 Token::LShift => {
1829 self.advance();
1830 let right = self.parse_additive();
1831 left = Expression::BinaryExpr {
1832 op: BinaryOp::Shl,
1833 left: Box::new(left),
1834 right: Box::new(right),
1835 };
1836 }
1837 Token::RShift => {
1838 self.advance();
1839 let right = self.parse_additive();
1840 left = Expression::BinaryExpr {
1841 op: BinaryOp::Shr,
1842 left: Box::new(left),
1843 right: Box::new(right),
1844 };
1845 }
1846 _ => break,
1847 }
1848 }
1849 left
1850 }
1851
1852 fn parse_additive(&mut self) -> Expression {
1853 let mut left = self.parse_multiplicative();
1854 loop {
1855 match self.peek() {
1856 Token::Plus => {
1857 self.advance();
1858 let right = self.parse_multiplicative();
1859 left = Expression::BinaryExpr {
1860 op: BinaryOp::Add,
1861 left: Box::new(left),
1862 right: Box::new(right),
1863 };
1864 }
1865 Token::Minus => {
1866 self.advance();
1867 let right = self.parse_multiplicative();
1868 left = Expression::BinaryExpr {
1869 op: BinaryOp::Sub,
1870 left: Box::new(left),
1871 right: Box::new(right),
1872 };
1873 }
1874 _ => break,
1875 }
1876 }
1877 left
1878 }
1879
1880 fn parse_multiplicative(&mut self) -> Expression {
1881 let mut left = self.parse_unary();
1882 loop {
1883 match self.peek() {
1884 Token::Star => {
1885 self.advance();
1886 let right = self.parse_unary();
1887 left = Expression::BinaryExpr {
1888 op: BinaryOp::Mul,
1889 left: Box::new(left),
1890 right: Box::new(right),
1891 };
1892 }
1893 Token::Slash => {
1894 self.advance();
1895 let right = self.parse_unary();
1896 left = Expression::BinaryExpr {
1897 op: BinaryOp::Div,
1898 left: Box::new(left),
1899 right: Box::new(right),
1900 };
1901 }
1902 Token::Percent => {
1903 self.advance();
1904 let right = self.parse_unary();
1905 left = Expression::BinaryExpr {
1906 op: BinaryOp::Mod,
1907 left: Box::new(left),
1908 right: Box::new(right),
1909 };
1910 }
1911 _ => break,
1912 }
1913 }
1914 left
1915 }
1916
1917 fn parse_unary(&mut self) -> Expression {
1918 match self.peek() {
1919 Token::Minus => {
1920 self.advance();
1921 let operand = self.parse_unary();
1922 Expression::UnaryExpr {
1923 op: UnaryOp::Neg,
1924 operand: Box::new(operand),
1925 }
1926 }
1927 Token::Tilde => {
1928 self.advance();
1929 let operand = self.parse_unary();
1930 Expression::UnaryExpr {
1931 op: UnaryOp::BitNot,
1932 operand: Box::new(operand),
1933 }
1934 }
1935 Token::Bang => {
1936 self.advance();
1937 let operand = self.parse_unary();
1938 Expression::UnaryExpr {
1939 op: UnaryOp::Not,
1940 operand: Box::new(operand),
1941 }
1942 }
1943 _ => self.parse_power(),
1944 }
1945 }
1946
1947 fn parse_power(&mut self) -> Expression {
1949 let base = self.parse_postfix();
1950
1951 if *self.peek() == Token::DoubleStar {
1952 self.advance();
1953 let exp = self.parse_power(); Expression::CallExpr {
1955 callee: Box::new(Expression::Identifier {
1956 name: "pow".to_string(),
1957 }),
1958 args: vec![base, exp],
1959 }
1960 } else {
1961 base
1962 }
1963 }
1964
1965 fn parse_postfix(&mut self) -> Expression {
1966 let expr = self.parse_primary();
1967 self.parse_postfix_from(expr)
1968 }
1969
1970 fn parse_postfix_from(&mut self, mut expr: Expression) -> Expression {
1972 loop {
1973 match self.peek().clone() {
1974 Token::Dot => {
1975 self.advance(); let raw_prop = self.expect_ident();
1977 let prop = map_builtin_name(&raw_prop);
1978
1979 if *self.peek() == Token::LParen {
1981 let args = self.parse_call_args();
1982 if matches!(&expr, Expression::Identifier { name } if name == "this") {
1984 expr = Expression::CallExpr {
1985 callee: Box::new(Expression::MemberExpr {
1986 object: Box::new(Expression::Identifier {
1987 name: "this".to_string(),
1988 }),
1989 property: prop,
1990 }),
1991 args,
1992 };
1993 } else {
1994 expr = Expression::CallExpr {
1995 callee: Box::new(Expression::MemberExpr {
1996 object: Box::new(expr),
1997 property: prop,
1998 }),
1999 args,
2000 };
2001 }
2002 } else {
2003 if matches!(&expr, Expression::Identifier { name } if name == "this") {
2005 expr = Expression::PropertyAccess { property: prop };
2006 } else {
2007 expr = Expression::MemberExpr {
2008 object: Box::new(expr),
2009 property: prop,
2010 };
2011 }
2012 }
2013 }
2014 Token::LParen => {
2015 let args = self.parse_call_args();
2016 expr = Expression::CallExpr {
2017 callee: Box::new(expr),
2018 args,
2019 };
2020 }
2021 Token::LBracket => {
2022 self.advance();
2023 let index = self.parse_expression();
2024 self.expect(&Token::RBracket);
2025 expr = Expression::IndexAccess {
2026 object: Box::new(expr),
2027 index: Box::new(index),
2028 };
2029 }
2030 _ => break,
2031 }
2032 }
2033
2034 expr
2035 }
2036
2037 fn parse_primary(&mut self) -> Expression {
2038 match self.advance() {
2039 Token::NumberLit(v) => Expression::BigIntLiteral { value: v },
2040 Token::TrueLit => Expression::BoolLiteral { value: true },
2041 Token::FalseLit => Expression::BoolLiteral { value: false },
2042 Token::NilLit => Expression::BigIntLiteral { value: 0 },
2043 Token::HexStringLit(v) => Expression::ByteStringLiteral { value: v },
2044 Token::StringLit(v) => Expression::ByteStringLiteral { value: v },
2045 Token::Ivar(name) => {
2046 let prop_name = snake_to_camel(&name);
2048 Expression::PropertyAccess {
2049 property: prop_name,
2050 }
2051 }
2052 Token::Ident(name) => {
2053 let mapped = map_builtin_name(&name);
2054 Expression::Identifier { name: mapped }
2055 }
2056 Token::Assert => {
2057 Expression::Identifier {
2059 name: "assert".to_string(),
2060 }
2061 }
2062 Token::Super => {
2063 Expression::Identifier {
2064 name: "super".to_string(),
2065 }
2066 }
2067 Token::LParen => {
2068 let expr = self.parse_expression();
2069 self.expect(&Token::RParen);
2070 expr
2071 }
2072 Token::LBracket => {
2073 let mut elements = Vec::new();
2075 while *self.peek() != Token::RBracket && *self.peek() != Token::Eof {
2076 elements.push(self.parse_expression());
2077 if !self.match_tok(&Token::Comma) {
2078 break;
2079 }
2080 }
2081 self.expect(&Token::RBracket);
2082 Expression::ArrayLiteral { elements }
2083 }
2084 other => {
2085 self.errors
2086 .push(format!("Unexpected token in expression: {:?}", other));
2087 Expression::BigIntLiteral { value: 0 }
2088 }
2089 }
2090 }
2091
2092 fn parse_call_args(&mut self) -> Vec<Expression> {
2093 self.expect(&Token::LParen);
2094 let mut args = Vec::new();
2095 while *self.peek() != Token::RParen && *self.peek() != Token::Eof {
2096 args.push(self.parse_expression());
2097 if !self.match_tok(&Token::Comma) {
2098 break;
2099 }
2100 }
2101 self.expect(&Token::RParen);
2102 args
2103 }
2104}
2105
2106fn build_constructor(properties: &[PropertyNode], file: &str) -> MethodNode {
2111 let required_props: Vec<&PropertyNode> =
2113 properties.iter().filter(|p| p.initializer.is_none()).collect();
2114
2115 let params: Vec<ParamNode> = required_props
2116 .iter()
2117 .map(|p| ParamNode {
2118 name: p.name.clone(),
2119 param_type: p.prop_type.clone(),
2120 })
2121 .collect();
2122
2123 let mut body: Vec<Statement> = Vec::new();
2124
2125 let super_args: Vec<Expression> = required_props
2127 .iter()
2128 .map(|p| Expression::Identifier {
2129 name: p.name.clone(),
2130 })
2131 .collect();
2132 body.push(Statement::ExpressionStatement {
2133 expression: Expression::CallExpr {
2134 callee: Box::new(Expression::Identifier {
2135 name: "super".to_string(),
2136 }),
2137 args: super_args,
2138 },
2139 source_location: SourceLocation {
2140 file: file.to_string(),
2141 line: 1,
2142 column: 0,
2143 },
2144 });
2145
2146 for p in &required_props {
2148 body.push(Statement::Assignment {
2149 target: Expression::PropertyAccess {
2150 property: p.name.clone(),
2151 },
2152 value: Expression::Identifier {
2153 name: p.name.clone(),
2154 },
2155 source_location: SourceLocation {
2156 file: file.to_string(),
2157 line: 1,
2158 column: 0,
2159 },
2160 });
2161 }
2162
2163 MethodNode {
2164 name: "constructor".to_string(),
2165 params,
2166 body,
2167 visibility: Visibility::Public,
2168 source_location: SourceLocation {
2169 file: file.to_string(),
2170 line: 1,
2171 column: 0,
2172 },
2173 }
2174}
2175
2176fn rewrite_bare_method_calls(stmts: &mut [Statement], method_names: &std::collections::HashSet<String>) {
2183 for stmt in stmts.iter_mut() {
2184 rewrite_stmt(stmt, method_names);
2185 }
2186}
2187
2188fn rewrite_expr(expr: &mut Expression, method_names: &std::collections::HashSet<String>) {
2189 match expr {
2190 Expression::CallExpr { callee, args } => {
2191 for arg in args.iter_mut() {
2192 rewrite_expr(arg, method_names);
2193 }
2194 if let Expression::Identifier { name } = callee.as_ref() {
2195 if method_names.contains(name) {
2196 *callee = Box::new(Expression::PropertyAccess { property: name.clone() });
2197 }
2198 } else {
2199 rewrite_expr(callee.as_mut(), method_names);
2200 }
2201 }
2202 Expression::BinaryExpr { left, right, .. } => {
2203 rewrite_expr(left.as_mut(), method_names);
2204 rewrite_expr(right.as_mut(), method_names);
2205 }
2206 Expression::UnaryExpr { operand, .. } => {
2207 rewrite_expr(operand.as_mut(), method_names);
2208 }
2209 Expression::TernaryExpr { condition, consequent, alternate } => {
2210 rewrite_expr(condition.as_mut(), method_names);
2211 rewrite_expr(consequent.as_mut(), method_names);
2212 rewrite_expr(alternate.as_mut(), method_names);
2213 }
2214 _ => {}
2215 }
2216}
2217
2218fn rewrite_stmt(stmt: &mut Statement, method_names: &std::collections::HashSet<String>) {
2219 match stmt {
2220 Statement::ExpressionStatement { expression, .. } => {
2221 rewrite_expr(expression, method_names);
2222 }
2223 Statement::VariableDecl { init, .. } => {
2224 rewrite_expr(init, method_names);
2225 }
2226 Statement::Assignment { value, .. } => {
2227 rewrite_expr(value, method_names);
2228 }
2229 Statement::ReturnStatement { value, .. } => {
2230 if let Some(v) = value {
2231 rewrite_expr(v, method_names);
2232 }
2233 }
2234 Statement::IfStatement { condition, then_branch, else_branch, .. } => {
2235 rewrite_expr(condition, method_names);
2236 rewrite_bare_method_calls(then_branch, method_names);
2237 if let Some(else_stmts) = else_branch {
2238 rewrite_bare_method_calls(else_stmts, method_names);
2239 }
2240 }
2241 Statement::ForStatement { body, .. } => {
2242 rewrite_bare_method_calls(body, method_names);
2243 }
2244 }
2245}
2246
2247#[cfg(test)]
2252mod tests {
2253 use super::*;
2254
2255 #[test]
2256 fn test_snake_to_camel() {
2257 assert_eq!(snake_to_camel("hello_world"), "helloWorld");
2258 assert_eq!(snake_to_camel("check_sig"), "checkSig");
2259 assert_eq!(snake_to_camel("already"), "already");
2260 assert_eq!(snake_to_camel("a_b_c"), "aBC");
2261 assert_eq!(snake_to_camel("pub_key_hash"), "pubKeyHash");
2262 }
2263
2264 #[test]
2265 fn test_builtin_name_mapping() {
2266 assert_eq!(map_builtin_name("check_sig"), "checkSig");
2267 assert_eq!(map_builtin_name("hash160"), "hash160");
2268 assert_eq!(map_builtin_name("verify_wots"), "verifyWOTS");
2269 assert_eq!(
2270 map_builtin_name("verify_slh_dsa_sha2_128s"),
2271 "verifySLHDSA_SHA2_128s"
2272 );
2273 assert_eq!(map_builtin_name("ec_add"), "ecAdd");
2274 assert_eq!(map_builtin_name("add_output"), "addOutput");
2275 assert_eq!(map_builtin_name("abs"), "abs");
2276 assert_eq!(map_builtin_name("some_func"), "someFunc");
2277 }
2278
2279 #[test]
2280 fn test_parse_simple_ruby_contract() {
2281 let source = r#"
2282require 'runar'
2283
2284class P2PKH < Runar::SmartContract
2285 prop :pub_key_hash, Addr
2286
2287 def initialize(pub_key_hash)
2288 super(pub_key_hash)
2289 @pub_key_hash = pub_key_hash
2290 end
2291
2292 runar_public sig: Sig, pub_key: PubKey
2293 def unlock(sig, pub_key)
2294 assert hash160(pub_key) == @pub_key_hash
2295 assert check_sig(sig, pub_key)
2296 end
2297end
2298"#;
2299
2300 let result = parse_ruby(source, Some("P2PKH.runar.rb"));
2301 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2302 let contract = result.contract.unwrap();
2303 assert_eq!(contract.name, "P2PKH");
2304 assert_eq!(contract.parent_class, "SmartContract");
2305 assert_eq!(contract.properties.len(), 1);
2306 assert_eq!(contract.properties[0].name, "pubKeyHash");
2307 assert!(contract.properties[0].readonly);
2308 assert_eq!(contract.methods.len(), 1);
2309 assert_eq!(contract.methods[0].name, "unlock");
2310 assert_eq!(contract.methods[0].visibility, Visibility::Public);
2311 assert_eq!(contract.methods[0].params.len(), 2);
2312 assert_eq!(contract.methods[0].params[0].name, "sig");
2313 assert_eq!(contract.methods[0].params[1].name, "pubKey");
2314 }
2315
2316 #[test]
2317 fn test_parse_stateful_ruby_contract() {
2318 let source = r#"
2319require 'runar'
2320
2321class Counter < Runar::StatefulSmartContract
2322 prop :count, Bigint
2323
2324 def initialize(count)
2325 super(count)
2326 @count = count
2327 end
2328
2329 runar_public
2330 def increment
2331 @count += 1
2332 end
2333
2334 runar_public
2335 def decrement
2336 assert @count > 0
2337 @count -= 1
2338 end
2339end
2340"#;
2341
2342 let result = parse_ruby(source, Some("Counter.runar.rb"));
2343 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2344 let contract = result.contract.unwrap();
2345 assert_eq!(contract.name, "Counter");
2346 assert_eq!(contract.parent_class, "StatefulSmartContract");
2347 assert_eq!(contract.properties.len(), 1);
2348 assert_eq!(contract.properties[0].name, "count");
2349 assert!(!contract.properties[0].readonly);
2350 assert_eq!(contract.methods.len(), 2);
2351 assert_eq!(contract.methods[0].name, "increment");
2352 assert_eq!(contract.methods[0].visibility, Visibility::Public);
2353 assert_eq!(contract.methods[1].name, "decrement");
2354 assert_eq!(contract.methods[1].visibility, Visibility::Public);
2355 }
2356
2357 #[test]
2358 fn test_parse_readonly_property() {
2359 let source = r#"
2360require 'runar'
2361
2362class Token < Runar::StatefulSmartContract
2363 prop :owner, PubKey
2364 prop :balance, Bigint
2365 prop :token_id, ByteString, readonly: true
2366
2367 def initialize(owner, balance, token_id)
2368 super(owner, balance, token_id)
2369 @owner = owner
2370 @balance = balance
2371 @token_id = token_id
2372 end
2373
2374 runar_public sig: Sig, to: PubKey, amount: Bigint, output_satoshis: Bigint
2375 def transfer(sig, to, amount, output_satoshis)
2376 assert check_sig(sig, @owner)
2377 assert amount > 0
2378 assert amount <= @balance
2379 add_output(output_satoshis, to, amount)
2380 add_output(output_satoshis, @owner, @balance - amount)
2381 end
2382end
2383"#;
2384
2385 let result = parse_ruby(source, Some("Token.runar.rb"));
2386 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2387 let contract = result.contract.unwrap();
2388 assert_eq!(contract.name, "Token");
2389 assert_eq!(contract.properties.len(), 3);
2390 assert!(!contract.properties[0].readonly); assert!(!contract.properties[1].readonly); assert!(contract.properties[2].readonly); assert_eq!(contract.methods.len(), 1);
2394 assert_eq!(contract.methods[0].name, "transfer");
2395 assert_eq!(contract.methods[0].params.len(), 4);
2396 }
2397
2398 #[test]
2399 fn test_parse_for_loop_exclusive() {
2400 let source = r#"
2401require 'runar'
2402
2403class BoundedLoop < Runar::SmartContract
2404 prop :expected_sum, Bigint
2405
2406 runar_public n: Bigint
2407 def verify(n)
2408 total = 0
2409 for i in 0...n do
2410 total += i
2411 end
2412 assert total == @expected_sum
2413 end
2414end
2415"#;
2416
2417 let result = parse_ruby(source, Some("BoundedLoop.runar.rb"));
2418 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2419 let contract = result.contract.unwrap();
2420 assert_eq!(contract.methods.len(), 1);
2421 let body = &contract.methods[0].body;
2423 assert!(body.len() >= 3); }
2425
2426 #[test]
2427 fn test_parse_for_loop_inclusive() {
2428 let source = r#"
2429require 'runar'
2430
2431class InclusiveLoop < Runar::SmartContract
2432 prop :expected_sum, Bigint
2433
2434 runar_public n: Bigint
2435 def verify(n)
2436 total = 0
2437 for i in 0..n
2438 total += i
2439 end
2440 assert total == @expected_sum
2441 end
2442end
2443"#;
2444
2445 let result = parse_ruby(source, Some("InclusiveLoop.runar.rb"));
2446 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2447 let contract = result.contract.unwrap();
2448 assert_eq!(contract.methods.len(), 1);
2449 }
2450
2451 #[test]
2452 fn test_parse_if_elsif_else() {
2453 let source = r#"
2454require 'runar'
2455
2456class Branching < Runar::SmartContract
2457 prop :value, Bigint
2458
2459 runar_public x: Bigint
2460 def check(x)
2461 if x > 10
2462 assert @value == 1
2463 elsif x > 5
2464 assert @value == 2
2465 else
2466 assert @value == 3
2467 end
2468 end
2469end
2470"#;
2471
2472 let result = parse_ruby(source, Some("Branching.runar.rb"));
2473 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2474 let contract = result.contract.unwrap();
2475 assert_eq!(contract.methods.len(), 1);
2476 }
2477
2478 #[test]
2479 fn test_parse_unless_statement() {
2480 let source = r#"
2481require 'runar'
2482
2483class Checker < Runar::SmartContract
2484 prop :value, Bigint
2485
2486 runar_public
2487 def check
2488 unless @value == 0
2489 assert @value > 0
2490 end
2491 end
2492end
2493"#;
2494
2495 let result = parse_ruby(source, Some("Checker.runar.rb"));
2496 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2497 let contract = result.contract.unwrap();
2498 assert_eq!(contract.methods.len(), 1);
2499 }
2500
2501 #[test]
2502 fn test_parse_power_operator() {
2503 let source = r#"
2504require 'runar'
2505
2506class Power < Runar::SmartContract
2507 prop :expected, Bigint
2508
2509 runar_public x: Bigint, y: Bigint
2510 def verify(x, y)
2511 assert x ** y == @expected
2512 end
2513end
2514"#;
2515
2516 let result = parse_ruby(source, Some("Power.runar.rb"));
2517 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2518 }
2519
2520 #[test]
2521 fn test_parse_auto_generated_constructor() {
2522 let source = r#"
2523require 'runar'
2524
2525class Simple < Runar::SmartContract
2526 prop :value, Bigint
2527
2528 runar_public x: Bigint
2529 def verify(x)
2530 assert x == @value
2531 end
2532end
2533"#;
2534
2535 let result = parse_ruby(source, Some("Simple.runar.rb"));
2536 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2537 let contract = result.contract.unwrap();
2538 assert_eq!(contract.constructor.name, "constructor");
2540 assert_eq!(contract.constructor.params.len(), 1);
2541 assert_eq!(contract.constructor.params[0].name, "value");
2542 }
2543
2544 #[test]
2545 fn test_parse_parent_class_without_runar_prefix() {
2546 let source = r#"
2547require 'runar'
2548
2549class Simple < SmartContract
2550 prop :value, Bigint
2551
2552 runar_public x: Bigint
2553 def verify(x)
2554 assert x == @value
2555 end
2556end
2557"#;
2558
2559 let result = parse_ruby(source, Some("Simple.runar.rb"));
2560 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2561 let contract = result.contract.unwrap();
2562 assert_eq!(contract.parent_class, "SmartContract");
2563 }
2564
2565 #[test]
2566 fn test_parse_hex_string_literal() {
2567 let source = r#"
2568require 'runar'
2569
2570class HexTest < Runar::SmartContract
2571 prop :data, ByteString
2572
2573 runar_public
2574 def verify
2575 assert @data == 'deadbeef'
2576 end
2577end
2578"#;
2579
2580 let result = parse_ruby(source, Some("HexTest.runar.rb"));
2581 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2582 }
2583
2584 #[test]
2585 fn test_parse_private_method_with_params() {
2586 let source = r#"
2587require 'runar'
2588
2589class WithHelper < Runar::SmartContract
2590 prop :value, Bigint
2591
2592 params x: Bigint
2593 def helper(x)
2594 return x + 1
2595 end
2596
2597 runar_public n: Bigint
2598 def verify(n)
2599 result = helper(n)
2600 assert result == @value
2601 end
2602end
2603"#;
2604
2605 let result = parse_ruby(source, Some("WithHelper.runar.rb"));
2606 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
2607 let contract = result.contract.unwrap();
2608 assert_eq!(contract.methods.len(), 2);
2609 assert_eq!(contract.methods[0].name, "helper");
2610 assert_eq!(contract.methods[0].visibility, Visibility::Private);
2611 assert_eq!(contract.methods[1].name, "verify");
2612 assert_eq!(contract.methods[1].visibility, Visibility::Public);
2613 }
2614}