1use std::str::FromStr;
2
3use chumsky::DefaultExpected;
4use chumsky::prelude::*;
5use chumsky::text::{Char, TextExpected};
6use chumsky::util::MaybeRef;
7use num_bigint::BigInt;
8use num_traits::Num;
9use tycho_types::boc::Boc;
10use tycho_types::cell::{CellType, HashBytes};
11use tycho_types::crc::crc_16;
12use tycho_types::prelude::{Cell, CellBuilder};
13
14pub type Span = SimpleSpan<usize>;
15
16pub fn parse(s: &'_ str) -> ParseResult<Code<'_>, ParserError> {
17 parser().parse(s)
18}
19
20#[derive(Debug, Clone)]
21pub struct Code<'a> {
22 pub span: Span,
23 pub items: Vec<Stmt<'a>>,
24}
25
26#[derive(Debug, Clone)]
27pub enum Stmt<'a> {
28 Instr(Instr<'a>),
29 Inline(Inline<'a>),
30 NewCell(#[allow(unused)] NewCell),
31 Define(Define<'a>),
32 Invalid(Span),
33}
34
35#[derive(Debug, Clone)]
36pub struct Instr<'a> {
37 pub span: Span,
38 pub ident_span: Span,
39 pub ident: &'a str,
40 pub args: Vec<InstrArg<'a>>,
41}
42
43#[derive(Debug, Clone)]
44pub struct Inline<'a> {
45 pub span: Span,
46 pub value: InstrArg<'a>,
47}
48
49#[derive(Debug, Clone)]
50pub struct Define<'a> {
51 #[allow(unused)]
52 pub span: Span,
53 pub name: DefineName<'a>,
54 pub value: InstrArg<'a>,
55}
56
57#[derive(Debug, Clone)]
58pub struct DefineName<'a> {
59 pub span: Span,
60 pub name: &'a str,
61}
62
63#[derive(Debug, Clone)]
64pub struct NewCell {
65 #[allow(unused)]
66 pub span: Span,
67}
68
69#[derive(Debug, Clone)]
70pub struct InstrArg<'a> {
71 pub span: Span,
72 pub value: InstrArgValue<'a>,
73}
74
75#[derive(Debug, Clone)]
76pub struct MethodId<'a> {
77 #[allow(unused)]
78 pub span: Span,
79 pub value: MethodIdValue<'a>,
80}
81
82#[derive(Debug, Clone)]
83pub struct MethodIdValue<'a> {
84 #[allow(unused)]
85 pub span: Span,
86 #[allow(unused)]
87 pub text: &'a str,
88 pub computed: BigInt,
89}
90
91#[derive(Debug, Clone)]
92pub struct Use<'a> {
93 pub span: Span,
94 pub value: UseValue<'a>,
95}
96
97#[derive(Debug, Clone)]
98pub struct UseValue<'a> {
99 #[allow(unused)]
100 pub span: Span,
101 pub name: &'a str,
102}
103
104#[derive(Debug, Clone)]
105pub struct JumpTable<'a> {
106 pub methods: Vec<JumpTableItem<'a>>,
107}
108
109#[derive(Debug, Clone)]
110pub struct JumpTableItem<'a> {
111 pub key: JumpTableItemKey<'a>,
112 pub value: JumpTableItemValue<'a>,
113}
114
115#[derive(Debug, Clone)]
116pub struct JumpTableItemKey<'a> {
117 pub span: Span,
118 pub data: JumpTableItemKeyData<'a>,
119}
120
121#[derive(Debug, Clone)]
122pub enum JumpTableItemKeyData<'a> {
123 Nat(BigInt),
124 MethodId(MethodId<'a>),
125 Use(Use<'a>),
126 Invalid,
127}
128
129#[derive(Debug, Clone)]
130pub struct JumpTableItemValue<'a> {
131 pub span: Span,
132 pub data: JumpTableItemValueData<'a>,
133}
134
135#[derive(Debug, Clone)]
136pub enum JumpTableItemValueData<'a> {
137 Block(Vec<Stmt<'a>>),
138 #[allow(unused)]
139 Invalid,
140}
141
142#[derive(Debug, Clone)]
143pub enum InstrArgValue<'a> {
144 Nat(BigInt),
145 SReg(i16),
146 CReg(u8),
147 Slice(Cell),
148 Lib(Cell),
149 Cell(Cell),
150 Block(Vec<Stmt<'a>>),
151 JumpTable(JumpTable<'a>),
152 MethodId(MethodId<'a>),
153 Use(Use<'a>),
154 Invalid,
155}
156
157type ParserExtra = extra::Full<ParserError, (), ()>;
158
159fn parser<'a>() -> impl Parser<'a, &'a str, Code<'a>, ParserExtra> {
160 let whitespace_or_comments = whitespace_or_comments();
161
162 whitespace_or_comments.ignore_then(
163 stmt()
164 .recover_with(via_parser(
165 any()
166 .filter(|c: &char| !c.is_whitespace())
167 .ignore_then(choice((any().ignored(), end())))
168 .map_with(|_, e| Stmt::Invalid(e.span())),
169 ))
170 .padded_by(whitespace_or_comments)
171 .repeated()
172 .collect()
173 .map_with(|items, e| Code {
174 span: e.span(),
175 items,
176 }),
177 )
178}
179
180fn stmt<'a>() -> impl Parser<'a, &'a str, Stmt<'a>, ParserExtra> {
181 recursive(|stmt| {
182 let whitespace_or_comment = whitespace_or_comments();
183
184 let inline = just("@inline")
185 .padded_by(whitespace_or_comment)
186 .ignore_then(instr_arg(stmt.clone()))
187 .map_with(|value, e| {
188 Stmt::Inline(Inline {
189 value,
190 span: e.span(),
191 })
192 });
193
194 let jumpref = just("@newcell")
195 .padded_by(whitespace_or_comment)
196 .map_with(|_, e| Stmt::NewCell(NewCell { span: e.span() }));
197
198 let define = define(stmt.clone()).map_with(|define, e| match define {
199 Some(value) => Stmt::Define(value),
200 None => Stmt::Invalid(e.span()),
201 });
202
203 let instr = instr(stmt).map(Stmt::Instr);
204
205 choice((inline, define, jumpref, instr))
206 })
207}
208
209fn define<'a>(
210 stmt: Recursive<dyn Parser<'a, &'a str, Stmt<'a>, ParserExtra> + 'a>,
211) -> impl Parser<'a, &'a str, Option<Define<'a>>, ParserExtra> + Clone {
212 just("@define(")
213 .ignore_then(
214 none_of(")")
215 .repeated()
216 .to_slice()
217 .try_map(move |s, mut span| match parse_ident_name(s, &mut span) {
218 Ok(name) => Ok(Some(DefineName { span, name })),
219 Err(e) => Err(ParserError::InvalidName {
220 span,
221 inner: e.into(),
222 }),
223 })
224 .recover_with(via_parser(until_next_arg_or_whitespace().map(|_| None))),
225 )
226 .then_ignore(just(')'))
227 .padded_by(whitespace_or_comments())
228 .then(instr_arg(stmt.clone()))
229 .map_with(|(name, value), e| {
230 name.map(|name| Define {
231 span: e.span(),
232 name,
233 value,
234 })
235 })
236}
237
238fn instr<'a>(
239 stmt: Recursive<dyn Parser<'a, &'a str, Stmt<'a>, ParserExtra> + 'a>,
240) -> impl Parser<'a, &'a str, Instr<'a>, ParserExtra> + Clone {
241 fn compute_min_span(ident_span: Span, args: &[InstrArg<'_>]) -> Span {
242 let mut res = ident_span;
243 for arg in args {
244 res.start = std::cmp::min(res.start, arg.span.start);
245 res.end = std::cmp::max(res.end, arg.span.end);
246 }
247 res
248 }
249
250 let whitespace_or_comment = whitespace_or_comments();
251
252 let args = instr_arg(stmt)
253 .separated_by(just(',').padded_by(whitespace_or_comment))
254 .collect::<Vec<_>>();
255
256 instr_ident()
257 .map_with(|ident, e| (ident, e.span()))
258 .padded_by(whitespace_or_comment)
259 .then(args)
260 .map(|((ident, ident_span), args)| Instr {
261 span: compute_min_span(ident_span, &args),
262 ident,
263 ident_span,
264 args,
265 })
266}
267
268fn instr_arg<'a>(
269 stmt: Recursive<dyn Parser<'a, &'a str, Stmt<'a>, ParserExtra> + 'a>,
270) -> impl Parser<'a, &'a str, InstrArg<'a>, ParserExtra> + Clone {
271 choice((
272 nat().map(|value| {
273 value
274 .map(InstrArgValue::Nat)
275 .unwrap_or(InstrArgValue::Invalid)
276 }),
277 just("@method")
278 .rewind()
279 .ignore_then(method_id().map(|value| {
280 value
281 .map(InstrArgValue::MethodId)
282 .unwrap_or(InstrArgValue::Invalid)
283 })),
284 just("@use").rewind().ignore_then(r#use().map(|value| {
285 value
286 .map(InstrArgValue::Use)
287 .unwrap_or(InstrArgValue::Invalid)
288 })),
289 stack_register().map(|idx| {
290 idx.map(InstrArgValue::SReg)
291 .unwrap_or(InstrArgValue::Invalid)
292 }),
293 control_register().map(|idx| {
294 idx.map(InstrArgValue::CReg)
295 .unwrap_or(InstrArgValue::Invalid)
296 }),
297 jump_table(stmt.clone()).map(InstrArgValue::JumpTable),
298 cont_block(stmt).map(InstrArgValue::Block),
299 cell_slice().map(|slice| {
300 slice
301 .map(InstrArgValue::Slice)
302 .unwrap_or(InstrArgValue::Invalid)
303 }),
304 library_cell().map(|lib| {
305 lib.map(InstrArgValue::Lib)
306 .unwrap_or(InstrArgValue::Invalid)
307 }),
308 raw_cell().map(|cell| {
309 cell.map(InstrArgValue::Cell)
310 .unwrap_or(InstrArgValue::Invalid)
311 }),
312 ))
313 .map_with(|value, e| InstrArg {
314 value,
315 span: e.span(),
316 })
317}
318
319fn instr_ident<'a>() -> impl Parser<'a, &'a str, &'a str, ParserExtra> + Clone {
320 until_next_arg_or_whitespace()
321 .at_least(1)
322 .to_slice()
323 .validate(|ident: &str, e, emitter| {
324 let invalid_char = 'char: {
325 let mut chars = ident.chars().peekable();
326 let Some(first) = chars.peek() else {
327 break 'char None;
328 };
329
330 if *first == '-' {
331 chars.next();
332 }
333
334 let Some(first) = chars.peek() else {
335 break 'char None;
336 };
337
338 if first.is_ascii_digit() {
339 chars.next();
340 let Some(second) = chars.next() else {
341 break 'char None;
342 };
343 if !second.is_ascii_uppercase() {
344 break 'char Some(second);
345 }
346 }
347
348 for c in chars {
349 if !(c.is_ascii_uppercase()
350 || c.is_ascii_digit()
351 || c == '#'
352 || c == '_'
353 || c == ':')
354 {
355 break 'char Some(c);
356 }
357 }
358
359 return ident;
360 };
361
362 emitter.emit(ParserError::ExpectedFound {
363 span: e.span(),
364 found: invalid_char,
365 });
366
367 "#INVALID#"
368 })
369}
370
371fn nat<'a>() -> impl Parser<'a, &'a str, Option<BigInt>, ParserExtra> + Clone {
372 fn parse_int(mut s: &str, radix: u32, span: Span) -> Result<BigInt, ParserError> {
373 if !s.is_empty() {
374 s = s.trim_start_matches('0');
375 if s.is_empty() {
376 s = "0";
377 }
378 }
379
380 let in_range = match radix {
381 2 => s.len() <= 256,
382 10 => s.len() <= 78,
383 16 => s.len() <= 64,
384 _ => return Err(ParserError::UnknownError),
385 };
386
387 if !in_range {
388 return Err(ParserError::TooBigInteger { span });
389 }
390
391 match BigInt::from_str_radix(s, radix) {
392 Ok(n) => Ok(n),
393 Err(e) => Err(ParserError::InvalidInt { span, inner: e }),
394 }
395 }
396
397 let num_slice = until_next_arg_or_whitespace().at_least(1).to_slice();
398
399 let after_decimal =
400 choice((end(), any().filter(|c: &char| !c.is_alphabetic()).ignored())).rewind();
401
402 let number = choice((
403 just("0x").ignore_then(num_slice).validate(|s, e, emitter| {
404 match parse_int(s, 16, e.span()) {
405 Ok(res) => Some(res),
406 Err(e) => {
407 emitter.emit(e);
408 None
409 }
410 }
411 }),
412 just("0b").ignore_then(num_slice).validate(|s, e, emitter| {
413 match parse_int(s, 2, e.span()) {
414 Ok(res) => Some(res),
415 Err(e) => {
416 emitter.emit(e);
417 None
418 }
419 }
420 }),
421 any()
422 .filter(|c: &char| c.is_ascii_digit())
423 .repeated()
424 .at_least(1)
425 .to_slice()
426 .then_ignore(after_decimal)
427 .validate(|s, e, emitter| match parse_int(s, 10, e.span()) {
428 Ok(res) => Some(res),
429 Err(e) => {
430 emitter.emit(e);
431 None
432 }
433 }),
434 ));
435
436 choice((
437 just('-')
438 .ignore_then(number)
439 .map(|value| value.map(std::ops::Neg::neg)),
440 number,
441 ))
442}
443
444fn stack_register<'a>() -> impl Parser<'a, &'a str, Option<i16>, ParserExtra> + Clone {
445 let until_eof_or_paren = none_of(")\n").repeated().then(just(')').or_not());
446
447 let idx = text::int::<_, ParserExtra>(10).try_map(|s, span| match i16::from_str(s) {
448 Ok(n) => Ok(n),
449 Err(e) => Err(ParserError::InvalidStackRegister {
450 span,
451 inner: e.into(),
452 }),
453 });
454
455 just('s').ignore_then(
456 choice((
457 just('(').ignore_then(
458 just('-')
459 .ignore_then(idx)
460 .map(|idx| Some(-idx))
461 .then_ignore(just(')'))
462 .recover_with(via_parser(until_eof_or_paren.map(|_| None))),
463 ),
464 idx.map(Some),
465 ))
466 .recover_with(via_parser(until_next_arg_or_whitespace().map(|_| None))),
467 )
468}
469
470fn control_register<'a>() -> impl Parser<'a, &'a str, Option<u8>, ParserExtra> + Clone {
471 let idx = text::int::<_, ParserExtra>(10)
472 .try_map(|s, span| match u8::from_str(s) {
473 Ok(n) if (0..=5).contains(&n) || n == 7 => Ok(Some(n)),
474 Ok(n) => Err(ParserError::InvalidControlRegister {
475 span,
476 inner: ControlRegisterError::OutOfRange(n).into(),
477 }),
478 Err(e) => Err(ParserError::InvalidControlRegister {
479 span,
480 inner: e.into(),
481 }),
482 })
483 .recover_with(via_parser(until_next_arg_or_whitespace().map(|_| None)));
484
485 just('c').ignore_then(idx)
486}
487
488fn cont_block<'a>(
489 stmt: Recursive<dyn Parser<'a, &'a str, Stmt<'a>, ParserExtra> + 'a>,
490) -> impl Parser<'a, &'a str, Vec<Stmt<'a>>, ParserExtra> + Clone {
491 stmt.padded().repeated().collect().delimited_by(
492 just('{').padded(),
493 just('}')
494 .ignored()
495 .recover_with(via_parser(end()))
496 .recover_with(skip_then_retry_until(any().ignored(), end())),
497 )
498}
499
500fn jump_table<'a>(
501 stmt: Recursive<dyn Parser<'a, &'a str, Stmt<'a>, ParserExtra> + 'a>,
502) -> impl Parser<'a, &'a str, JumpTable<'a>, ParserExtra> + Clone {
503 let key = choice((
504 nat().map(|value| {
505 value
506 .map(JumpTableItemKeyData::Nat)
507 .unwrap_or(JumpTableItemKeyData::Invalid)
508 }),
509 just("@method")
510 .rewind()
511 .ignore_then(method_id().map(|value| {
512 value
513 .map(JumpTableItemKeyData::MethodId)
514 .unwrap_or(JumpTableItemKeyData::Invalid)
515 })),
516 just("@use").rewind().ignore_then(r#use().map(|value| {
517 value
518 .map(JumpTableItemKeyData::Use)
519 .unwrap_or(JumpTableItemKeyData::Invalid)
520 })),
521 ))
522 .map_with(|data, e| JumpTableItemKey {
523 data,
524 span: e.span(),
525 });
526
527 let value = cont_block(stmt).map_with(|value, e| JumpTableItemValue {
528 span: e.span(),
529 data: JumpTableItemValueData::Block(value),
530 });
531
532 let item = key
533 .then_ignore(just("=>").padded())
534 .then(value)
535 .map(|(key, value)| JumpTableItem { key, value });
536
537 item.padded()
538 .repeated()
539 .collect()
540 .delimited_by(
541 just('[').padded(),
542 just(']')
543 .ignored()
544 .recover_with(via_parser(end()))
545 .recover_with(skip_then_retry_until(any().ignored(), end())),
546 )
547 .map_with(|methods: Vec<JumpTableItem<'_>>, _e| JumpTable { methods })
548}
549
550fn library_cell<'a>() -> impl Parser<'a, &'a str, Option<Cell>, ParserExtra> + Clone {
551 just("@{")
552 .ignore_then(any().filter(|&c: &char| c != '}').repeated().to_slice())
553 .then(choice((
554 just('}').map_with(|_, e| (true, e.span())),
555 end().map_with(|_, e| (false, e.span())),
556 )))
557 .validate(|(s, (terminated, end_span)), e, emitter| {
558 if !terminated {
559 emitter.emit(ParserError::ExpectedFound {
560 span: end_span,
561 found: None,
562 });
563 return None;
564 }
565 match parse_library_ref(s) {
566 Ok(lib) => Some(lib),
567 Err(err) => {
568 emitter.emit(ParserError::InvalidLibrary {
569 span: e.span(),
570 inner: err.into(),
571 });
572 None
573 }
574 }
575 })
576}
577
578fn raw_cell<'a>() -> impl Parser<'a, &'a str, Option<Cell>, ParserExtra> + Clone {
579 just("te6ccg").rewind().ignore_then(
580 any()
581 .filter(|&c: &char| match c {
582 '{' | '}' | '(' | ')' | ',' => false,
583 _ => !c.is_whitespace(),
584 })
585 .repeated()
586 .to_slice()
587 .try_map(move |s, span| match parse_cell_boc(s) {
588 Ok(cell) => Ok(Some(cell)),
589 Err(e) => Err(ParserError::InvalidCell {
590 span,
591 inner: e.into(),
592 }),
593 })
594 .recover_with(via_parser(until_next_arg_or_whitespace().map(|_| None))),
595 )
596}
597
598fn method_id<'a>() -> impl Parser<'a, &'a str, Option<MethodId<'a>>, ParserExtra> + Clone {
599 let until_next_arg_or_whitespace = until_next_arg_or_whitespace();
600
601 just("@method(")
602 .ignore_then(
603 none_of(")")
604 .repeated()
605 .to_slice()
606 .try_map(move |s, mut span| match parse_ident_name(s, &mut span) {
607 Ok(text) => Ok(Some(MethodIdValue {
608 span,
609 text,
610 computed: {
611 let crc = crc_16(text.as_bytes());
612 BigInt::from(crc as u32 | 0x10000)
613 },
614 })),
615 Err(e) => Err(ParserError::InvalidMethodId {
616 span,
617 inner: e.into(),
618 }),
619 })
620 .recover_with(via_parser(until_next_arg_or_whitespace.map(|_| None))),
621 )
622 .then_ignore(just(')'))
623 .map_with(|value, e| {
624 value.map(|value| MethodId {
625 span: e.span(),
626 value,
627 })
628 })
629 .recover_with(via_parser(until_next_arg_or_whitespace.map(|_| None)))
630}
631
632fn r#use<'a>() -> impl Parser<'a, &'a str, Option<Use<'a>>, ParserExtra> + Clone {
633 let until_next_arg_or_eof = until_next_arg_or_whitespace();
634
635 just("@use(")
636 .ignore_then(
637 none_of(")")
638 .repeated()
639 .to_slice()
640 .try_map(move |s, mut span| match parse_ident_name(s, &mut span) {
641 Ok(name) => Ok(Some(UseValue { span, name })),
642 Err(e) => Err(ParserError::InvalidMethodId {
643 span,
644 inner: e.into(),
645 }),
646 })
647 .recover_with(via_parser(until_next_arg_or_eof.map(|_| None))),
648 )
649 .then_ignore(just(')'))
650 .map_with(|value, e| {
651 value.map(|value| Use {
652 span: e.span(),
653 value,
654 })
655 })
656 .recover_with(via_parser(until_next_arg_or_eof.map(|_| None)))
657}
658
659fn cell_slice<'a>() -> impl Parser<'a, &'a str, Option<Cell>, ParserExtra> + Clone {
660 let content_recovery = any()
661 .filter(|&c: &char| match c {
662 '}' | '/' => false,
663 _ => !c.is_whitespace(),
664 })
665 .repeated();
666
667 let braces_recovery = none_of("}\n").repeated().then(just('}').or_not());
668
669 let make_slice_parser =
670 |prefix: &'static str, parser: fn(&'a str) -> Result<Cell, SliceError>| {
671 just(prefix)
672 .ignore_then(
673 any()
674 .filter(|&c: &char| c != '}' && !c.is_whitespace())
675 .repeated()
676 .to_slice()
677 .try_map(move |s, span| match (parser)(s) {
678 Ok(s) => Ok(Some(s)),
679 Err(e) => Err(ParserError::InvalidSlice {
680 span,
681 inner: e.into(),
682 }),
683 })
684 .recover_with(via_parser(content_recovery.map(|_| None))),
685 )
686 .then(
687 just('}')
688 .map(|_| true)
689 .recover_with(via_parser(braces_recovery.map(|_| false))),
690 )
691 .map(|(mut t, valid)| {
692 if !valid {
693 t = None;
694 }
695 t
696 })
697 };
698
699 choice((
700 make_slice_parser("x{", parse_hex_slice),
701 make_slice_parser("b{", parse_bin_slice),
702 ))
703}
704
705fn whitespace_or_comments<'a>() -> impl Parser<'a, &'a str, (), ParserExtra> + Copy {
706 choice((
707 any().filter(|c: &char| c.is_whitespace()).ignored(),
708 just("//")
709 .then(any().filter(|c: &char| !c.is_newline()).repeated())
710 .ignored(),
711 ))
712 .repeated()
713}
714
715fn until_next_arg_or_whitespace<'a>() -> chumsky::combinator::Repeated<
716 impl Parser<'a, &'a str, char, ParserExtra> + Copy,
717 char,
718 &'a str,
719 ParserExtra,
720> {
721 any()
722 .filter(|&c: &char| match c {
723 '{' | '}' | '(' | ')' | ',' | '/' | '@' => false,
724 _ => !c.is_whitespace(),
725 })
726 .repeated()
727}
728
729fn parse_hex_slice(s: &str) -> Result<Cell, SliceError> {
730 fn hex_char(c: u8) -> Result<u8, SliceError> {
731 match c {
732 b'A'..=b'F' => Ok(c - b'A' + 10),
733 b'a'..=b'f' => Ok(c - b'a' + 10),
734 b'0'..=b'9' => Ok(c - b'0'),
735 _ => Err(SliceError::InvalidHex(c as char)),
736 }
737 }
738
739 if !s.is_ascii() {
740 return Err(SliceError::NonAscii);
741 }
742
743 let s = s.as_bytes();
744 let (mut s, with_tag) = match s.strip_suffix(b"_") {
745 Some(s) => (s, true),
746 None => (s, false),
747 };
748
749 let mut half_byte = None;
750 if s.len() % 2 != 0 {
751 if let Some((last, prefix)) = s.split_last() {
752 half_byte = Some(hex_char(*last)?);
753 s = prefix;
754 }
755 }
756
757 if s.len() > 128 * 2 {
758 return Err(SliceError::TooLong);
759 }
760
761 let mut builder = CellBuilder::new();
762
763 let mut bytes = hex::decode(s)?;
764
765 let mut bits = bytes.len() as u16 * 8;
766 if let Some(half_byte) = half_byte {
767 bits += 4;
768 bytes.push(half_byte << 4);
769 }
770
771 if with_tag {
772 bits = bytes.len() as u16 * 8;
773 for byte in bytes.iter().rev() {
774 if *byte == 0 {
775 bits -= 8;
776 } else {
777 bits -= 1 + byte.trailing_zeros() as u16;
778 break;
779 }
780 }
781 }
782
783 builder.store_raw(&bytes, bits)?;
784 builder.build().map_err(SliceError::CellError)
785}
786
787fn parse_bin_slice(s: &str) -> Result<Cell, SliceError> {
788 use tycho_types::cell::MAX_BIT_LEN;
789
790 let mut bits = 0;
791 let mut bytes = [0; 128];
792
793 for char in s.chars() {
794 let value = match char {
795 '0' => 0u8,
796 '1' => 1,
797 c => return Err(SliceError::InvalidBin(c)),
798 };
799 bytes[bits / 8] |= value << (7 - bits % 8);
800
801 bits += 1;
802 if bits > MAX_BIT_LEN as usize {
803 return Err(SliceError::TooLong);
804 }
805 }
806
807 let mut builder = CellBuilder::new();
808 builder.store_raw(&bytes, bits as _)?;
809 builder.build().map_err(SliceError::CellError)
810}
811
812fn parse_cell_boc(s: &str) -> Result<Cell, CellError> {
813 Boc::decode_base64(s).map_err(Into::into)
814}
815
816fn parse_library_ref(s: &str) -> Result<Cell, LibraryError> {
817 let hash = s.parse::<HashBytes>()?;
818 let mut b = CellBuilder::new();
819 b.set_exotic(true);
820 b.store_u8(CellType::LibraryReference.to_byte())?;
821 b.store_u256(&hash)?;
822 b.build().map_err(LibraryError::CellError)
823}
824
825fn parse_ident_name<'a>(mut s: &'a str, span: &mut Span) -> Result<&'a str, NameError> {
826 let mut original = s;
828 s = s.trim_start();
829 span.start += original.len() - s.len();
830
831 original = s;
833 s = s.trim_end();
834 span.end -= original.len() - s.len();
835
836 let mut chars = s.chars();
838 let Some(first) = chars.next() else {
839 return Err(NameError::Empty);
840 };
841 if !first.is_ascii_alphabetic() {
842 return Err(NameError::InvalidPrefix);
843 }
844 for c in chars {
845 if !c.is_ascii_alphanumeric() && c != '_' {
846 return Err(NameError::UnexpectedChar(c));
847 }
848 }
849 Ok(s)
850}
851
852#[derive(thiserror::Error, Debug)]
853pub enum ParserError {
854 #[error("unexpected character found: {found:?}")]
855 ExpectedFound { span: Span, found: Option<char> },
856 #[error("invalid int: {inner}")]
857 InvalidInt {
858 span: Span,
859 inner: num_bigint::ParseBigIntError,
860 },
861 #[error("too big integer")]
862 TooBigInteger { span: Span },
863 #[error("invalid stack register: {inner}")]
864 InvalidStackRegister { span: Span, inner: anyhow::Error },
865 #[error("invalid control register: {inner}")]
866 InvalidControlRegister { span: Span, inner: anyhow::Error },
867 #[error("invalid slice: {inner}")]
868 InvalidSlice { span: Span, inner: anyhow::Error },
869 #[error("invalid cell: {inner}")]
870 InvalidCell { span: Span, inner: anyhow::Error },
871 #[error("invalid library cell: {inner}")]
872 InvalidLibrary { span: Span, inner: anyhow::Error },
873 #[error("invalid method id: {inner}")]
874 InvalidMethodId { span: Span, inner: anyhow::Error },
875 #[error("invalid name: {inner}")]
876 InvalidName { span: Span, inner: anyhow::Error },
877 #[error("unknown error")]
878 UnknownError,
879}
880
881impl ParserError {
882 pub fn span(&self) -> Option<Span> {
883 match self {
884 Self::ExpectedFound { span, .. }
885 | Self::InvalidInt { span, .. }
886 | Self::TooBigInteger { span }
887 | Self::InvalidStackRegister { span, .. }
888 | Self::InvalidControlRegister { span, .. }
889 | Self::InvalidSlice { span, .. }
890 | Self::InvalidCell { span, .. }
891 | Self::InvalidLibrary { span, .. }
892 | Self::InvalidMethodId { span, .. }
893 | Self::InvalidName { span, .. } => Some(*span),
894 Self::UnknownError => None,
895 }
896 }
897}
898
899#[derive(thiserror::Error, Debug)]
900enum ControlRegisterError {
901 #[error("control register `c{0}` is out of range")]
902 OutOfRange(u8),
903}
904
905#[derive(thiserror::Error, Debug)]
906enum SliceError {
907 #[error("non-ascii characters in bitstring")]
908 NonAscii,
909 #[error("unexpected char `{0}` in hex bitstring")]
910 InvalidHex(char),
911 #[error("invalid hex bitstring: {0}")]
912 InvalidHexFull(#[from] hex::FromHexError),
913 #[error("unexpected char `{0}` in binary bitstring")]
914 InvalidBin(char),
915 #[error("bitstring is too long")]
916 TooLong,
917 #[error("cell build error: {0}")]
918 CellError(#[from] tycho_types::error::Error),
919}
920
921#[derive(thiserror::Error, Debug)]
922#[error("invalid cell BOC: {0}")]
923struct CellError(#[from] tycho_types::boc::de::Error);
924
925#[derive(thiserror::Error, Debug)]
926pub enum NameError {
927 #[error("name cannot be empty")]
928 Empty,
929 #[error("name must start with a letter")]
930 InvalidPrefix,
931 #[error("unexpected char `{0}` in a name")]
932 UnexpectedChar(char),
933}
934
935#[derive(thiserror::Error, Debug)]
936enum LibraryError {
937 #[error("invalid hash: {0}")]
938 InvalidHexFull(#[from] tycho_types::error::ParseHashBytesError),
939 #[error("cell build error: {0}")]
940 CellError(#[from] tycho_types::error::Error),
941}
942
943impl<'a> chumsky::error::LabelError<'a, &'a str, MaybeRef<'a, char>> for ParserError {
944 fn expected_found<Iter: IntoIterator<Item = MaybeRef<'a, char>>>(
945 _: Iter,
946 found: Option<MaybeRef<'a, char>>,
947 span: Span,
948 ) -> Self {
949 Self::ExpectedFound {
950 span,
951 found: found.as_deref().copied(),
952 }
953 }
954}
955
956impl<'a> chumsky::error::LabelError<'a, &'a str, TextExpected<'a, &'a str>> for ParserError {
957 fn expected_found<Iter: IntoIterator<Item = TextExpected<'a, &'a str>>>(
958 _: Iter,
959 found: Option<MaybeRef<'a, char>>,
960 span: Span,
961 ) -> Self {
962 Self::ExpectedFound {
963 span,
964 found: found.as_deref().copied(),
965 }
966 }
967}
968
969impl<'a> chumsky::error::LabelError<'a, &'a str, DefaultExpected<'a, char>> for ParserError {
970 fn expected_found<Iter: IntoIterator<Item = DefaultExpected<'a, char>>>(
971 _: Iter,
972 found: Option<MaybeRef<'a, char>>,
973 span: Span,
974 ) -> Self {
975 Self::ExpectedFound {
976 span,
977 found: found.as_deref().copied(),
978 }
979 }
980}
981
982impl<'a> chumsky::error::Error<'a, &'a str> for ParserError {
983 fn merge(self, _: Self) -> Self {
984 self
985 }
986}
987
988#[cfg(test)]
989mod tests {
990 use super::*;
991
992 #[test]
993 fn empty_asm() {
994 assert!(parse("").unwrap().items.is_empty());
995 }
996
997 #[test]
998 fn simple_asm() {
999 const CODE: &str = r#"
1000 PUSHCONT {
1001 PUSHREF x{afff_}
1002 PUSH s(-1)
1003 OVER
1004 LESSINT 2
1005 PUSHCONT {
1006 2DROP
1007 PUSHINT 1
1008 }
1009 IFJMP
1010 OVER
1011 DEC
1012 SWAP
1013 DUP
1014 EXECUTE
1015 MUL
1016 }
1017 DUP
1018 JMPX
1019 "#;
1020
1021 let (output, errors) = parse(CODE).into_output_errors();
1022 println!("OUTPUT: {:#?}", output);
1023 println!("ERRORS: {:#?}", errors);
1024
1025 assert!(output.is_some());
1026 assert!(errors.is_empty());
1027 }
1028
1029 #[test]
1030 fn example_asm() {
1031 const CODE: &str = include_str!("tests/example.tvm");
1032
1033 let (output, errors) = parse(CODE).into_output_errors();
1034 println!("OUTPUT: {:#?}", output);
1035 println!("ERRORS: {:#?}", errors);
1036
1037 assert!(output.is_some());
1038 assert!(errors.is_empty());
1039 }
1040
1041 #[test]
1042 fn complex_asm() {
1043 const CODE: &str = include_str!("tests/walletv3.tvm");
1044
1045 let (output, errors) = parse(CODE).into_output_errors();
1046 println!("OUTPUT: {:#?}", output);
1047 println!("ERRORS: {:#?}", errors);
1048
1049 assert!(output.is_some());
1050 assert!(errors.is_empty());
1051 }
1052
1053 #[test]
1054 fn strange_opcodes() {
1055 let (output, errors) = parse(
1056 r#"
1057 NOP
1058 2DROP
1059 OVER
1060 LESSINT 2
1061 "#,
1062 )
1063 .into_output_errors();
1064 println!("OUTPUT: {:#?}", output);
1065 println!("ERRORS: {:#?}", errors);
1066
1067 assert!(output.is_some());
1068 assert!(errors.is_empty());
1069 }
1070
1071 #[test]
1072 fn library_cells() {
1073 let (output, errors) = parse(
1074 r#"
1075 PUSHREF @{aabbaaccaabbaaccaabbaaccaabbaaccaabbaaccaabbaaccaabbaaccaabbaacc}
1076 "#,
1077 )
1078 .into_output_errors();
1079 println!("OUTPUT: {:#?}", output);
1080 println!("ERRORS: {:#?}", errors);
1081
1082 assert!(output.is_some());
1083 assert!(errors.is_empty());
1084 }
1085}