1use super::ast::{
7 BinaryOp, ContractNode, Expression, MethodNode, ParamNode, PrimitiveTypeName,
8 PropertyNode, SourceLocation, Statement, TypeNode, UnaryOp, Visibility,
9};
10use super::diagnostic::Diagnostic;
11use super::parser::ParseResult;
12
13#[derive(Debug, Clone, PartialEq)]
18enum TokenType {
19 Use, Struct, Impl, Fn, Pub, Let, Mut, If, Else, For, Return, In,
20 True, False, Self_,
21 AssertMacro, AssertEqMacro,
22 Ident(String), Number(String), HexString(String),
23 HashBracket, LParen, RParen, LBrace, RBrace, LBracket, RBracket,
27 Semi, Comma, Dot, Colon, ColonColon, Arrow,
28 Plus, Minus, Star, Slash, Percent,
30 EqEq, BangEq, Lt, LtEq, Gt, GtEq,
31 AmpAmp, PipePipe,
32 Amp, Pipe, Caret, Tilde, Bang,
33 Eq, PlusEq, MinusEq,
34 Eof,
36}
37
38#[derive(Debug, Clone)]
39struct Token {
40 typ: TokenType,
41 line: usize,
42 col: usize,
43}
44
45fn tokenize(source: &str) -> Vec<Token> {
50 let chars: Vec<char> = source.chars().collect();
51 let mut tokens = Vec::new();
52 let mut pos = 0;
53 let mut line = 1usize;
54 let mut col = 1usize;
55
56 while pos < chars.len() {
57 let ch = chars[pos];
58 let l = line;
59 let c = col;
60
61 if ch.is_whitespace() {
63 if ch == '\n' { line += 1; col = 1; } else { col += 1; }
64 pos += 1;
65 continue;
66 }
67
68 if ch == '/' && pos + 1 < chars.len() && chars[pos + 1] == '/' {
70 while pos < chars.len() && chars[pos] != '\n' { pos += 1; }
71 continue;
72 }
73
74 if ch == '/' && pos + 1 < chars.len() && chars[pos + 1] == '*' {
76 pos += 2; col += 2;
77 while pos + 1 < chars.len() {
78 if chars[pos] == '\n' { line += 1; col = 1; }
79 if chars[pos] == '*' && chars[pos + 1] == '/' { pos += 2; col += 2; break; }
80 pos += 1; col += 1;
81 }
82 continue;
83 }
84
85 if ch == '#' && pos + 1 < chars.len() && chars[pos + 1] == '[' {
87 tokens.push(Token { typ: TokenType::HashBracket, line: l, col: c });
88 pos += 2; col += 2;
89 continue;
90 }
91
92 if pos + 1 < chars.len() {
94 let two = format!("{}{}", ch, chars[pos + 1]);
95 let tok = match two.as_str() {
96 "::" => Some(TokenType::ColonColon),
97 "->" => Some(TokenType::Arrow),
98 "==" => Some(TokenType::EqEq),
99 "!=" => Some(TokenType::BangEq),
100 "<=" => Some(TokenType::LtEq),
101 ">=" => Some(TokenType::GtEq),
102 "&&" => Some(TokenType::AmpAmp),
103 "||" => Some(TokenType::PipePipe),
104 "+=" => Some(TokenType::PlusEq),
105 "-=" => Some(TokenType::MinusEq),
106 _ => None,
107 };
108 if let Some(t) = tok {
109 tokens.push(Token { typ: t, line: l, col: c });
110 pos += 2; col += 2;
111 continue;
112 }
113 }
114
115 let single = match ch {
117 '(' => Some(TokenType::LParen),
118 ')' => Some(TokenType::RParen),
119 '{' => Some(TokenType::LBrace),
120 '}' => Some(TokenType::RBrace),
121 '[' => Some(TokenType::LBracket),
122 ']' => Some(TokenType::RBracket),
123 ';' => Some(TokenType::Semi),
124 ',' => Some(TokenType::Comma),
125 '.' => Some(TokenType::Dot),
126 ':' => Some(TokenType::Colon),
127 '+' => Some(TokenType::Plus),
128 '-' => Some(TokenType::Minus),
129 '*' => Some(TokenType::Star),
130 '/' => Some(TokenType::Slash),
131 '%' => Some(TokenType::Percent),
132 '<' => Some(TokenType::Lt),
133 '>' => Some(TokenType::Gt),
134 '&' => Some(TokenType::Amp),
135 '|' => Some(TokenType::Pipe),
136 '^' => Some(TokenType::Caret),
137 '~' => Some(TokenType::Tilde),
138 '!' => Some(TokenType::Bang),
139 '=' => Some(TokenType::Eq),
140 _ => None,
141 };
142 if let Some(t) = single {
143 tokens.push(Token { typ: t, line: l, col: c });
144 pos += 1; col += 1;
145 continue;
146 }
147
148 if ch == '0' && pos + 1 < chars.len() && chars[pos + 1] == 'x' {
150 let mut val = String::new();
151 pos += 2; col += 2;
152 while pos < chars.len() && chars[pos].is_ascii_hexdigit() {
153 val.push(chars[pos]);
154 pos += 1; col += 1;
155 }
156 tokens.push(Token { typ: TokenType::HexString(val), line: l, col: c });
157 continue;
158 }
159
160 if ch.is_ascii_digit() {
162 let mut val = String::new();
163 while pos < chars.len() && (chars[pos].is_ascii_digit() || chars[pos] == '_') {
164 if chars[pos] != '_' { val.push(chars[pos]); }
165 pos += 1; col += 1;
166 }
167 tokens.push(Token { typ: TokenType::Number(val), line: l, col: c });
168 continue;
169 }
170
171 if ch.is_alphabetic() || ch == '_' {
173 let mut val = String::new();
174 while pos < chars.len() && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
175 val.push(chars[pos]);
176 pos += 1; col += 1;
177 }
178 if (val == "assert" || val == "assert_eq") && pos < chars.len() && chars[pos] == '!' {
180 pos += 1; col += 1;
181 let tok = if val == "assert" { TokenType::AssertMacro } else { TokenType::AssertEqMacro };
182 tokens.push(Token { typ: tok, line: l, col: c });
183 continue;
184 }
185 let tok = match val.as_str() {
186 "use" => TokenType::Use,
187 "struct" => TokenType::Struct,
188 "impl" => TokenType::Impl,
189 "fn" => TokenType::Fn,
190 "pub" => TokenType::Pub,
191 "let" => TokenType::Let,
192 "mut" => TokenType::Mut,
193 "if" => TokenType::If,
194 "else" => TokenType::Else,
195 "for" => TokenType::For,
196 "return" => TokenType::Return,
197 "in" => TokenType::In,
198 "true" => TokenType::True,
199 "false" => TokenType::False,
200 "self" => TokenType::Self_,
201 _ => TokenType::Ident(val),
202 };
203 tokens.push(Token { typ: tok, line: l, col: c });
204 continue;
205 }
206
207 if ch == '"' {
209 let mut val = String::new();
210 pos += 1; col += 1;
211 while pos < chars.len() && chars[pos] != '"' {
212 val.push(chars[pos]);
213 pos += 1; col += 1;
214 }
215 if pos < chars.len() { pos += 1; col += 1; } tokens.push(Token { typ: TokenType::HexString(val), line: l, col: c });
217 continue;
218 }
219
220 pos += 1; col += 1;
221 }
222
223 tokens.push(Token { typ: TokenType::Eof, line, col });
224 tokens
225}
226
227struct RustDslParser {
232 tokens: Vec<Token>,
233 pos: usize,
234 file: String,
235 errors: Vec<Diagnostic>,
236}
237
238impl RustDslParser {
239 fn new(tokens: Vec<Token>, file: String) -> Self {
240 Self { tokens, pos: 0, file, errors: Vec::new() }
241 }
242
243 fn current(&self) -> &Token {
244 self.tokens.get(self.pos).unwrap_or(self.tokens.last().unwrap())
245 }
246
247 fn advance_clone(&mut self) -> Token {
248 let t = self.current().clone();
249 if self.pos < self.tokens.len() - 1 { self.pos += 1; }
250 t
251 }
252
253 fn expect(&mut self, expected: &TokenType) {
254 if std::mem::discriminant(&self.current().typ) != std::mem::discriminant(expected) {
255 self.errors.push(Diagnostic::error(format!("Expected {:?}, got {:?} at {}:{}", expected, self.current().typ, self.current().line, self.current().col), None));
256 }
257 self.advance_clone();
258 }
259
260 fn match_tok(&mut self, expected: &TokenType) -> bool {
261 if std::mem::discriminant(&self.current().typ) == std::mem::discriminant(expected) {
262 self.advance_clone();
263 true
264 } else {
265 false
266 }
267 }
268
269 fn loc(&self) -> SourceLocation {
270 SourceLocation { file: self.file.clone(), line: self.current().line, column: self.current().col }
271 }
272
273 fn parse(mut self) -> ParseResult {
274 while matches!(self.current().typ, TokenType::Use) {
276 while !matches!(self.current().typ, TokenType::Semi | TokenType::Eof) { self.advance_clone(); }
277 if matches!(self.current().typ, TokenType::Semi) { self.advance_clone(); }
278 }
279
280 let mut properties: Vec<PropertyNode> = Vec::new();
282 let mut contract_name = String::new();
283 let mut parent_class = "SmartContract".to_string();
284 let mut methods: Vec<MethodNode> = Vec::new();
285
286 while !matches!(self.current().typ, TokenType::Eof) {
287 if matches!(self.current().typ, TokenType::HashBracket) {
289 let attr = self.parse_attribute();
290
291 if attr == "runar::contract" || attr == "runar::stateful_contract" {
292 if attr == "runar::stateful_contract" {
293 parent_class = "StatefulSmartContract".to_string();
294 }
295 if matches!(self.current().typ, TokenType::Pub) { self.advance_clone(); }
297 self.expect(&TokenType::Struct);
298 if let TokenType::Ident(name) = self.current().typ.clone() {
299 contract_name = name;
300 self.advance_clone();
301 }
302 self.expect(&TokenType::LBrace);
303
304 while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
305 let mut readonly = false;
307 if matches!(self.current().typ, TokenType::HashBracket) {
308 let field_attr = self.parse_attribute();
309 if field_attr == "readonly" { readonly = true; }
310 }
311
312 let loc = self.loc();
313 if let TokenType::Ident(field_name) = self.current().typ.clone() {
314 self.advance_clone();
315 self.expect(&TokenType::Colon);
316 let field_type = self.parse_rust_type();
317 self.match_tok(&TokenType::Comma);
318
319 if !readonly {
320 }
322
323 let camel_name = snake_to_camel(&field_name);
325 if camel_name != "txPreimage" {
326 properties.push(PropertyNode {
327 name: camel_name,
328 prop_type: field_type,
329 readonly,
330 initializer: None,
331 source_location: loc,
332 });
333 }
334 } else {
335 self.advance_clone();
336 }
337 }
338 self.expect(&TokenType::RBrace);
339 } else if attr.starts_with("runar::methods") {
340 if matches!(self.current().typ, TokenType::Impl) { self.advance_clone(); }
342 if let TokenType::Ident(_) = self.current().typ { self.advance_clone(); }
344 self.expect(&TokenType::LBrace);
345
346 while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
347 let mut visibility = Visibility::Private;
349 if matches!(self.current().typ, TokenType::HashBracket) {
350 let method_attr = self.parse_attribute();
351 if method_attr == "public" { visibility = Visibility::Public; }
352 }
353 if matches!(self.current().typ, TokenType::Pub) {
354 self.advance_clone();
355 visibility = Visibility::Public;
356 }
357 methods.push(self.parse_function(visibility));
358 }
359 self.expect(&TokenType::RBrace);
360 } else {
361 continue;
363 }
364 } else {
365 self.advance_clone();
366 }
367 }
368
369 if properties.iter().any(|p| !p.readonly) {
371 parent_class = "StatefulSmartContract".to_string();
372 }
373
374 if contract_name.is_empty() {
375 self.errors.push(Diagnostic::error("No Rúnar contract struct found", None));
376 return ParseResult { contract: None, errors: self.errors };
377 }
378
379 let mut final_methods = Vec::new();
382 for m in methods {
383 if m.name == "init" && m.params.is_empty() {
384 for stmt in &m.body {
385 if let Statement::Assignment { target, value, .. } = stmt {
386 if let Expression::PropertyAccess { property } = target {
387 for p in properties.iter_mut() {
388 if p.name == *property {
389 p.initializer = Some(value.clone());
390 break;
391 }
392 }
393 }
394 }
395 }
396 } else {
397 final_methods.push(m);
398 }
399 }
400 let methods = final_methods;
401
402 let uninit_props: Vec<&PropertyNode> = properties.iter()
404 .filter(|p| p.initializer.is_none())
405 .collect();
406
407 let loc = SourceLocation { file: self.file.clone(), line: 1, column: 1 };
408
409 let super_args: Vec<Expression> = uninit_props.iter()
411 .map(|p| Expression::Identifier { name: p.name.clone() })
412 .collect();
413 let super_call = Statement::ExpressionStatement {
414 expression: Expression::CallExpr {
415 callee: Box::new(Expression::Identifier { name: "super".to_string() }),
416 args: super_args,
417 },
418 source_location: loc.clone(),
419 };
420
421 let mut ctor_body = vec![super_call];
423 for p in &uninit_props {
424 ctor_body.push(Statement::Assignment {
425 target: Expression::PropertyAccess { property: p.name.clone() },
426 value: Expression::Identifier { name: p.name.clone() },
427 source_location: loc.clone(),
428 });
429 }
430
431 let constructor = MethodNode {
432 name: "constructor".to_string(),
433 params: uninit_props.iter().map(|p| ParamNode {
434 name: p.name.clone(),
435 param_type: p.prop_type.clone(),
436 }).collect(),
437 body: ctor_body,
438 visibility: Visibility::Public,
439 source_location: loc,
440 };
441
442 let contract = ContractNode {
443 name: contract_name,
444 parent_class,
445 properties,
446 constructor,
447 methods,
448 source_file: self.file.clone(),
449 };
450
451 ParseResult { contract: Some(contract), errors: self.errors }
452 }
453
454 fn parse_attribute(&mut self) -> String {
455 self.advance_clone(); let mut attr = String::new();
458 let mut depth = 1;
459 while depth > 0 && !matches!(self.current().typ, TokenType::Eof) {
460 match &self.current().typ {
461 TokenType::LBracket => { depth += 1; self.advance_clone(); }
462 TokenType::RBracket => {
463 depth -= 1;
464 if depth == 0 { self.advance_clone(); break; }
465 self.advance_clone();
466 }
467 TokenType::Ident(name) => { attr.push_str(name); self.advance_clone(); }
468 TokenType::ColonColon => { attr.push_str("::"); self.advance_clone(); }
469 TokenType::LParen => { attr.push('('); self.advance_clone(); }
470 TokenType::RParen => { attr.push(')'); self.advance_clone(); }
471 _ => { self.advance_clone(); }
472 }
473 }
474 attr
475 }
476
477 fn parse_rust_type(&mut self) -> TypeNode {
478 if let TokenType::Ident(name) = self.current().typ.clone() {
479 self.advance_clone();
480 let mapped = map_rust_type(&name);
481 if let Some(prim) = PrimitiveTypeName::from_str(&mapped) {
482 TypeNode::Primitive(prim)
483 } else {
484 TypeNode::Custom(mapped)
485 }
486 } else {
487 self.advance_clone();
488 TypeNode::Custom("unknown".to_string())
489 }
490 }
491
492 fn parse_function(&mut self, visibility: Visibility) -> MethodNode {
493 let loc = self.loc();
494 self.expect(&TokenType::Fn);
495
496 let raw_name = if let TokenType::Ident(name) = self.current().typ.clone() {
497 self.advance_clone();
498 name
499 } else {
500 self.advance_clone();
501 "unknown".to_string()
502 };
503 let name = snake_to_camel(&raw_name);
504
505 self.expect(&TokenType::LParen);
506 let mut params: Vec<ParamNode> = Vec::new();
507
508 while !matches!(self.current().typ, TokenType::RParen | TokenType::Eof) {
509 if matches!(self.current().typ, TokenType::Amp) {
511 self.advance_clone();
512 if matches!(self.current().typ, TokenType::Mut) { self.advance_clone(); }
513 if matches!(self.current().typ, TokenType::Self_) {
514 self.advance_clone();
515 if matches!(self.current().typ, TokenType::Comma) { self.advance_clone(); }
516 continue;
517 }
518 }
519 if matches!(self.current().typ, TokenType::Self_) {
520 self.advance_clone();
521 if matches!(self.current().typ, TokenType::Comma) { self.advance_clone(); }
522 continue;
523 }
524
525 if let TokenType::Ident(param_name) = self.current().typ.clone() {
526 self.advance_clone();
527 self.expect(&TokenType::Colon);
528 if matches!(self.current().typ, TokenType::Amp) {
530 self.advance_clone();
531 if matches!(self.current().typ, TokenType::Mut) { self.advance_clone(); }
532 }
533 let param_type = self.parse_rust_type();
534 params.push(ParamNode {
535 name: snake_to_camel(¶m_name),
536 param_type,
537 });
538 } else {
539 self.advance_clone();
540 }
541 if matches!(self.current().typ, TokenType::Comma) { self.advance_clone(); }
542 }
543 self.expect(&TokenType::RParen);
544
545 if matches!(self.current().typ, TokenType::Arrow) {
547 self.advance_clone();
548 self.parse_rust_type();
549 }
550
551 self.expect(&TokenType::LBrace);
552 let mut body: Vec<Statement> = Vec::new();
553 while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
554 if let Some(stmt) = self.parse_statement() {
555 body.push(stmt);
556 }
557 }
558 self.expect(&TokenType::RBrace);
559
560 MethodNode { name, params, body, visibility, source_location: loc }
561 }
562
563 fn parse_statement(&mut self) -> Option<Statement> {
564 let loc = self.loc();
565
566 if matches!(self.current().typ, TokenType::AssertMacro) {
568 self.advance_clone();
569 self.expect(&TokenType::LParen);
570 let expr = self.parse_expression();
571 self.expect(&TokenType::RParen);
572 self.match_tok(&TokenType::Semi);
573 return Some(Statement::ExpressionStatement {
574 expression: Expression::CallExpr {
575 callee: Box::new(Expression::Identifier { name: "assert".to_string() }),
576 args: vec![expr],
577 },
578 source_location: loc,
579 });
580 }
581
582 if matches!(self.current().typ, TokenType::AssertEqMacro) {
584 self.advance_clone();
585 self.expect(&TokenType::LParen);
586 let left = self.parse_expression();
587 self.expect(&TokenType::Comma);
588 let right = self.parse_expression();
589 self.expect(&TokenType::RParen);
590 self.match_tok(&TokenType::Semi);
591 return Some(Statement::ExpressionStatement {
592 expression: Expression::CallExpr {
593 callee: Box::new(Expression::Identifier { name: "assert".to_string() }),
594 args: vec![Expression::BinaryExpr {
595 op: BinaryOp::StrictEq,
596 left: Box::new(left),
597 right: Box::new(right),
598 }],
599 },
600 source_location: loc,
601 });
602 }
603
604 if matches!(self.current().typ, TokenType::Let) {
606 self.advance_clone();
607 let mutable = self.match_tok(&TokenType::Mut);
608 let var_name = if let TokenType::Ident(name) = self.current().typ.clone() {
609 self.advance_clone();
610 snake_to_camel(&name)
611 } else {
612 self.advance_clone();
613 "unknown".to_string()
614 };
615 let var_type = if matches!(self.current().typ, TokenType::Colon) {
616 self.advance_clone();
617 if matches!(self.current().typ, TokenType::Amp) { self.advance_clone(); }
618 if matches!(self.current().typ, TokenType::Mut) { self.advance_clone(); }
619 Some(self.parse_rust_type())
620 } else {
621 None
622 };
623 self.expect(&TokenType::Eq);
624 let init = self.parse_expression();
625 self.match_tok(&TokenType::Semi);
626 return Some(Statement::VariableDecl {
627 name: var_name, var_type, mutable, init, source_location: loc,
628 });
629 }
630
631 if matches!(self.current().typ, TokenType::If) {
633 self.advance_clone();
634 let condition = self.parse_expression();
635 self.expect(&TokenType::LBrace);
636 let mut then_branch = Vec::new();
637 while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
638 if let Some(s) = self.parse_statement() { then_branch.push(s); }
639 }
640 self.expect(&TokenType::RBrace);
641 let else_branch = if matches!(self.current().typ, TokenType::Else) {
642 self.advance_clone();
643 self.expect(&TokenType::LBrace);
644 let mut eb = Vec::new();
645 while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
646 if let Some(s) = self.parse_statement() { eb.push(s); }
647 }
648 self.expect(&TokenType::RBrace);
649 Some(eb)
650 } else {
651 None
652 };
653 return Some(Statement::IfStatement { condition, then_branch, else_branch, source_location: loc });
654 }
655
656 if matches!(self.current().typ, TokenType::Return) {
658 self.advance_clone();
659 let value = if !matches!(self.current().typ, TokenType::Semi | TokenType::RBrace) {
660 Some(self.parse_expression())
661 } else {
662 None
663 };
664 self.match_tok(&TokenType::Semi);
665 return Some(Statement::ReturnStatement { value, source_location: loc });
666 }
667
668 let expr = self.parse_expression();
670
671 if matches!(self.current().typ, TokenType::Eq) {
673 self.advance_clone();
674 let value = self.parse_expression();
675 self.match_tok(&TokenType::Semi);
676 return Some(Statement::Assignment {
677 target: self.convert_self_access(expr),
678 value,
679 source_location: loc,
680 });
681 }
682
683 if matches!(self.current().typ, TokenType::PlusEq) {
685 self.advance_clone();
686 let rhs = self.parse_expression();
687 self.match_tok(&TokenType::Semi);
688 let target = self.convert_self_access(expr.clone());
689 return Some(Statement::Assignment {
690 target: target.clone(),
691 value: Expression::BinaryExpr { op: BinaryOp::Add, left: Box::new(target), right: Box::new(rhs) },
692 source_location: loc,
693 });
694 }
695 if matches!(self.current().typ, TokenType::MinusEq) {
696 self.advance_clone();
697 let rhs = self.parse_expression();
698 self.match_tok(&TokenType::Semi);
699 let target = self.convert_self_access(expr.clone());
700 return Some(Statement::Assignment {
701 target: target.clone(),
702 value: Expression::BinaryExpr { op: BinaryOp::Sub, left: Box::new(target), right: Box::new(rhs) },
703 source_location: loc,
704 });
705 }
706
707 let had_semi = self.match_tok(&TokenType::Semi);
708 if !had_semi && matches!(self.current().typ, TokenType::RBrace) {
710 return Some(Statement::ReturnStatement { value: Some(expr), source_location: loc });
711 }
712 Some(Statement::ExpressionStatement { expression: expr, source_location: loc })
713 }
714
715 fn convert_self_access(&self, expr: Expression) -> Expression {
716 if let Expression::MemberExpr { ref object, ref property } = expr {
717 if let Expression::Identifier { ref name } = **object {
718 if name == "self" {
719 return Expression::PropertyAccess { property: snake_to_camel(property) };
720 }
721 }
722 }
723 expr
724 }
725
726 fn parse_expression(&mut self) -> Expression { self.parse_or() }
728
729 fn parse_or(&mut self) -> Expression {
730 let mut left = self.parse_and();
731 while matches!(self.current().typ, TokenType::PipePipe) {
732 self.advance_clone();
733 let right = self.parse_and();
734 left = Expression::BinaryExpr { op: BinaryOp::Or, left: Box::new(left), right: Box::new(right) };
735 }
736 left
737 }
738
739 fn parse_and(&mut self) -> Expression {
740 let mut left = self.parse_bit_or();
741 while matches!(self.current().typ, TokenType::AmpAmp) {
742 self.advance_clone();
743 let right = self.parse_bit_or();
744 left = Expression::BinaryExpr { op: BinaryOp::And, left: Box::new(left), right: Box::new(right) };
745 }
746 left
747 }
748
749 fn parse_bit_or(&mut self) -> Expression {
750 let mut left = self.parse_bit_xor();
751 while matches!(self.current().typ, TokenType::Pipe) {
752 self.advance_clone();
753 left = Expression::BinaryExpr { op: BinaryOp::BitOr, left: Box::new(left), right: Box::new(self.parse_bit_xor()) };
754 }
755 left
756 }
757
758 fn parse_bit_xor(&mut self) -> Expression {
759 let mut left = self.parse_bit_and();
760 while matches!(self.current().typ, TokenType::Caret) {
761 self.advance_clone();
762 left = Expression::BinaryExpr { op: BinaryOp::BitXor, left: Box::new(left), right: Box::new(self.parse_bit_and()) };
763 }
764 left
765 }
766
767 fn parse_bit_and(&mut self) -> Expression {
768 let mut left = self.parse_equality();
769 while matches!(self.current().typ, TokenType::Amp) {
770 self.advance_clone();
771 left = Expression::BinaryExpr { op: BinaryOp::BitAnd, left: Box::new(left), right: Box::new(self.parse_equality()) };
772 }
773 left
774 }
775
776 fn parse_equality(&mut self) -> Expression {
777 let mut left = self.parse_comparison();
778 loop {
779 let op = match self.current().typ {
780 TokenType::EqEq => BinaryOp::StrictEq,
781 TokenType::BangEq => BinaryOp::StrictNe,
782 _ => break,
783 };
784 self.advance_clone();
785 left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(self.parse_comparison()) };
786 }
787 left
788 }
789
790 fn parse_comparison(&mut self) -> Expression {
791 let mut left = self.parse_add_sub();
792 loop {
793 let op = match self.current().typ {
794 TokenType::Lt => BinaryOp::Lt,
795 TokenType::LtEq => BinaryOp::Le,
796 TokenType::Gt => BinaryOp::Gt,
797 TokenType::GtEq => BinaryOp::Ge,
798 _ => break,
799 };
800 self.advance_clone();
801 left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(self.parse_add_sub()) };
802 }
803 left
804 }
805
806 fn parse_add_sub(&mut self) -> Expression {
807 let mut left = self.parse_mul_div();
808 loop {
809 let op = match self.current().typ {
810 TokenType::Plus => BinaryOp::Add,
811 TokenType::Minus => BinaryOp::Sub,
812 _ => break,
813 };
814 self.advance_clone();
815 left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(self.parse_mul_div()) };
816 }
817 left
818 }
819
820 fn parse_mul_div(&mut self) -> Expression {
821 let mut left = self.parse_unary();
822 loop {
823 let op = match self.current().typ {
824 TokenType::Star => BinaryOp::Mul,
825 TokenType::Slash => BinaryOp::Div,
826 TokenType::Percent => BinaryOp::Mod,
827 _ => break,
828 };
829 self.advance_clone();
830 left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(self.parse_unary()) };
831 }
832 left
833 }
834
835 fn parse_unary(&mut self) -> Expression {
836 match self.current().typ {
837 TokenType::Bang => { self.advance_clone(); Expression::UnaryExpr { op: UnaryOp::Not, operand: Box::new(self.parse_unary()) } }
838 TokenType::Minus => { self.advance_clone(); Expression::UnaryExpr { op: UnaryOp::Neg, operand: Box::new(self.parse_unary()) } }
839 TokenType::Tilde => { self.advance_clone(); Expression::UnaryExpr { op: UnaryOp::BitNot, operand: Box::new(self.parse_unary()) } }
840 TokenType::Amp => {
841 self.advance_clone();
842 if matches!(self.current().typ, TokenType::Mut) { self.advance_clone(); }
843 self.parse_postfix()
844 }
845 _ => self.parse_postfix(),
846 }
847 }
848
849 fn parse_postfix(&mut self) -> Expression {
850 let mut expr = self.parse_primary();
851 loop {
852 if matches!(self.current().typ, TokenType::LParen) {
853 self.advance_clone();
854 let mut args = Vec::new();
855 while !matches!(self.current().typ, TokenType::RParen | TokenType::Eof) {
856 args.push(self.parse_expression());
857 if matches!(self.current().typ, TokenType::Comma) { self.advance_clone(); }
858 }
859 self.expect(&TokenType::RParen);
860 expr = Expression::CallExpr { callee: Box::new(expr), args };
861 } else if matches!(self.current().typ, TokenType::Dot) {
862 self.advance_clone();
863 let prop = if let TokenType::Ident(name) = self.current().typ.clone() {
864 self.advance_clone();
865 snake_to_camel(&name)
866 } else {
867 self.advance_clone();
868 "unknown".to_string()
869 };
870 if let Expression::Identifier { ref name } = expr {
872 if name == "self" {
873 expr = Expression::PropertyAccess { property: prop };
874 continue;
875 }
876 }
877 expr = Expression::MemberExpr { object: Box::new(expr), property: prop };
878 } else if matches!(self.current().typ, TokenType::ColonColon) {
879 self.advance_clone();
880 if let TokenType::Ident(name) = self.current().typ.clone() {
881 self.advance_clone();
882 expr = Expression::Identifier { name: snake_to_camel(&name) };
883 }
884 } else if matches!(self.current().typ, TokenType::LBracket) {
885 self.advance_clone();
886 let index = self.parse_expression();
887 self.expect(&TokenType::RBracket);
888 expr = Expression::IndexAccess { object: Box::new(expr), index: Box::new(index) };
889 } else {
890 break;
891 }
892 }
893 expr
894 }
895
896 fn parse_primary(&mut self) -> Expression {
897 match self.current().typ.clone() {
898 TokenType::Number(val) => {
899 self.advance_clone();
900 let n: i64 = val.parse().unwrap_or(0);
901 Expression::BigIntLiteral { value: n }
902 }
903 TokenType::HexString(val) => {
904 self.advance_clone();
905 Expression::ByteStringLiteral { value: val }
906 }
907 TokenType::True => { self.advance_clone(); Expression::BoolLiteral { value: true } }
908 TokenType::False => { self.advance_clone(); Expression::BoolLiteral { value: false } }
909 TokenType::Self_ => {
910 self.advance_clone();
911 Expression::Identifier { name: "self".to_string() }
912 }
913 TokenType::LParen => {
914 self.advance_clone();
915 let expr = self.parse_expression();
916 self.expect(&TokenType::RParen);
917 expr
918 }
919 TokenType::Ident(name) => {
920 self.advance_clone();
921 let mapped = map_rust_builtin(&name);
922 Expression::Identifier { name: mapped }
923 }
924 _ => {
925 let tok = self.current().clone();
926 self.advance_clone();
927 self.errors.push(Diagnostic::error(format!(
928 "unsupported token '{:?}' at {}:{} — not valid in Rúnar contract",
929 tok.typ, tok.line, tok.col), None));
930 Expression::Identifier { name: "unknown".to_string() }
931 }
932 }
933 }
934}
935
936fn snake_to_camel(name: &str) -> String {
941 let parts: Vec<&str> = name.split('_').collect();
942 if parts.len() <= 1 {
943 return name.to_string();
944 }
945 let mut result = parts[0].to_string();
946 for part in &parts[1..] {
947 if !part.is_empty() {
948 let mut chars = part.chars();
949 if let Some(first) = chars.next() {
950 result.push(first.to_uppercase().next().unwrap());
951 result.extend(chars);
952 }
953 }
954 }
955 result
956}
957
958fn map_rust_type(name: &str) -> String {
959 match name {
960 "Bigint" | "Int" | "i64" | "u64" | "i128" | "u128" => "bigint".to_string(),
961 "Bool" | "bool" => "boolean".to_string(),
962 _ => name.to_string(),
963 }
964}
965
966fn map_rust_builtin(name: &str) -> String {
967 match name {
970 "bool_cast" => return "bool".to_string(),
971 "verify_wots" => return "verifyWOTS".to_string(),
972 "verify_slh_dsa_sha2_128s" => return "verifySLHDSA_SHA2_128s".to_string(),
973 "verify_slh_dsa_sha2_128f" => return "verifySLHDSA_SHA2_128f".to_string(),
974 "verify_slh_dsa_sha2_192s" => return "verifySLHDSA_SHA2_192s".to_string(),
975 "verify_slh_dsa_sha2_192f" => return "verifySLHDSA_SHA2_192f".to_string(),
976 "verify_slh_dsa_sha2_256s" => return "verifySLHDSA_SHA2_256s".to_string(),
977 "verify_slh_dsa_sha2_256f" => return "verifySLHDSA_SHA2_256f".to_string(),
978 "bin_2_num" => return "bin2num".to_string(),
979 "int_2_str" => return "int2str".to_string(),
980 "to_byte_string" => return "toByteString".to_string(),
981 _ => {}
982 }
983
984 let camel = snake_to_camel(name);
985 match camel.as_str() {
988 "hash160" => "hash160".to_string(),
989 "hash256" => "hash256".to_string(),
990 "sha256" => "sha256".to_string(),
991 "ripemd160" => "ripemd160".to_string(),
992 "checkSig" => "checkSig".to_string(),
993 "checkMultiSig" => "checkMultiSig".to_string(),
994 "checkPreimage" => "checkPreimage".to_string(),
995 "verifyRabinSig" => "verifyRabinSig".to_string(),
996 "num2bin" => "num2bin".to_string(),
997 "bin2num" => "bin2num".to_string(),
998 "int2str" => "int2str".to_string(),
999 "extractLocktime" => "extractLocktime".to_string(),
1000 "extractOutputHash" => "extractOutputHash".to_string(),
1001 "extractVersion" => "extractVersion".to_string(),
1002 "extractHashPrevouts" => "extractHashPrevouts".to_string(),
1003 "extractHashSequence" => "extractHashSequence".to_string(),
1004 "extractOutpoint" => "extractOutpoint".to_string(),
1005 "extractInputIndex" => "extractInputIndex".to_string(),
1006 "extractScriptCode" => "extractScriptCode".to_string(),
1007 "extractAmount" => "extractAmount".to_string(),
1008 "extractSequence" => "extractSequence".to_string(),
1009 "extractOutputs" => "extractOutputs".to_string(),
1010 "extractSigHashType" => "extractSigHashType".to_string(),
1011 "addOutput" => "addOutput".to_string(),
1012 "reverseBytes" => "reverseBytes".to_string(),
1013 "toByteString" => "toByteString".to_string(),
1014 _ => camel,
1015 }
1016}
1017
1018pub fn parse_rust_dsl(source: &str, file_name: Option<&str>) -> ParseResult {
1023 let file = file_name.unwrap_or("contract.runar.rs").to_string();
1024 let tokens = tokenize(source);
1025 let parser = RustDslParser::new(tokens, file);
1026 parser.parse()
1027}
1028
1029#[cfg(test)]
1034mod tests {
1035 use super::*;
1036
1037 #[test]
1038 fn test_parse_basic_rust_contract() {
1039 let source = r#"
1040use runar::prelude::*;
1041
1042#[runar::contract]
1043pub struct P2PKH {
1044 pub_key_hash: Addr,
1045}
1046
1047#[runar::methods]
1048impl P2PKH {
1049 #[public]
1050 fn unlock(&self, sig: Sig, pub_key: PubKey) {
1051 assert!(hash160(pub_key) == self.pub_key_hash);
1052 assert!(check_sig(sig, pub_key));
1053 }
1054}
1055"#;
1056
1057 let result = parse_rust_dsl(source, Some("P2PKH.runar.rs"));
1058 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1059 let contract = result.contract.unwrap();
1060 assert_eq!(contract.name, "P2PKH");
1061 assert_eq!(contract.properties.len(), 1);
1062 assert_eq!(contract.methods.len(), 1);
1063 assert_eq!(contract.methods[0].name, "unlock");
1064 assert_eq!(contract.methods[0].visibility, Visibility::Public);
1065 assert_eq!(contract.methods[0].params.len(), 2);
1067 }
1068
1069 #[test]
1070 fn test_snake_to_camel_conversion() {
1071 assert_eq!(snake_to_camel("pub_key_hash"), "pubKeyHash");
1072 assert_eq!(snake_to_camel("check_sig"), "checkSig");
1073 assert_eq!(snake_to_camel("already"), "already");
1074 assert_eq!(snake_to_camel("a_b_c"), "aBC");
1075 assert_eq!(snake_to_camel("hello_world"), "helloWorld");
1076 }
1077
1078 #[test]
1079 fn test_type_mapping_works() {
1080 assert_eq!(map_rust_type("i64"), "bigint");
1082 assert_eq!(map_rust_type("u64"), "bigint");
1083 assert_eq!(map_rust_type("i128"), "bigint");
1084 assert_eq!(map_rust_type("u128"), "bigint");
1085 assert_eq!(map_rust_type("Bigint"), "bigint");
1086 assert_eq!(map_rust_type("bool"), "boolean");
1088 assert_eq!(map_rust_type("Bool"), "boolean");
1089 assert_eq!(map_rust_type("PubKey"), "PubKey");
1091 assert_eq!(map_rust_type("Sig"), "Sig");
1092 assert_eq!(map_rust_type("Addr"), "Addr");
1093 }
1094
1095 #[test]
1096 fn test_public_attribute_makes_method_public() {
1097 let source = r#"
1098use runar::prelude::*;
1099
1100#[runar::contract]
1101pub struct Test {
1102 #[readonly]
1103 x: bigint,
1104}
1105
1106#[runar::methods]
1107impl Test {
1108 #[public]
1109 fn public_method(&self, v: i64) {
1110 assert!(v == self.x);
1111 }
1112
1113 fn private_method(&self, v: i64) -> i64 {
1114 return v + 1;
1115 }
1116}
1117"#;
1118
1119 let result = parse_rust_dsl(source, Some("Test.runar.rs"));
1120 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1121 let contract = result.contract.unwrap();
1122 assert_eq!(contract.methods.len(), 2);
1123
1124 assert_eq!(contract.methods[0].name, "publicMethod");
1126 assert_eq!(contract.methods[0].visibility, Visibility::Public);
1127
1128 assert_eq!(contract.methods[1].name, "privateMethod");
1130 assert_eq!(contract.methods[1].visibility, Visibility::Private);
1131 }
1132
1133 #[test]
1134 fn test_contract_name_extracted_correctly() {
1135 let source = r#"
1136use runar::prelude::*;
1137
1138#[runar::contract]
1139pub struct MyFancyContract {
1140 value: bigint,
1141}
1142
1143#[runar::methods]
1144impl MyFancyContract {
1145 #[public]
1146 fn check(&self, v: i64) {
1147 assert!(v == self.value);
1148 }
1149}
1150"#;
1151
1152 let result = parse_rust_dsl(source, Some("MyFancyContract.runar.rs"));
1153 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1154 let contract = result.contract.unwrap();
1155 assert_eq!(contract.name, "MyFancyContract");
1156 }
1157
1158 #[test]
1159 fn test_property_names_are_camel_cased() {
1160 let source = r#"
1161use runar::prelude::*;
1162
1163#[runar::contract]
1164pub struct Test {
1165 pub_key_hash: Addr,
1166 my_value: bigint,
1167}
1168
1169#[runar::methods]
1170impl Test {
1171 #[public]
1172 fn check(&self, v: i64) {
1173 assert!(v == self.my_value);
1174 }
1175}
1176"#;
1177
1178 let result = parse_rust_dsl(source, Some("Test.runar.rs"));
1179 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1180 let contract = result.contract.unwrap();
1181 assert_eq!(contract.properties[0].name, "pubKeyHash");
1182 assert_eq!(contract.properties[1].name, "myValue");
1183 }
1184
1185 #[test]
1186 fn test_stateful_contract_attribute() {
1187 let source = r#"
1188use runar::prelude::*;
1189
1190#[runar::stateful_contract]
1191pub struct Counter {
1192 count: bigint,
1193}
1194
1195#[runar::methods]
1196impl Counter {
1197 #[public]
1198 fn increment(&mut self) {
1199 self.count = self.count + 1;
1200 }
1201}
1202"#;
1203
1204 let result = parse_rust_dsl(source, Some("Counter.runar.rs"));
1205 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1206 let contract = result.contract.unwrap();
1207 assert_eq!(contract.name, "Counter");
1208 assert_eq!(contract.parent_class, "StatefulSmartContract");
1209 }
1210
1211 #[test]
1212 fn test_constructor_auto_generated() {
1213 let source = r#"
1214use runar::prelude::*;
1215
1216#[runar::contract]
1217pub struct Test {
1218 a: bigint,
1219 b: PubKey,
1220}
1221
1222#[runar::methods]
1223impl Test {
1224 #[public]
1225 fn check(&self) {
1226 assert!(self.a > 0);
1227 }
1228}
1229"#;
1230
1231 let result = parse_rust_dsl(source, None);
1232 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1233 let contract = result.contract.unwrap();
1234 assert_eq!(contract.constructor.params.len(), 2);
1236 assert_eq!(contract.constructor.body.len(), 3);
1238 }
1239
1240 #[test]
1241 fn test_assert_eq_macro_maps_to_assert_strict_eq() {
1242 let source = r#"
1243use runar::prelude::*;
1244
1245#[runar::contract]
1246pub struct Test {
1247 #[readonly]
1248 x: bigint,
1249}
1250
1251#[runar::methods]
1252impl Test {
1253 #[public]
1254 fn check(&self, v: i64) {
1255 assert_eq!(self.x, v);
1256 }
1257}
1258"#;
1259
1260 let result = parse_rust_dsl(source, Some("Test.runar.rs"));
1261 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1262 let contract = result.contract.unwrap();
1263 let body = &contract.methods[0].body;
1264 assert_eq!(body.len(), 1);
1265
1266 if let Statement::ExpressionStatement { expression, .. } = &body[0] {
1268 if let Expression::CallExpr { callee, args } = expression {
1269 if let Expression::Identifier { name } = callee.as_ref() {
1270 assert_eq!(name, "assert");
1271 }
1272 if let Expression::BinaryExpr { op, .. } = &args[0] {
1273 assert_eq!(*op, BinaryOp::StrictEq);
1274 } else {
1275 panic!("Expected BinaryExpr inside assert_eq!, got {:?}", args[0]);
1276 }
1277 }
1278 }
1279 }
1280
1281 #[test]
1282 fn test_int_type_maps_to_bigint() {
1283 assert_eq!(map_rust_type("Int"), "bigint");
1284 }
1285
1286 #[test]
1287 fn test_bool_cast_maps_to_bool() {
1288 assert_eq!(map_rust_builtin("bool_cast"), "bool");
1289 }
1290
1291 #[test]
1292 fn test_verify_wots_mapping() {
1293 assert_eq!(map_rust_builtin("verify_wots"), "verifyWOTS");
1294 }
1295
1296 #[test]
1297 fn test_verify_slh_dsa_mappings() {
1298 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_128s"), "verifySLHDSA_SHA2_128s");
1299 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_128f"), "verifySLHDSA_SHA2_128f");
1300 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_192s"), "verifySLHDSA_SHA2_192s");
1301 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_192f"), "verifySLHDSA_SHA2_192f");
1302 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_256s"), "verifySLHDSA_SHA2_256s");
1303 assert_eq!(map_rust_builtin("verify_slh_dsa_sha2_256f"), "verifySLHDSA_SHA2_256f");
1304 }
1305
1306 #[test]
1307 fn test_extract_builtin_mappings() {
1308 assert_eq!(map_rust_builtin("extract_version"), "extractVersion");
1309 assert_eq!(map_rust_builtin("extract_hash_prevouts"), "extractHashPrevouts");
1310 assert_eq!(map_rust_builtin("extract_hash_sequence"), "extractHashSequence");
1311 assert_eq!(map_rust_builtin("extract_outpoint"), "extractOutpoint");
1312 assert_eq!(map_rust_builtin("extract_input_index"), "extractInputIndex");
1313 assert_eq!(map_rust_builtin("extract_script_code"), "extractScriptCode");
1314 assert_eq!(map_rust_builtin("extract_amount"), "extractAmount");
1315 assert_eq!(map_rust_builtin("extract_sequence"), "extractSequence");
1316 assert_eq!(map_rust_builtin("extract_output_hash"), "extractOutputHash");
1317 assert_eq!(map_rust_builtin("extract_outputs"), "extractOutputs");
1318 assert_eq!(map_rust_builtin("extract_locktime"), "extractLocktime");
1319 assert_eq!(map_rust_builtin("extract_sig_hash_type"), "extractSigHashType");
1320 }
1321
1322 #[test]
1323 fn test_byte_operation_builtin_mappings() {
1324 assert_eq!(map_rust_builtin("reverse_bytes"), "reverseBytes");
1325 assert_eq!(map_rust_builtin("bin2num"), "bin2num");
1326 assert_eq!(map_rust_builtin("bin_2_num"), "bin2num");
1327 assert_eq!(map_rust_builtin("int2str"), "int2str");
1328 assert_eq!(map_rust_builtin("int_2_str"), "int2str");
1329 assert_eq!(map_rust_builtin("to_byte_string"), "toByteString");
1330 assert_eq!(map_rust_builtin("add_output"), "addOutput");
1331 }
1332
1333 #[test]
1334 fn test_implicit_return_in_method() {
1335 let source = r#"
1336use runar::prelude::*;
1337#[runar::contract]
1338pub struct Foo { #[readonly] pub x: Bigint }
1339#[runar::methods(Foo)]
1340impl Foo {
1341 fn compute(&self, a: Bigint, b: Bigint) -> Bigint {
1342 let sum = a + b;
1343 sum
1344 }
1345 #[public]
1346 pub fn check(&self) {
1347 assert!(self.x > 0);
1348 }
1349}
1350"#;
1351 let result = parse_rust_dsl(source, Some("Foo.runar.rs"));
1352 assert!(result.errors.is_empty(), "errors: {:?}", result.errors);
1353 let contract = result.contract.unwrap();
1354 let compute = contract.methods.iter().find(|m| m.name == "compute").unwrap();
1355 assert_eq!(compute.body.len(), 2, "expected 2 statements (let + return)");
1356 match &compute.body[1] {
1358 Statement::ReturnStatement { value: Some(_), .. } => {}
1359 other => panic!("expected ReturnStatement, got {:?}", other),
1360 }
1361 }
1362}