Skip to main content

runar_compiler_rust/frontend/
parser_sol.rs

1//! Solidity-like parser for Rúnar contracts.
2//!
3//! Parses a Solidity-style syntax into the same AST as the TypeScript parser.
4//! Hand-written tokenizer + recursive descent parser.
5//!
6//! ## Expected format
7//!
8//! ```solidity
9//! // SPDX-License-Identifier: MIT
10//! contract P2PKH is SmartContract {
11//!     immutable Addr pubKeyHash;
12//!
13//!     constructor(Addr pubKeyHash) {
14//!         super(pubKeyHash);
15//!         this.pubKeyHash = pubKeyHash;
16//!     }
17//!
18//!     function unlock(Sig sig, PubKey pubKey) public {
19//!         require(hash160(pubKey) == this.pubKeyHash);
20//!         require(checkSig(sig, pubKey));
21//!     }
22//! }
23//! ```
24//!
25//! Key mappings:
26//! - `require(x)` -> `assert(x)`
27//! - `immutable` -> readonly
28//! - `==` -> StrictEq (===)
29//! - `!=` -> StrictNe (!==)
30//! - Types before names (Solidity convention)
31
32use super::ast::{
33    BinaryOp, ContractNode, Expression, MethodNode, ParamNode, PrimitiveTypeName, PropertyNode,
34    SourceLocation, Statement, TypeNode, UnaryOp, Visibility,
35};
36use super::diagnostic::Diagnostic;
37use super::parser::ParseResult;
38
39// ---------------------------------------------------------------------------
40// Public API
41// ---------------------------------------------------------------------------
42
43/// Parse a Solidity-format Rúnar contract source.
44pub fn parse_solidity(source: &str, file_name: Option<&str>) -> ParseResult {
45    let file = file_name.unwrap_or("contract.runar.sol");
46    let mut errors: Vec<Diagnostic> = Vec::new();
47
48    let tokens = tokenize(source);
49    let mut parser = SolParser::new(tokens, file, &mut errors);
50
51    let contract = parser.parse_contract();
52
53    ParseResult {
54        contract,
55        errors,
56    }
57}
58
59// ---------------------------------------------------------------------------
60// Tokenizer
61// ---------------------------------------------------------------------------
62
63#[derive(Debug, Clone, PartialEq)]
64enum Token {
65    // Keywords
66    Contract,
67    Is,
68    Immutable,
69    Constructor,
70    Function,
71    Public,
72    Private,
73    If,
74    Else,
75    For,
76    Return,
77    Require,
78    Let,
79    Const,
80    True,
81    False,
82
83    // Identifiers and literals
84    Ident(String),
85    NumberLit(i64),
86    StringLit(String),
87
88    // Operators
89    Plus,
90    Minus,
91    Star,
92    Slash,
93    Percent,
94    EqEq,       // == (maps to ===)
95    NotEq,      // != (maps to !==)
96    EqEqEq,     // ===
97    NotEqEq,    // !==
98    Lt,
99    Le,
100    Gt,
101    Ge,
102    And,        // &&
103    Or,         // ||
104    BitAnd,     // &
105    BitOr,      // |
106    BitXor,     // ^
107    Not,        // !
108    Tilde,      // ~
109    Eq,         // =
110    PlusEq,     // +=
111    MinusEq,    // -=
112    StarEq,     // *=
113    SlashEq,    // /=
114    PercentEq,  // %=
115    PlusPlus,   // ++
116    MinusMinus, // --
117
118    // Delimiters
119    LParen,
120    RParen,
121    LBrace,
122    RBrace,
123    LBracket,
124    RBracket,
125    Semicolon,
126    Comma,
127    Dot,
128    Colon,
129    Question,
130
131    // Special
132    Eof,
133}
134
135fn tokenize(source: &str) -> Vec<Token> {
136    let mut tokens = Vec::new();
137    let chars: Vec<char> = source.chars().collect();
138    let len = chars.len();
139    let mut i = 0;
140
141    while i < len {
142        let ch = chars[i];
143
144        // Whitespace
145        if ch.is_whitespace() {
146            i += 1;
147            continue;
148        }
149
150        // Line comments
151        if ch == '/' && i + 1 < len && chars[i + 1] == '/' {
152            while i < len && chars[i] != '\n' {
153                i += 1;
154            }
155            continue;
156        }
157
158        // Block comments
159        if ch == '/' && i + 1 < len && chars[i + 1] == '*' {
160            i += 2;
161            while i + 1 < len && !(chars[i] == '*' && chars[i + 1] == '/') {
162                i += 1;
163            }
164            if i + 1 < len {
165                i += 2;
166            }
167            continue;
168        }
169
170        // Multi-character operators (order matters: longest match first)
171        if ch == '=' && i + 2 < len && chars[i + 1] == '=' && chars[i + 2] == '=' {
172            tokens.push(Token::EqEqEq);
173            i += 3;
174            continue;
175        }
176        if ch == '!' && i + 2 < len && chars[i + 1] == '=' && chars[i + 2] == '=' {
177            tokens.push(Token::NotEqEq);
178            i += 3;
179            continue;
180        }
181        if ch == '=' && i + 1 < len && chars[i + 1] == '=' {
182            tokens.push(Token::EqEq);
183            i += 2;
184            continue;
185        }
186        if ch == '!' && i + 1 < len && chars[i + 1] == '=' {
187            tokens.push(Token::NotEq);
188            i += 2;
189            continue;
190        }
191        if ch == '<' && i + 1 < len && chars[i + 1] == '=' {
192            tokens.push(Token::Le);
193            i += 2;
194            continue;
195        }
196        if ch == '>' && i + 1 < len && chars[i + 1] == '=' {
197            tokens.push(Token::Ge);
198            i += 2;
199            continue;
200        }
201        if ch == '&' && i + 1 < len && chars[i + 1] == '&' {
202            tokens.push(Token::And);
203            i += 2;
204            continue;
205        }
206        if ch == '|' && i + 1 < len && chars[i + 1] == '|' {
207            tokens.push(Token::Or);
208            i += 2;
209            continue;
210        }
211        if ch == '+' && i + 1 < len && chars[i + 1] == '+' {
212            tokens.push(Token::PlusPlus);
213            i += 2;
214            continue;
215        }
216        if ch == '-' && i + 1 < len && chars[i + 1] == '-' {
217            tokens.push(Token::MinusMinus);
218            i += 2;
219            continue;
220        }
221        if ch == '+' && i + 1 < len && chars[i + 1] == '=' {
222            tokens.push(Token::PlusEq);
223            i += 2;
224            continue;
225        }
226        if ch == '-' && i + 1 < len && chars[i + 1] == '=' {
227            tokens.push(Token::MinusEq);
228            i += 2;
229            continue;
230        }
231        if ch == '*' && i + 1 < len && chars[i + 1] == '=' {
232            tokens.push(Token::StarEq);
233            i += 2;
234            continue;
235        }
236        if ch == '/' && i + 1 < len && chars[i + 1] == '=' {
237            tokens.push(Token::SlashEq);
238            i += 2;
239            continue;
240        }
241        if ch == '%' && i + 1 < len && chars[i + 1] == '=' {
242            tokens.push(Token::PercentEq);
243            i += 2;
244            continue;
245        }
246
247        // Single-character tokens
248        match ch {
249            '+' => { tokens.push(Token::Plus); i += 1; continue; }
250            '-' => { tokens.push(Token::Minus); i += 1; continue; }
251            '*' => { tokens.push(Token::Star); i += 1; continue; }
252            '/' => { tokens.push(Token::Slash); i += 1; continue; }
253            '%' => { tokens.push(Token::Percent); i += 1; continue; }
254            '<' => { tokens.push(Token::Lt); i += 1; continue; }
255            '>' => { tokens.push(Token::Gt); i += 1; continue; }
256            '!' => { tokens.push(Token::Not); i += 1; continue; }
257            '~' => { tokens.push(Token::Tilde); i += 1; continue; }
258            '&' => { tokens.push(Token::BitAnd); i += 1; continue; }
259            '|' => { tokens.push(Token::BitOr); i += 1; continue; }
260            '^' => { tokens.push(Token::BitXor); i += 1; continue; }
261            '=' => { tokens.push(Token::Eq); i += 1; continue; }
262            '(' => { tokens.push(Token::LParen); i += 1; continue; }
263            ')' => { tokens.push(Token::RParen); i += 1; continue; }
264            '{' => { tokens.push(Token::LBrace); i += 1; continue; }
265            '}' => { tokens.push(Token::RBrace); i += 1; continue; }
266            '[' => { tokens.push(Token::LBracket); i += 1; continue; }
267            ']' => { tokens.push(Token::RBracket); i += 1; continue; }
268            ';' => { tokens.push(Token::Semicolon); i += 1; continue; }
269            ',' => { tokens.push(Token::Comma); i += 1; continue; }
270            '.' => { tokens.push(Token::Dot); i += 1; continue; }
271            ':' => { tokens.push(Token::Colon); i += 1; continue; }
272            '?' => { tokens.push(Token::Question); i += 1; continue; }
273            _ => {}
274        }
275
276        // String literals
277        if ch == '\'' || ch == '"' {
278            let quote = ch;
279            i += 1;
280            let start = i;
281            while i < len && chars[i] != quote {
282                if chars[i] == '\\' {
283                    i += 1; // skip escaped char
284                }
285                i += 1;
286            }
287            let val: String = chars[start..i].iter().collect();
288            tokens.push(Token::StringLit(val));
289            if i < len {
290                i += 1;
291            }
292            continue;
293        }
294
295        // Numbers
296        if ch.is_ascii_digit() {
297            let start = i;
298            while i < len && (chars[i].is_ascii_digit() || chars[i] == 'n') {
299                i += 1;
300            }
301            let num_str: String = chars[start..i].iter().collect();
302            let num_str = num_str.trim_end_matches('n');
303            let val = num_str.parse::<i64>().unwrap_or(0);
304            tokens.push(Token::NumberLit(val));
305            continue;
306        }
307
308        // Identifiers and keywords
309        if ch.is_ascii_alphabetic() || ch == '_' {
310            let start = i;
311            while i < len && (chars[i].is_ascii_alphanumeric() || chars[i] == '_') {
312                i += 1;
313            }
314            let word: String = chars[start..i].iter().collect();
315            let tok = match word.as_str() {
316                "contract" => Token::Contract,
317                "is" => Token::Is,
318                "immutable" => Token::Immutable,
319                "constructor" => Token::Constructor,
320                "function" => Token::Function,
321                "public" => Token::Public,
322                "private" => Token::Private,
323                "if" => Token::If,
324                "else" => Token::Else,
325                "for" => Token::For,
326                "return" => Token::Return,
327                "require" => Token::Require,
328                "let" => Token::Let,
329                "const" => Token::Const,
330                "true" => Token::True,
331                "false" => Token::False,
332                _ => Token::Ident(word),
333            };
334            tokens.push(tok);
335            continue;
336        }
337
338        // Skip unrecognized characters
339        i += 1;
340    }
341
342    tokens.push(Token::Eof);
343    tokens
344}
345
346// ---------------------------------------------------------------------------
347// Parser
348// ---------------------------------------------------------------------------
349
350struct SolParser<'a> {
351    tokens: Vec<Token>,
352    pos: usize,
353    file: &'a str,
354    errors: &'a mut Vec<Diagnostic>,
355}
356
357impl<'a> SolParser<'a> {
358    fn new(tokens: Vec<Token>, file: &'a str, errors: &'a mut Vec<Diagnostic>) -> Self {
359        Self {
360            tokens,
361            pos: 0,
362            file,
363            errors,
364        }
365    }
366
367    fn peek(&self) -> &Token {
368        self.tokens.get(self.pos).unwrap_or(&Token::Eof)
369    }
370
371    fn advance(&mut self) -> Token {
372        let t = self.tokens.get(self.pos).cloned().unwrap_or(Token::Eof);
373        self.pos += 1;
374        t
375    }
376
377    fn expect(&mut self, expected: &Token) -> bool {
378        if self.peek() == expected {
379            self.advance();
380            true
381        } else {
382            self.errors.push(Diagnostic::error(format!(
383                "Expected {:?}, got {:?}",
384                expected,
385                self.peek()
386            ), None));
387            false
388        }
389    }
390
391    fn expect_ident(&mut self) -> String {
392        match self.advance() {
393            Token::Ident(name) => name,
394            other => {
395                self.errors
396                    .push(Diagnostic::error(format!("Expected identifier, got {:?}", other), None));
397                "_error".to_string()
398            }
399        }
400    }
401
402    fn loc(&self) -> SourceLocation {
403        SourceLocation {
404            file: self.file.to_string(),
405            line: 1,
406            column: 0,
407        }
408    }
409
410    // -----------------------------------------------------------------------
411    // Top-level contract
412    // -----------------------------------------------------------------------
413
414    fn parse_contract(&mut self) -> Option<ContractNode> {
415        // Skip any top-level pragmas, imports, or comments until we see 'contract'
416        while *self.peek() != Token::Contract && *self.peek() != Token::Eof {
417            self.advance();
418        }
419
420        if *self.peek() == Token::Eof {
421            self.errors
422                .push(Diagnostic::error("No 'contract' declaration found", None));
423            return None;
424        }
425
426        self.expect(&Token::Contract);
427        let name = self.expect_ident();
428
429        // 'is' BaseClass
430        let parent_class = if *self.peek() == Token::Is {
431            self.advance();
432            self.expect_ident()
433        } else {
434            "SmartContract".to_string()
435        };
436
437        self.expect(&Token::LBrace);
438
439        let mut properties = Vec::new();
440        let mut constructor: Option<MethodNode> = None;
441        let mut methods = Vec::new();
442
443        while *self.peek() != Token::RBrace && *self.peek() != Token::Eof {
444            match self.peek().clone() {
445                Token::Constructor => {
446                    constructor = Some(self.parse_constructor());
447                }
448                Token::Function => {
449                    methods.push(self.parse_function());
450                }
451                Token::Immutable => {
452                    properties.push(self.parse_property(true));
453                }
454                Token::Ident(_) => {
455                    // Non-immutable property: "Type name;"
456                    properties.push(self.parse_property(false));
457                }
458                _ => {
459                    self.errors.push(Diagnostic::error(format!(
460                        "Unexpected token in contract body: {:?}",
461                        self.peek()
462                    ), None));
463                    self.advance();
464                }
465            }
466        }
467
468        self.expect(&Token::RBrace);
469
470        let constructor = constructor.unwrap_or_else(|| {
471            self.errors
472                .push(Diagnostic::error("Contract must have a constructor", None));
473            MethodNode {
474                name: "constructor".to_string(),
475                params: Vec::new(),
476                body: Vec::new(),
477                visibility: Visibility::Public,
478                source_location: self.loc(),
479            }
480        });
481
482        Some(ContractNode {
483            name,
484            parent_class,
485            properties,
486            constructor,
487            methods,
488            source_file: self.file.to_string(),
489        })
490    }
491
492    // -----------------------------------------------------------------------
493    // Properties
494    // -----------------------------------------------------------------------
495
496    fn parse_property(&mut self, is_immutable: bool) -> PropertyNode {
497        if is_immutable {
498            self.advance(); // consume 'immutable'
499        }
500
501        let type_node = self.parse_type();
502        let name = self.expect_ident();
503
504        // Parse optional initializer: = value
505        let initializer = if *self.peek() == Token::Eq {
506            self.advance(); // consume '='
507            Some(self.parse_expression())
508        } else {
509            None
510        };
511
512        self.expect(&Token::Semicolon);
513
514        PropertyNode {
515            name,
516            prop_type: type_node,
517            readonly: is_immutable,
518            initializer,
519            source_location: self.loc(),
520        }
521    }
522
523    // -----------------------------------------------------------------------
524    // Types
525    // -----------------------------------------------------------------------
526
527    fn parse_type(&mut self) -> TypeNode {
528        let name = self.expect_ident();
529
530        // Check for FixedArray<T, N>
531        if name == "FixedArray" {
532            if *self.peek() == Token::Lt {
533                self.advance(); // <
534                let element = self.parse_type();
535                self.expect(&Token::Comma);
536                let length = match self.advance() {
537                    Token::NumberLit(n) => n as usize,
538                    _ => {
539                        self.errors
540                            .push(Diagnostic::error("FixedArray requires numeric length", None));
541                        0
542                    }
543                };
544                self.expect(&Token::Gt);
545                return TypeNode::FixedArray {
546                    element: Box::new(element),
547                    length,
548                };
549            }
550        }
551
552        // Map Solidity-like type names to Rúnar types
553        let mapped = match name.as_str() {
554            "uint256" | "int256" | "uint" | "int" => "bigint",
555            "bool" => "boolean",
556            "bytes" => "ByteString",
557            "address" => "Addr",
558            _ => &name,
559        };
560
561        if let Some(prim) = PrimitiveTypeName::from_str(mapped) {
562            TypeNode::Primitive(prim)
563        } else {
564            TypeNode::Custom(mapped.to_string())
565        }
566    }
567
568    // -----------------------------------------------------------------------
569    // Constructor
570    // -----------------------------------------------------------------------
571
572    fn parse_constructor(&mut self) -> MethodNode {
573        self.advance(); // consume 'constructor'
574        self.expect(&Token::LParen);
575        let params = self.parse_param_list();
576        self.expect(&Token::RParen);
577
578        // Optional visibility
579        if *self.peek() == Token::Public || *self.peek() == Token::Private {
580            self.advance();
581        }
582
583        let body = self.parse_block();
584
585        MethodNode {
586            name: "constructor".to_string(),
587            params,
588            body,
589            visibility: Visibility::Public,
590            source_location: self.loc(),
591        }
592    }
593
594    // -----------------------------------------------------------------------
595    // Functions/methods
596    // -----------------------------------------------------------------------
597
598    fn parse_function(&mut self) -> MethodNode {
599        self.advance(); // consume 'function'
600        let name = self.expect_ident();
601        self.expect(&Token::LParen);
602        let params = self.parse_param_list();
603        self.expect(&Token::RParen);
604
605        // Parse visibility modifier (Solidity puts it after params)
606        let visibility = match self.peek() {
607            Token::Public => {
608                self.advance();
609                Visibility::Public
610            }
611            Token::Private => {
612                self.advance();
613                Visibility::Private
614            }
615            _ => Visibility::Public,
616        };
617
618        let body = self.parse_block();
619
620        MethodNode {
621            name,
622            params,
623            body,
624            visibility,
625            source_location: self.loc(),
626        }
627    }
628
629    // -----------------------------------------------------------------------
630    // Parameters (Solidity-style: Type name, Type name)
631    // -----------------------------------------------------------------------
632
633    fn parse_param_list(&mut self) -> Vec<ParamNode> {
634        let mut params = Vec::new();
635        if *self.peek() == Token::RParen {
636            return params;
637        }
638
639        params.push(self.parse_param());
640        while *self.peek() == Token::Comma {
641            self.advance();
642            params.push(self.parse_param());
643        }
644        params
645    }
646
647    fn parse_param(&mut self) -> ParamNode {
648        let param_type = self.parse_type();
649        let name = self.expect_ident();
650        ParamNode { name, param_type }
651    }
652
653    // -----------------------------------------------------------------------
654    // Blocks and statements
655    // -----------------------------------------------------------------------
656
657    fn parse_block(&mut self) -> Vec<Statement> {
658        self.expect(&Token::LBrace);
659        let mut stmts = Vec::new();
660        while *self.peek() != Token::RBrace && *self.peek() != Token::Eof {
661            if let Some(stmt) = self.parse_statement() {
662                stmts.push(stmt);
663            }
664        }
665        self.expect(&Token::RBrace);
666        stmts
667    }
668
669    fn parse_statement(&mut self) -> Option<Statement> {
670        match self.peek().clone() {
671            Token::Let | Token::Const => Some(self.parse_var_decl()),
672            Token::If => Some(self.parse_if()),
673            Token::For => Some(self.parse_for()),
674            Token::Return => Some(self.parse_return()),
675            Token::Require => Some(self.parse_require()),
676            _ => {
677                // Expression statement or assignment
678                let expr = self.parse_expression();
679
680                // Check for assignment
681                match self.peek() {
682                    Token::Eq => {
683                        self.advance();
684                        let value = self.parse_expression();
685                        self.expect(&Token::Semicolon);
686                        Some(Statement::Assignment {
687                            target: expr,
688                            value,
689                            source_location: self.loc(),
690                        })
691                    }
692                    Token::PlusEq => {
693                        self.advance();
694                        let rhs = self.parse_expression();
695                        let value = Expression::BinaryExpr {
696                            op: BinaryOp::Add,
697                            left: Box::new(expr.clone()),
698                            right: Box::new(rhs),
699                        };
700                        self.expect(&Token::Semicolon);
701                        Some(Statement::Assignment {
702                            target: expr,
703                            value,
704                            source_location: self.loc(),
705                        })
706                    }
707                    Token::MinusEq => {
708                        self.advance();
709                        let rhs = self.parse_expression();
710                        let value = Expression::BinaryExpr {
711                            op: BinaryOp::Sub,
712                            left: Box::new(expr.clone()),
713                            right: Box::new(rhs),
714                        };
715                        self.expect(&Token::Semicolon);
716                        Some(Statement::Assignment {
717                            target: expr,
718                            value,
719                            source_location: self.loc(),
720                        })
721                    }
722                    Token::StarEq => {
723                        self.advance();
724                        let rhs = self.parse_expression();
725                        let value = Expression::BinaryExpr {
726                            op: BinaryOp::Mul,
727                            left: Box::new(expr.clone()),
728                            right: Box::new(rhs),
729                        };
730                        self.expect(&Token::Semicolon);
731                        Some(Statement::Assignment {
732                            target: expr,
733                            value,
734                            source_location: self.loc(),
735                        })
736                    }
737                    Token::SlashEq => {
738                        self.advance();
739                        let rhs = self.parse_expression();
740                        let value = Expression::BinaryExpr {
741                            op: BinaryOp::Div,
742                            left: Box::new(expr.clone()),
743                            right: Box::new(rhs),
744                        };
745                        self.expect(&Token::Semicolon);
746                        Some(Statement::Assignment {
747                            target: expr,
748                            value,
749                            source_location: self.loc(),
750                        })
751                    }
752                    Token::PercentEq => {
753                        self.advance();
754                        let rhs = self.parse_expression();
755                        let value = Expression::BinaryExpr {
756                            op: BinaryOp::Mod,
757                            left: Box::new(expr.clone()),
758                            right: Box::new(rhs),
759                        };
760                        self.expect(&Token::Semicolon);
761                        Some(Statement::Assignment {
762                            target: expr,
763                            value,
764                            source_location: self.loc(),
765                        })
766                    }
767                    _ => {
768                        self.expect(&Token::Semicolon);
769                        Some(Statement::ExpressionStatement {
770                            expression: expr,
771                            source_location: self.loc(),
772                        })
773                    }
774                }
775            }
776        }
777    }
778
779    fn parse_var_decl(&mut self) -> Statement {
780        let is_const = *self.peek() == Token::Const;
781        self.advance(); // consume let/const
782
783        // Solidity style: might be "Type name = expr;" or "name = expr;"
784        // We support both: "let name = expr;" and "let Type name = expr;" and "let name: Type = expr;"
785
786        // Look ahead: if the next-next token is also an ident (before = or ;), it's "Type name"
787        let (name, var_type) = if self.is_type_then_name() {
788            let type_node = self.parse_type();
789            let name = self.expect_ident();
790            (name, Some(type_node))
791        } else {
792            let name = self.expect_ident();
793            // Check for ": Type"
794            let var_type = if *self.peek() == Token::Colon {
795                self.advance();
796                Some(self.parse_type())
797            } else {
798                None
799            };
800            (name, var_type)
801        };
802
803        self.expect(&Token::Eq);
804        let init = self.parse_expression();
805        self.expect(&Token::Semicolon);
806
807        Statement::VariableDecl {
808            name,
809            var_type,
810            mutable: !is_const,
811            init,
812            source_location: self.loc(),
813        }
814    }
815
816    /// Heuristic: check if the next two tokens are Ident Ident (i.e. Type Name).
817    fn is_type_then_name(&self) -> bool {
818        matches!(
819            (self.tokens.get(self.pos), self.tokens.get(self.pos + 1)),
820            (Some(Token::Ident(_)), Some(Token::Ident(_)))
821        )
822    }
823
824    fn parse_require(&mut self) -> Statement {
825        self.advance(); // consume 'require'
826        self.expect(&Token::LParen);
827        let expr = self.parse_expression();
828        self.expect(&Token::RParen);
829        self.expect(&Token::Semicolon);
830
831        // require(x) -> assert(x)
832        Statement::ExpressionStatement {
833            expression: Expression::CallExpr {
834                callee: Box::new(Expression::Identifier {
835                    name: "assert".to_string(),
836                }),
837                args: vec![expr],
838            },
839            source_location: self.loc(),
840        }
841    }
842
843    fn parse_if(&mut self) -> Statement {
844        self.advance(); // consume 'if'
845        self.expect(&Token::LParen);
846        let condition = self.parse_expression();
847        self.expect(&Token::RParen);
848
849        let then_branch = self.parse_block();
850
851        let else_branch = if *self.peek() == Token::Else {
852            self.advance();
853            if *self.peek() == Token::If {
854                // else if -> wrapped in a single-element branch
855                let nested = self.parse_if();
856                Some(vec![nested])
857            } else {
858                Some(self.parse_block())
859            }
860        } else {
861            None
862        };
863
864        Statement::IfStatement {
865            condition,
866            then_branch,
867            else_branch,
868            source_location: self.loc(),
869        }
870    }
871
872    fn parse_for(&mut self) -> Statement {
873        self.advance(); // consume 'for'
874        self.expect(&Token::LParen);
875
876        // Init
877        let init = if *self.peek() == Token::Let || *self.peek() == Token::Const {
878            self.parse_var_decl()
879        } else {
880            self.errors
881                .push(Diagnostic::error("For loop init must be a variable declaration", None));
882            self.expect(&Token::Semicolon);
883            Statement::VariableDecl {
884                name: "_i".to_string(),
885                var_type: None,
886                mutable: true,
887                init: Expression::BigIntLiteral { value: 0 },
888                source_location: self.loc(),
889            }
890        };
891
892        // Condition
893        let condition = self.parse_expression();
894        self.expect(&Token::Semicolon);
895
896        // Update
897        let update = self.parse_for_update();
898        self.expect(&Token::RParen);
899
900        let body = self.parse_block();
901
902        Statement::ForStatement {
903            init: Box::new(init),
904            condition,
905            update: Box::new(update),
906            body,
907            source_location: self.loc(),
908        }
909    }
910
911    fn parse_for_update(&mut self) -> Statement {
912        let expr = self.parse_expression();
913
914        // Check for assignment
915        match self.peek() {
916            Token::Eq => {
917                self.advance();
918                let value = self.parse_expression();
919                Statement::Assignment {
920                    target: expr,
921                    value,
922                    source_location: self.loc(),
923                }
924            }
925            Token::PlusEq => {
926                self.advance();
927                let rhs = self.parse_expression();
928                Statement::Assignment {
929                    target: expr.clone(),
930                    value: Expression::BinaryExpr {
931                        op: BinaryOp::Add,
932                        left: Box::new(expr),
933                        right: Box::new(rhs),
934                    },
935                    source_location: self.loc(),
936                }
937            }
938            Token::MinusEq => {
939                self.advance();
940                let rhs = self.parse_expression();
941                Statement::Assignment {
942                    target: expr.clone(),
943                    value: Expression::BinaryExpr {
944                        op: BinaryOp::Sub,
945                        left: Box::new(expr),
946                        right: Box::new(rhs),
947                    },
948                    source_location: self.loc(),
949                }
950            }
951            _ => Statement::ExpressionStatement {
952                expression: expr,
953                source_location: self.loc(),
954            },
955        }
956    }
957
958    fn parse_return(&mut self) -> Statement {
959        self.advance(); // consume 'return'
960        if *self.peek() == Token::Semicolon {
961            self.advance();
962            return Statement::ReturnStatement {
963                value: None,
964                source_location: self.loc(),
965            };
966        }
967        let value = self.parse_expression();
968        self.expect(&Token::Semicolon);
969        Statement::ReturnStatement {
970            value: Some(value),
971            source_location: self.loc(),
972        }
973    }
974
975    // -----------------------------------------------------------------------
976    // Expressions (precedence climbing)
977    // -----------------------------------------------------------------------
978
979    fn parse_expression(&mut self) -> Expression {
980        self.parse_ternary()
981    }
982
983    fn parse_ternary(&mut self) -> Expression {
984        let cond = self.parse_or();
985        if *self.peek() == Token::Question {
986            self.advance();
987            let cons = self.parse_ternary();
988            self.expect(&Token::Colon);
989            let alt = self.parse_ternary();
990            Expression::TernaryExpr {
991                condition: Box::new(cond),
992                consequent: Box::new(cons),
993                alternate: Box::new(alt),
994            }
995        } else {
996            cond
997        }
998    }
999
1000    fn parse_or(&mut self) -> Expression {
1001        let mut left = self.parse_and();
1002        while *self.peek() == Token::Or {
1003            self.advance();
1004            let right = self.parse_and();
1005            left = Expression::BinaryExpr {
1006                op: BinaryOp::Or,
1007                left: Box::new(left),
1008                right: Box::new(right),
1009            };
1010        }
1011        left
1012    }
1013
1014    fn parse_and(&mut self) -> Expression {
1015        let mut left = self.parse_bit_or();
1016        while *self.peek() == Token::And {
1017            self.advance();
1018            let right = self.parse_bit_or();
1019            left = Expression::BinaryExpr {
1020                op: BinaryOp::And,
1021                left: Box::new(left),
1022                right: Box::new(right),
1023            };
1024        }
1025        left
1026    }
1027
1028    fn parse_bit_or(&mut self) -> Expression {
1029        let mut left = self.parse_bit_xor();
1030        while *self.peek() == Token::BitOr {
1031            self.advance();
1032            let right = self.parse_bit_xor();
1033            left = Expression::BinaryExpr {
1034                op: BinaryOp::BitOr,
1035                left: Box::new(left),
1036                right: Box::new(right),
1037            };
1038        }
1039        left
1040    }
1041
1042    fn parse_bit_xor(&mut self) -> Expression {
1043        let mut left = self.parse_bit_and();
1044        while *self.peek() == Token::BitXor {
1045            self.advance();
1046            let right = self.parse_bit_and();
1047            left = Expression::BinaryExpr {
1048                op: BinaryOp::BitXor,
1049                left: Box::new(left),
1050                right: Box::new(right),
1051            };
1052        }
1053        left
1054    }
1055
1056    fn parse_bit_and(&mut self) -> Expression {
1057        let mut left = self.parse_equality();
1058        while *self.peek() == Token::BitAnd {
1059            self.advance();
1060            let right = self.parse_equality();
1061            left = Expression::BinaryExpr {
1062                op: BinaryOp::BitAnd,
1063                left: Box::new(left),
1064                right: Box::new(right),
1065            };
1066        }
1067        left
1068    }
1069
1070    fn parse_equality(&mut self) -> Expression {
1071        let mut left = self.parse_comparison();
1072        loop {
1073            match self.peek() {
1074                Token::EqEq | Token::EqEqEq => {
1075                    self.advance();
1076                    let right = self.parse_comparison();
1077                    left = Expression::BinaryExpr {
1078                        op: BinaryOp::StrictEq,
1079                        left: Box::new(left),
1080                        right: Box::new(right),
1081                    };
1082                }
1083                Token::NotEq | Token::NotEqEq => {
1084                    self.advance();
1085                    let right = self.parse_comparison();
1086                    left = Expression::BinaryExpr {
1087                        op: BinaryOp::StrictNe,
1088                        left: Box::new(left),
1089                        right: Box::new(right),
1090                    };
1091                }
1092                _ => break,
1093            }
1094        }
1095        left
1096    }
1097
1098    fn parse_comparison(&mut self) -> Expression {
1099        let mut left = self.parse_additive();
1100        loop {
1101            match self.peek() {
1102                Token::Lt => {
1103                    self.advance();
1104                    let right = self.parse_additive();
1105                    left = Expression::BinaryExpr {
1106                        op: BinaryOp::Lt,
1107                        left: Box::new(left),
1108                        right: Box::new(right),
1109                    };
1110                }
1111                Token::Le => {
1112                    self.advance();
1113                    let right = self.parse_additive();
1114                    left = Expression::BinaryExpr {
1115                        op: BinaryOp::Le,
1116                        left: Box::new(left),
1117                        right: Box::new(right),
1118                    };
1119                }
1120                Token::Gt => {
1121                    self.advance();
1122                    let right = self.parse_additive();
1123                    left = Expression::BinaryExpr {
1124                        op: BinaryOp::Gt,
1125                        left: Box::new(left),
1126                        right: Box::new(right),
1127                    };
1128                }
1129                Token::Ge => {
1130                    self.advance();
1131                    let right = self.parse_additive();
1132                    left = Expression::BinaryExpr {
1133                        op: BinaryOp::Ge,
1134                        left: Box::new(left),
1135                        right: Box::new(right),
1136                    };
1137                }
1138                _ => break,
1139            }
1140        }
1141        left
1142    }
1143
1144    fn parse_additive(&mut self) -> Expression {
1145        let mut left = self.parse_multiplicative();
1146        loop {
1147            match self.peek() {
1148                Token::Plus => {
1149                    self.advance();
1150                    let right = self.parse_multiplicative();
1151                    left = Expression::BinaryExpr {
1152                        op: BinaryOp::Add,
1153                        left: Box::new(left),
1154                        right: Box::new(right),
1155                    };
1156                }
1157                Token::Minus => {
1158                    self.advance();
1159                    let right = self.parse_multiplicative();
1160                    left = Expression::BinaryExpr {
1161                        op: BinaryOp::Sub,
1162                        left: Box::new(left),
1163                        right: Box::new(right),
1164                    };
1165                }
1166                _ => break,
1167            }
1168        }
1169        left
1170    }
1171
1172    fn parse_multiplicative(&mut self) -> Expression {
1173        let mut left = self.parse_unary();
1174        loop {
1175            match self.peek() {
1176                Token::Star => {
1177                    self.advance();
1178                    let right = self.parse_unary();
1179                    left = Expression::BinaryExpr {
1180                        op: BinaryOp::Mul,
1181                        left: Box::new(left),
1182                        right: Box::new(right),
1183                    };
1184                }
1185                Token::Slash => {
1186                    self.advance();
1187                    let right = self.parse_unary();
1188                    left = Expression::BinaryExpr {
1189                        op: BinaryOp::Div,
1190                        left: Box::new(left),
1191                        right: Box::new(right),
1192                    };
1193                }
1194                Token::Percent => {
1195                    self.advance();
1196                    let right = self.parse_unary();
1197                    left = Expression::BinaryExpr {
1198                        op: BinaryOp::Mod,
1199                        left: Box::new(left),
1200                        right: Box::new(right),
1201                    };
1202                }
1203                _ => break,
1204            }
1205        }
1206        left
1207    }
1208
1209    fn parse_unary(&mut self) -> Expression {
1210        match self.peek() {
1211            Token::Not => {
1212                self.advance();
1213                let operand = self.parse_unary();
1214                Expression::UnaryExpr {
1215                    op: UnaryOp::Not,
1216                    operand: Box::new(operand),
1217                }
1218            }
1219            Token::Minus => {
1220                self.advance();
1221                let operand = self.parse_unary();
1222                Expression::UnaryExpr {
1223                    op: UnaryOp::Neg,
1224                    operand: Box::new(operand),
1225                }
1226            }
1227            Token::Tilde => {
1228                self.advance();
1229                let operand = self.parse_unary();
1230                Expression::UnaryExpr {
1231                    op: UnaryOp::BitNot,
1232                    operand: Box::new(operand),
1233                }
1234            }
1235            Token::PlusPlus => {
1236                self.advance();
1237                let operand = self.parse_postfix();
1238                Expression::IncrementExpr {
1239                    operand: Box::new(operand),
1240                    prefix: true,
1241                }
1242            }
1243            Token::MinusMinus => {
1244                self.advance();
1245                let operand = self.parse_postfix();
1246                Expression::DecrementExpr {
1247                    operand: Box::new(operand),
1248                    prefix: true,
1249                }
1250            }
1251            _ => self.parse_postfix(),
1252        }
1253    }
1254
1255    fn parse_postfix(&mut self) -> Expression {
1256        let mut expr = self.parse_primary();
1257
1258        loop {
1259            match self.peek().clone() {
1260                Token::Dot => {
1261                    self.advance();
1262                    let prop = self.expect_ident();
1263                    if matches!(&expr, Expression::Identifier { name } if name == "this") {
1264                        expr = Expression::PropertyAccess { property: prop };
1265                    } else {
1266                        expr = Expression::MemberExpr {
1267                            object: Box::new(expr),
1268                            property: prop,
1269                        };
1270                    }
1271                }
1272                Token::LParen => {
1273                    self.advance();
1274                    let mut args = Vec::new();
1275                    if *self.peek() != Token::RParen {
1276                        args.push(self.parse_expression());
1277                        while *self.peek() == Token::Comma {
1278                            self.advance();
1279                            args.push(self.parse_expression());
1280                        }
1281                    }
1282                    self.expect(&Token::RParen);
1283                    expr = Expression::CallExpr {
1284                        callee: Box::new(expr),
1285                        args,
1286                    };
1287                }
1288                Token::LBracket => {
1289                    self.advance();
1290                    let index = self.parse_expression();
1291                    self.expect(&Token::RBracket);
1292                    expr = Expression::IndexAccess {
1293                        object: Box::new(expr),
1294                        index: Box::new(index),
1295                    };
1296                }
1297                Token::PlusPlus => {
1298                    self.advance();
1299                    expr = Expression::IncrementExpr {
1300                        operand: Box::new(expr),
1301                        prefix: false,
1302                    };
1303                }
1304                Token::MinusMinus => {
1305                    self.advance();
1306                    expr = Expression::DecrementExpr {
1307                        operand: Box::new(expr),
1308                        prefix: false,
1309                    };
1310                }
1311                _ => break,
1312            }
1313        }
1314
1315        expr
1316    }
1317
1318    fn parse_primary(&mut self) -> Expression {
1319        match self.advance() {
1320            Token::NumberLit(v) => Expression::BigIntLiteral { value: v },
1321            Token::True => Expression::BoolLiteral { value: true },
1322            Token::False => Expression::BoolLiteral { value: false },
1323            Token::StringLit(v) => Expression::ByteStringLiteral { value: v },
1324            Token::Ident(name) => {
1325                if name == "this" {
1326                    Expression::Identifier {
1327                        name: "this".to_string(),
1328                    }
1329                } else if name == "super" {
1330                    Expression::Identifier {
1331                        name: "super".to_string(),
1332                    }
1333                } else {
1334                    Expression::Identifier { name }
1335                }
1336            }
1337            Token::Require => {
1338                // require used as expression (unusual but handle it)
1339                self.expect(&Token::LParen);
1340                let arg = self.parse_expression();
1341                self.expect(&Token::RParen);
1342                Expression::CallExpr {
1343                    callee: Box::new(Expression::Identifier {
1344                        name: "assert".to_string(),
1345                    }),
1346                    args: vec![arg],
1347                }
1348            }
1349            Token::LParen => {
1350                let expr = self.parse_expression();
1351                self.expect(&Token::RParen);
1352                expr
1353            }
1354            other => {
1355                self.errors
1356                    .push(Diagnostic::error(format!("Unexpected token in expression: {:?}", other), None));
1357                Expression::BigIntLiteral { value: 0 }
1358            }
1359        }
1360    }
1361}
1362
1363// ---------------------------------------------------------------------------
1364// Tests
1365// ---------------------------------------------------------------------------
1366
1367#[cfg(test)]
1368mod tests {
1369    use super::*;
1370
1371    #[test]
1372    fn test_parse_simple_solidity_contract() {
1373        let source = r#"
1374contract P2PKH is SmartContract {
1375    immutable Addr pubKeyHash;
1376
1377    constructor(Addr pubKeyHash) {
1378        super(pubKeyHash);
1379        this.pubKeyHash = pubKeyHash;
1380    }
1381
1382    function unlock(Sig sig, PubKey pubKey) public {
1383        require(hash160(pubKey) == this.pubKeyHash);
1384        require(checkSig(sig, pubKey));
1385    }
1386}
1387"#;
1388
1389        let result = parse_solidity(source, Some("P2PKH.runar.sol"));
1390        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1391        let contract = result.contract.unwrap();
1392        assert_eq!(contract.name, "P2PKH");
1393        assert_eq!(contract.parent_class, "SmartContract");
1394        assert_eq!(contract.properties.len(), 1);
1395        assert_eq!(contract.properties[0].name, "pubKeyHash");
1396        assert!(contract.properties[0].readonly);
1397        assert_eq!(contract.methods.len(), 1);
1398        assert_eq!(contract.methods[0].name, "unlock");
1399        assert_eq!(contract.methods[0].visibility, Visibility::Public);
1400        assert_eq!(contract.methods[0].params.len(), 2);
1401    }
1402
1403    #[test]
1404    fn test_parse_stateful_solidity_contract() {
1405        let source = r#"
1406contract Counter is StatefulSmartContract {
1407    bigint count;
1408
1409    constructor(bigint count) {
1410        super(count);
1411        this.count = count;
1412    }
1413
1414    function increment() public {
1415        this.count++;
1416    }
1417
1418    function decrement() public {
1419        require(this.count > 0);
1420        this.count--;
1421    }
1422}
1423"#;
1424
1425        let result = parse_solidity(source, Some("Counter.runar.sol"));
1426        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1427        let contract = result.contract.unwrap();
1428        assert_eq!(contract.name, "Counter");
1429        assert_eq!(contract.parent_class, "StatefulSmartContract");
1430        assert_eq!(contract.properties.len(), 1);
1431        assert!(!contract.properties[0].readonly);
1432        assert_eq!(contract.methods.len(), 2);
1433    }
1434
1435    #[test]
1436    fn test_eq_maps_to_strict_eq() {
1437        let source = r#"
1438contract Test is SmartContract {
1439    immutable bigint x;
1440
1441    constructor(bigint x) {
1442        super(x);
1443        this.x = x;
1444    }
1445
1446    function check(bigint y) public {
1447        require(this.x == y);
1448        require(this.x != y);
1449    }
1450}
1451"#;
1452
1453        let result = parse_solidity(source, Some("Test.runar.sol"));
1454        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1455        let contract = result.contract.unwrap();
1456        let body = &contract.methods[0].body;
1457        assert_eq!(body.len(), 2);
1458
1459        // First assert: StrictEq
1460        if let Statement::ExpressionStatement { expression, .. } = &body[0] {
1461            if let Expression::CallExpr { args, .. } = expression {
1462                if let Expression::BinaryExpr { op, .. } = &args[0] {
1463                    assert_eq!(*op, BinaryOp::StrictEq);
1464                } else {
1465                    panic!("Expected BinaryExpr inside assert");
1466                }
1467            } else {
1468                panic!("Expected CallExpr for assert");
1469            }
1470        }
1471    }
1472
1473    #[test]
1474    fn test_for_loop() {
1475        let source = r#"
1476contract Loop is SmartContract {
1477    immutable bigint n;
1478
1479    constructor(bigint n) {
1480        super(n);
1481        this.n = n;
1482    }
1483
1484    function run() public {
1485        let bigint sum = 0;
1486        for (let bigint i = 0; i < this.n; i++) {
1487            sum += i;
1488        }
1489        require(sum > 0);
1490    }
1491}
1492"#;
1493
1494        let result = parse_solidity(source, Some("Loop.runar.sol"));
1495        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1496        let contract = result.contract.unwrap();
1497        assert_eq!(contract.methods[0].body.len(), 3); // let sum, for, require
1498    }
1499
1500    // -----------------------------------------------------------------------
1501    // Test: method name, visibility, and parameter names/types are parsed
1502    // -----------------------------------------------------------------------
1503
1504    #[test]
1505    fn test_methods_and_params() {
1506        let source = r#"
1507contract Adder is SmartContract {
1508    immutable bigint target;
1509
1510    constructor(bigint target) {
1511        super(target);
1512        this.target = target;
1513    }
1514
1515    function verify(bigint a, bigint b) public {
1516        require(a + b == this.target);
1517    }
1518}
1519"#;
1520
1521        let result = parse_solidity(source, Some("Adder.runar.sol"));
1522        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1523        let contract = result.contract.unwrap();
1524
1525        assert_eq!(contract.methods.len(), 1, "expected 1 method");
1526        let method = &contract.methods[0];
1527        assert_eq!(method.name, "verify");
1528        assert_eq!(
1529            method.visibility,
1530            Visibility::Public,
1531            "expected public visibility"
1532        );
1533        assert_eq!(method.params.len(), 2, "expected 2 params");
1534        assert_eq!(method.params[0].name, "a");
1535        assert_eq!(method.params[1].name, "b");
1536    }
1537
1538    // -----------------------------------------------------------------------
1539    // Test: malformed Solidity produces an error
1540    // -----------------------------------------------------------------------
1541
1542    #[test]
1543    fn test_invalid_syntax_error() {
1544        // Missing contract name and parent
1545        let source = r#"
1546contract {
1547    // missing name and parent
1548}
1549"#;
1550
1551        let result = parse_solidity(source, Some("bad.runar.sol"));
1552        // Should either produce errors or fail to produce a valid contract
1553        let is_bad = !result.errors.is_empty() || result.contract.is_none();
1554        assert!(
1555            is_bad,
1556            "expected errors or no contract for invalid Solidity syntax"
1557        );
1558    }
1559
1560    // -----------------------------------------------------------------------
1561    // Test: contract with multiple properties all parsed correctly
1562    // -----------------------------------------------------------------------
1563
1564    #[test]
1565    fn test_multiple_properties() {
1566        let source = r#"
1567contract TwoProps is SmartContract {
1568    immutable Addr addr;
1569    immutable PubKey key;
1570
1571    constructor(Addr addr, PubKey key) {
1572        super(addr, key);
1573        this.addr = addr;
1574        this.key = key;
1575    }
1576
1577    function check(bigint x) public {
1578        require(x == 1);
1579    }
1580}
1581"#;
1582
1583        let result = parse_solidity(source, Some("TwoProps.runar.sol"));
1584        assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1585        let contract = result.contract.unwrap();
1586        assert_eq!(
1587            contract.properties.len(),
1588            2,
1589            "expected 2 properties, got {}",
1590            contract.properties.len()
1591        );
1592        assert_eq!(contract.properties[0].name, "addr");
1593        assert_eq!(contract.properties[1].name, "key");
1594    }
1595}