1use pest::Parser;
4use pest_derive::Parser;
5
6use crate::counting::CountingFlags;
7use crate::definition::{
8 AddressDef, AddressPath, ContainerDef, ExternalFnDef, GlobalVarDef, LineEntry, ListDef,
9 ListItemDef, ScopeLineTable, SlotInfo, SourceLocation,
10};
11use crate::id::{DefinitionId, NameId};
12use crate::line::{LineContent, LinePart, PluralCategory, SelectKey};
13use crate::opcode::{ChoiceFlags, Opcode, SequenceKind};
14use crate::story::StoryData;
15use crate::value::{ListValue, Value, ValueType};
16
17#[derive(Parser)]
18#[grammar = "inkt/inkt.pest"]
19struct InktParser;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct InktParseError {
24 pub message: String,
25 pub line: usize,
26 pub col: usize,
27}
28
29impl core::fmt::Display for InktParseError {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 write!(f, "{}:{}: {}", self.line, self.col, self.message)
32 }
33}
34
35impl std::error::Error for InktParseError {}
36
37pub fn read_inkt(input: &str) -> Result<StoryData, InktParseError> {
39 let pairs = InktParser::parse(Rule::story, input).map_err(|e| {
40 let (line, col) = match e.line_col {
41 pest::error::LineColLocation::Pos(pos) => pos,
42 pest::error::LineColLocation::Span(start, _) => start,
43 };
44 InktParseError {
45 message: e.to_string(),
46 line,
47 col,
48 }
49 })?;
50
51 let story_pair = pairs.into_iter().next().ok_or_else(|| InktParseError {
52 message: "no story node".into(),
53 line: 1,
54 col: 1,
55 })?;
56
57 parse_story(story_pair)
58}
59
60type P<'a> = pest::iterators::Pair<'a, Rule>;
61
62fn err(pair: &P<'_>, msg: impl Into<String>) -> InktParseError {
63 let (line, col) = pair.line_col();
64 InktParseError {
65 message: msg.into(),
66 line,
67 col,
68 }
69}
70
71fn parse_story(pair: P<'_>) -> Result<StoryData, InktParseError> {
72 let mut name_table = Vec::new();
73 let mut variables = Vec::new();
74 let mut list_defs = Vec::new();
75 let mut list_items = Vec::new();
76 let mut externals = Vec::new();
77 let mut addresses = Vec::new();
78 let mut address_paths = Vec::new();
79 let mut containers = Vec::new();
80 let mut line_tables = Vec::new();
81 let mut list_literals = Vec::new();
82 let mut source_checksum = 0u32;
83
84 for inner in pair.into_inner() {
85 match inner.as_rule() {
86 Rule::story_checksum => {
87 if let Some(hex_pair) = inner.into_inner().next() {
88 source_checksum = parse_hex_u32(hex_pair.as_str());
89 }
90 }
91 Rule::name_table => name_table = parse_name_table(inner)?,
92 Rule::globals => variables = parse_globals(inner)?,
93 Rule::lists => list_defs = parse_lists(inner)?,
94 Rule::list_items => list_items = parse_list_items(inner)?,
95 Rule::externals => externals = parse_externals(inner)?,
96 Rule::addresses => addresses = parse_addresses(inner)?,
97 Rule::address_paths => address_paths = parse_address_paths(inner)?,
98 Rule::list_literals => list_literals = parse_list_literals(inner)?,
99 Rule::container => {
100 let (container, lt) = parse_container(inner)?;
101 let is_scope_owner = container.scope_id == container.id;
102 containers.push(container);
103 if is_scope_owner {
106 line_tables.push(lt);
107 }
108 }
109 _ => {}
110 }
111 }
112
113 line_tables.sort_by_key(|lt| lt.scope_id.to_raw());
116
117 Ok(StoryData {
118 containers,
119 line_tables,
120 variables,
121 list_defs,
122 list_items,
123 externals,
124 addresses,
125 address_paths,
126 name_table,
127 list_literals,
128 source_checksum,
129 })
130}
131
132fn parse_name_table(pair: P<'_>) -> Result<Vec<String>, InktParseError> {
135 let mut names = Vec::new();
136 for entry in pair.into_inner() {
137 if entry.as_rule() == Rule::name_entry {
138 let mut inner = entry.into_inner();
139 let _index = inner.next(); let s = inner.next().ok_or_else(|| InktParseError {
141 message: "expected string in name_entry".into(),
142 line: 0,
143 col: 0,
144 })?;
145 names.push(unescape_string(s.as_str()));
146 }
147 }
148 Ok(names)
149}
150
151fn parse_globals(pair: P<'_>) -> Result<Vec<GlobalVarDef>, InktParseError> {
154 let mut vars = Vec::new();
155 for entry in pair.into_inner() {
156 if entry.as_rule() == Rule::global_entry {
157 vars.push(parse_global_entry(entry)?);
158 }
159 }
160 Ok(vars)
161}
162
163fn parse_global_entry(pair: P<'_>) -> Result<GlobalVarDef, InktParseError> {
164 let mut inner = pair.into_inner();
165 let id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
166 message: "expected def_id in global".into(),
167 line: 0,
168 col: 0,
169 })?)?;
170
171 let type_pair = inner.next().ok_or_else(|| InktParseError {
172 message: "expected type_name in global".into(),
173 line: 0,
174 col: 0,
175 })?;
176 let value_type = parse_value_type(type_pair)?;
177
178 let value_pair = inner.next().ok_or_else(|| InktParseError {
179 message: "expected value in global".into(),
180 line: 0,
181 col: 0,
182 })?;
183 let default_value = parse_value(value_pair, Some(value_type))?;
184
185 let mut mutable = false;
186 let mut name = NameId(0);
187
188 for remaining in inner {
189 match remaining.as_rule() {
190 Rule::mutable_flag => mutable = true,
191 Rule::integer => {
192 name = NameId(parse_u16(&remaining)?);
193 }
194 _ => {}
195 }
196 }
197
198 Ok(GlobalVarDef {
199 id,
200 name,
201 value_type,
202 default_value,
203 mutable,
204 })
205}
206
207#[expect(clippy::needless_pass_by_value)]
208fn parse_value_type(pair: P<'_>) -> Result<ValueType, InktParseError> {
209 let s = pair.as_str();
210 match s {
211 "int" => Ok(ValueType::Int),
212 "float" => Ok(ValueType::Float),
213 "bool" => Ok(ValueType::Bool),
214 "string" => Ok(ValueType::String),
215 "list" => Ok(ValueType::List),
216 "divert_target" => Ok(ValueType::DivertTarget),
217 "var_pointer" => Ok(ValueType::VariablePointer),
218 "temp_pointer" => Ok(ValueType::TempPointer),
219 "fragment_ref" => Ok(ValueType::FragmentRef),
220 "null" => Ok(ValueType::Null),
221 _ => Err(err(&pair, format!("unknown value type: {s}"))),
222 }
223}
224
225fn parse_value(pair: P<'_>, type_hint: Option<ValueType>) -> Result<Value, InktParseError> {
226 let inner = pair.into_inner().next().ok_or_else(|| InktParseError {
227 message: "empty value".into(),
228 line: 0,
229 col: 0,
230 })?;
231
232 match inner.as_rule() {
233 Rule::integer => {
234 match type_hint {
237 Some(ValueType::Float) => {
238 let n: f32 = inner
239 .as_str()
240 .parse()
241 .map_err(|_| err(&inner, "invalid float"))?;
242 Ok(Value::Float(n))
243 }
244 Some(ValueType::Bool) => {
245 let n: i32 = inner
246 .as_str()
247 .parse()
248 .map_err(|_| err(&inner, "invalid integer"))?;
249 Ok(Value::Bool(n != 0))
250 }
251 _ => {
252 let n: i32 = inner
253 .as_str()
254 .parse()
255 .map_err(|_| err(&inner, "invalid integer"))?;
256 Ok(Value::Int(n))
257 }
258 }
259 }
260 Rule::float => {
261 let n: f32 = inner
262 .as_str()
263 .parse()
264 .map_err(|_| err(&inner, "invalid float"))?;
265 Ok(Value::Float(n))
266 }
267 Rule::bool_value => Ok(Value::Bool(inner.as_str() == "true")),
268 Rule::string => Ok(Value::String(unescape_string(inner.as_str()).into())),
269 Rule::def_id => Ok(Value::DivertTarget(parse_def_id(inner)?)),
270 Rule::null_value => Ok(Value::Null),
271 Rule::list_value => parse_list_value(inner),
272 Rule::var_pointer_value => {
273 let id_pair = inner.into_inner().next().ok_or_else(|| InktParseError {
274 message: "expected def_id in var_pointer".into(),
275 line: 0,
276 col: 0,
277 })?;
278 Ok(Value::VariablePointer(parse_def_id(id_pair)?))
279 }
280 Rule::fragment_ref_value => {
281 let idx_pair = inner.into_inner().next().ok_or_else(|| InktParseError {
282 message: "expected integer in fragment_ref".into(),
283 line: 0,
284 col: 0,
285 })?;
286 let idx: u32 = idx_pair.as_str().parse().map_err(|_| InktParseError {
287 message: "invalid fragment_ref index".into(),
288 line: 0,
289 col: 0,
290 })?;
291 Ok(Value::FragmentRef(idx))
292 }
293 _ => Err(err(
294 &inner,
295 format!("unexpected value rule: {:?}", inner.as_rule()),
296 )),
297 }
298}
299
300fn parse_list_value(pair: P<'_>) -> Result<Value, InktParseError> {
301 let mut items = Vec::new();
302 let mut origins = Vec::new();
303
304 for child in pair.into_inner() {
305 match child.as_rule() {
306 Rule::list_value_items => {
307 for def_pair in child.into_inner() {
308 if def_pair.as_rule() == Rule::def_id {
309 items.push(parse_def_id(def_pair)?);
310 }
311 }
312 }
313 Rule::list_value_origins => {
314 for def_pair in child.into_inner() {
315 if def_pair.as_rule() == Rule::def_id {
316 origins.push(parse_def_id(def_pair)?);
317 }
318 }
319 }
320 _ => {}
321 }
322 }
323
324 Ok(Value::List(ListValue { items, origins }.into()))
325}
326
327fn parse_lists(pair: P<'_>) -> Result<Vec<ListDef>, InktParseError> {
330 let mut defs = Vec::new();
331 for entry in pair.into_inner() {
332 if entry.as_rule() == Rule::list_entry {
333 defs.push(parse_list_entry(entry)?);
334 }
335 }
336 Ok(defs)
337}
338
339fn parse_list_entry(pair: P<'_>) -> Result<ListDef, InktParseError> {
340 let mut inner = pair.into_inner();
341 let id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
342 message: "expected def_id in list".into(),
343 line: 0,
344 col: 0,
345 })?)?;
346
347 let name_int = inner.next().ok_or_else(|| InktParseError {
348 message: "expected name integer in list".into(),
349 line: 0,
350 col: 0,
351 })?;
352 let name = NameId(parse_u16(&name_int)?);
353
354 let mut items = Vec::new();
355 for remaining in inner {
356 if remaining.as_rule() == Rule::list_item_inline {
357 let mut li_inner = remaining.into_inner();
358 let item_name_id = parse_u16(&li_inner.next().ok_or_else(|| InktParseError {
359 message: "expected name in list item".into(),
360 line: 0,
361 col: 0,
362 })?)?;
363 let ordinal: i32 = li_inner
364 .next()
365 .ok_or_else(|| InktParseError {
366 message: "expected ordinal in list item".into(),
367 line: 0,
368 col: 0,
369 })?
370 .as_str()
371 .parse()
372 .map_err(|_| InktParseError {
373 message: "invalid ordinal".into(),
374 line: 0,
375 col: 0,
376 })?;
377 items.push((NameId(item_name_id), ordinal));
378 }
379 }
380
381 Ok(ListDef { id, name, items })
382}
383
384fn parse_list_items(pair: P<'_>) -> Result<Vec<ListItemDef>, InktParseError> {
387 let mut items = Vec::new();
388 for entry in pair.into_inner() {
389 if entry.as_rule() == Rule::list_item_entry {
390 items.push(parse_list_item_entry(entry)?);
391 }
392 }
393 Ok(items)
394}
395
396fn parse_list_item_entry(pair: P<'_>) -> Result<ListItemDef, InktParseError> {
397 let mut inner = pair.into_inner();
398 let id = parse_def_id(next_rule(&mut inner, Rule::def_id, "list_item id")?)?;
399 let origin = parse_def_id(next_rule(&mut inner, Rule::def_id, "list_item origin")?)?;
400 let ordinal: i32 = next_rule(&mut inner, Rule::integer, "list_item ordinal")?
401 .as_str()
402 .parse()
403 .map_err(|_| InktParseError {
404 message: "invalid ordinal".into(),
405 line: 0,
406 col: 0,
407 })?;
408 let name_val =
409 next_rule(&mut inner, Rule::integer, "list_item name").map_or(Ok(0), |p| parse_u16(&p))?;
410 Ok(ListItemDef {
411 id,
412 origin,
413 ordinal,
414 name: NameId(name_val),
415 })
416}
417
418fn parse_externals(pair: P<'_>) -> Result<Vec<ExternalFnDef>, InktParseError> {
421 let mut exts = Vec::new();
422 for entry in pair.into_inner() {
423 if entry.as_rule() == Rule::extern_entry {
424 exts.push(parse_extern_entry(entry)?);
425 }
426 }
427 Ok(exts)
428}
429
430fn parse_extern_entry(pair: P<'_>) -> Result<ExternalFnDef, InktParseError> {
431 let mut inner = pair.into_inner();
432 let id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
433 message: "expected def_id in extern".into(),
434 line: 0,
435 col: 0,
436 })?)?;
437
438 let argc_pair = inner.next().ok_or_else(|| InktParseError {
439 message: "expected argc in extern".into(),
440 line: 0,
441 col: 0,
442 })?;
443 let arg_count: u8 = argc_pair
444 .as_str()
445 .parse()
446 .map_err(|_| err(&argc_pair, "invalid argc"))?;
447
448 let name_int = inner.next().ok_or_else(|| InktParseError {
449 message: "expected name in extern".into(),
450 line: 0,
451 col: 0,
452 })?;
453 let name = NameId(parse_u16(&name_int)?);
454
455 let mut fallback = None;
456 for remaining in inner {
457 if remaining.as_rule() == Rule::fallback {
458 let fb_inner = remaining
459 .into_inner()
460 .next()
461 .ok_or_else(|| InktParseError {
462 message: "expected def_id in fallback".into(),
463 line: 0,
464 col: 0,
465 })?;
466 fallback = Some(parse_def_id(fb_inner)?);
467 }
468 }
469
470 Ok(ExternalFnDef {
471 id,
472 name,
473 arg_count,
474 fallback,
475 })
476}
477
478fn parse_addresses(pair: P<'_>) -> Result<Vec<AddressDef>, InktParseError> {
481 let mut addresses = Vec::new();
482 for entry in pair.into_inner() {
483 if entry.as_rule() == Rule::address_entry {
484 addresses.push(parse_address_entry(entry)?);
485 }
486 }
487 Ok(addresses)
488}
489
490fn parse_address_entry(pair: P<'_>) -> Result<AddressDef, InktParseError> {
491 let mut inner = pair.into_inner();
492 let id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
493 message: "expected def_id in address".into(),
494 line: 0,
495 col: 0,
496 })?)?;
497 let container_id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
498 message: "expected container_id in address".into(),
499 line: 0,
500 col: 0,
501 })?)?;
502 let offset_pair = inner.next().ok_or_else(|| InktParseError {
503 message: "expected byte_offset in address".into(),
504 line: 0,
505 col: 0,
506 })?;
507 let byte_offset: u32 = offset_pair
508 .as_str()
509 .parse()
510 .map_err(|_| err(&offset_pair, "invalid byte_offset"))?;
511 Ok(AddressDef {
512 id,
513 container_id,
514 byte_offset,
515 })
516}
517
518fn parse_address_paths(pair: P<'_>) -> Result<Vec<AddressPath>, InktParseError> {
519 let mut paths = Vec::new();
520 for entry in pair.into_inner() {
521 if entry.as_rule() == Rule::address_path_entry {
522 paths.push(parse_address_path_entry(entry)?);
523 }
524 }
525 Ok(paths)
526}
527
528fn parse_address_path_entry(pair: P<'_>) -> Result<AddressPath, InktParseError> {
529 let mut inner = pair.into_inner();
530 let path_int = inner.next().ok_or_else(|| InktParseError {
531 message: "expected path index in address_path".into(),
532 line: 0,
533 col: 0,
534 })?;
535 let path = NameId(parse_u16(&path_int)?);
536 let target = parse_def_id(inner.next().ok_or_else(|| InktParseError {
537 message: "expected target def_id in address_path".into(),
538 line: 0,
539 col: 0,
540 })?)?;
541 Ok(AddressPath { path, target })
542}
543
544fn parse_list_literals(pair: P<'_>) -> Result<Vec<ListValue>, InktParseError> {
547 let mut literals = Vec::new();
548 for entry in pair.into_inner() {
549 if entry.as_rule() == Rule::list_literal_entry {
550 literals.push(parse_list_literal_entry(entry)?);
551 }
552 }
553 Ok(literals)
554}
555
556fn parse_list_literal_entry(pair: P<'_>) -> Result<ListValue, InktParseError> {
557 let mut items = Vec::new();
558 let mut origins = Vec::new();
559
560 for child in pair.into_inner() {
561 match child.as_rule() {
562 Rule::list_value_items => {
563 for def_pair in child.into_inner() {
564 if def_pair.as_rule() == Rule::def_id {
565 items.push(parse_def_id(def_pair)?);
566 }
567 }
568 }
569 Rule::list_value_origins => {
570 for def_pair in child.into_inner() {
571 if def_pair.as_rule() == Rule::def_id {
572 origins.push(parse_def_id(def_pair)?);
573 }
574 }
575 }
576 _ => {}
577 }
578 }
579
580 Ok(ListValue { items, origins })
581}
582
583fn parse_container(pair: P<'_>) -> Result<(ContainerDef, ScopeLineTable), InktParseError> {
586 let mut inner = pair.into_inner();
587 let id = parse_def_id(inner.next().ok_or_else(|| InktParseError {
588 message: "expected def_id in container".into(),
589 line: 0,
590 col: 0,
591 })?)?;
592
593 let mut counting_flags = CountingFlags::empty();
594 let mut path_hash = 0i32;
595 let mut param_count = 0u8;
596 let mut lines = Vec::new();
597 let mut bytecode = Vec::new();
598 let mut name: Option<NameId> = None;
599
600 let mut scope_id = id;
601
602 for child in inner {
603 match child.as_rule() {
604 Rule::scope_field => {
605 let scope_pair = child.into_inner().next().ok_or_else(|| InktParseError {
606 message: "expected def_id in scope".into(),
607 line: 0,
608 col: 0,
609 })?;
610 scope_id = parse_def_id(scope_pair)?;
611 }
612 Rule::container_name_field => {
613 let val = child.into_inner().next().ok_or_else(|| InktParseError {
614 message: "expected integer in container name".into(),
615 line: 0,
616 col: 0,
617 })?;
618 name = Some(NameId(parse_u16(&val)?));
619 }
620 Rule::flags_field => {
621 for flag in child.into_inner() {
622 if flag.as_rule() == Rule::flag_name {
623 match flag.as_str() {
624 "visits" => counting_flags |= CountingFlags::VISITS,
625 "turns" => counting_flags |= CountingFlags::TURNS,
626 "start_only" => counting_flags |= CountingFlags::COUNT_START_ONLY,
627 _ => {}
628 }
629 }
630 }
631 }
632 Rule::path_hash_field => {
633 let val = child.into_inner().next().ok_or_else(|| InktParseError {
634 message: "expected integer in path_hash".into(),
635 line: 0,
636 col: 0,
637 })?;
638 path_hash = val.as_str().parse().map_err(|_| InktParseError {
639 message: "invalid path_hash integer".into(),
640 line: 0,
641 col: 0,
642 })?;
643 }
644 Rule::params_field => {
645 let val = child.into_inner().next().ok_or_else(|| InktParseError {
646 message: "expected integer in params".into(),
647 line: 0,
648 col: 0,
649 })?;
650 param_count = val.as_str().parse().map_err(|_| InktParseError {
651 message: "invalid params integer".into(),
652 line: 0,
653 col: 0,
654 })?;
655 }
656 Rule::lines_field => {
657 lines = parse_lines_field(child)?;
658 }
659 Rule::code_field => {
660 bytecode = parse_code_field(child)?;
661 }
662 _ => {}
663 }
664 }
665
666 let container = ContainerDef {
667 id,
668 scope_id,
669 name,
670 bytecode,
671 counting_flags,
672 path_hash,
673 param_count,
674 };
675 let line_table = ScopeLineTable { scope_id, lines };
676 Ok((container, line_table))
677}
678
679fn parse_lines_field(pair: P<'_>) -> Result<Vec<LineEntry>, InktParseError> {
680 let mut entries = Vec::new();
681 for entry in pair.into_inner() {
682 if entry.as_rule() == Rule::line_entry {
683 entries.push(parse_line_entry(entry)?);
684 }
685 }
686 Ok(entries)
687}
688
689fn parse_line_entry(pair: P<'_>) -> Result<LineEntry, InktParseError> {
690 let mut inner = pair.into_inner();
691 let _index = inner.next(); let content_pair = inner.next().ok_or_else(|| InktParseError {
693 message: "expected line content".into(),
694 line: 0,
695 col: 0,
696 })?;
697 let content = parse_line_content(content_pair)?;
698 let hash_pair = inner.next().ok_or_else(|| InktParseError {
699 message: "expected source_hash".into(),
700 line: 0,
701 col: 0,
702 })?;
703 let hash_str = hash_pair.as_str();
705 let source_hash = parse_hex_u64(&format!("0x{}", &hash_str[1..]))?;
706
707 let mut audio_ref = None;
708 let mut slot_info = Vec::new();
709 let mut source_location = None;
710
711 for remaining in inner {
712 match remaining.as_rule() {
713 Rule::audio_field => {
714 let s = remaining
715 .into_inner()
716 .next()
717 .ok_or_else(|| InktParseError {
718 message: "expected audio string".into(),
719 line: 0,
720 col: 0,
721 })?;
722 audio_ref = Some(unescape_string(s.as_str()));
723 }
724 Rule::slots_field => {
725 for slot_entry in remaining.into_inner() {
726 if slot_entry.as_rule() == Rule::slot_entry {
727 let mut parts = slot_entry.into_inner();
728 let idx_str = parts.next().map_or("0", |p| p.as_str());
729 let idx: u8 = idx_str.parse().unwrap_or(0);
730 let name_str = parts
731 .next()
732 .map_or_else(String::new, |p| unescape_string(p.as_str()));
733 slot_info.push(SlotInfo {
734 index: idx,
735 name: name_str,
736 });
737 }
738 }
739 }
740 Rule::source_field => {
741 let mut parts = remaining.into_inner();
742 let file = parts
743 .next()
744 .map_or_else(String::new, |p| unescape_string(p.as_str()));
745 let start: u32 = parts
746 .next()
747 .and_then(|p| p.as_str().parse().ok())
748 .unwrap_or(0);
749 let end: u32 = parts
750 .next()
751 .and_then(|p| p.as_str().parse().ok())
752 .unwrap_or(0);
753 source_location = Some(SourceLocation {
754 file,
755 range_start: start,
756 range_end: end,
757 });
758 }
759 _ => {}
760 }
761 }
762
763 let flags = crate::LineFlags::from_content(&content);
764 Ok(LineEntry {
765 content,
766 flags,
767 source_hash,
768 audio_ref,
769 slot_info,
770 source_location,
771 })
772}
773
774fn parse_line_content(pair: P<'_>) -> Result<LineContent, InktParseError> {
775 let inner = pair.into_inner().next().ok_or_else(|| InktParseError {
776 message: "empty line content".into(),
777 line: 0,
778 col: 0,
779 })?;
780 match inner.as_rule() {
781 Rule::string => Ok(LineContent::Plain(unescape_string(inner.as_str()))),
782 Rule::template => parse_template(inner),
783 _ => Err(err(
784 &inner,
785 format!("unexpected line content: {:?}", inner.as_rule()),
786 )),
787 }
788}
789
790fn parse_template(pair: P<'_>) -> Result<LineContent, InktParseError> {
791 let mut parts = Vec::new();
792 for child in pair.into_inner() {
793 if child.as_rule() == Rule::template_part {
794 parts.push(parse_template_part(child)?);
795 }
796 }
797 Ok(LineContent::Template(parts))
798}
799
800fn parse_template_part(pair: P<'_>) -> Result<LinePart, InktParseError> {
801 let inner = pair.into_inner().next().ok_or_else(|| InktParseError {
802 message: "empty template part".into(),
803 line: 0,
804 col: 0,
805 })?;
806 match inner.as_rule() {
807 Rule::literal_part => {
808 let s = inner.into_inner().next().ok_or_else(|| InktParseError {
809 message: "expected string in literal".into(),
810 line: 0,
811 col: 0,
812 })?;
813 Ok(LinePart::Literal(unescape_string(s.as_str())))
814 }
815 Rule::slot_part => {
816 let idx = inner.into_inner().next().ok_or_else(|| InktParseError {
817 message: "expected integer in slot".into(),
818 line: 0,
819 col: 0,
820 })?;
821 let n: u8 = idx
822 .as_str()
823 .parse()
824 .map_err(|_| err(&idx, "invalid slot index"))?;
825 Ok(LinePart::Slot(n))
826 }
827 Rule::select_part => parse_select_part(inner),
828 _ => Err(err(
829 &inner,
830 format!("unexpected template part: {:?}", inner.as_rule()),
831 )),
832 }
833}
834
835fn parse_select_part(pair: P<'_>) -> Result<LinePart, InktParseError> {
836 let mut inner = pair.into_inner();
837 let slot_pair = inner.next().ok_or_else(|| InktParseError {
838 message: "expected slot in select".into(),
839 line: 0,
840 col: 0,
841 })?;
842 let slot: u8 = slot_pair
843 .as_str()
844 .parse()
845 .map_err(|_| err(&slot_pair, "invalid slot"))?;
846
847 let mut variants = Vec::new();
848 let mut default = String::new();
849
850 for child in inner {
851 match child.as_rule() {
852 Rule::select_variant => {
853 let mut vi = child.into_inner();
854 let key_pair = vi.next().ok_or_else(|| InktParseError {
855 message: "expected key in variant".into(),
856 line: 0,
857 col: 0,
858 })?;
859 let key = parse_select_key(key_pair)?;
860 let text = vi.next().ok_or_else(|| InktParseError {
861 message: "expected text in variant".into(),
862 line: 0,
863 col: 0,
864 })?;
865 variants.push((key, unescape_string(text.as_str())));
866 }
867 Rule::select_default => {
868 let s = child.into_inner().next().ok_or_else(|| InktParseError {
869 message: "expected string in default".into(),
870 line: 0,
871 col: 0,
872 })?;
873 default = unescape_string(s.as_str());
874 }
875 _ => {}
876 }
877 }
878
879 Ok(LinePart::Select {
880 slot,
881 variants,
882 default,
883 })
884}
885
886fn parse_select_key(pair: P<'_>) -> Result<SelectKey, InktParseError> {
887 let inner = pair.into_inner().next().ok_or_else(|| InktParseError {
888 message: "empty select key".into(),
889 line: 0,
890 col: 0,
891 })?;
892 match inner.as_rule() {
893 Rule::cardinal_key => {
894 let cat = inner.into_inner().next().ok_or_else(|| InktParseError {
895 message: "expected plural_cat".into(),
896 line: 0,
897 col: 0,
898 })?;
899 Ok(SelectKey::Cardinal(parse_plural_cat(cat)?))
900 }
901 Rule::ordinal_key => {
902 let cat = inner.into_inner().next().ok_or_else(|| InktParseError {
903 message: "expected plural_cat".into(),
904 line: 0,
905 col: 0,
906 })?;
907 Ok(SelectKey::Ordinal(parse_plural_cat(cat)?))
908 }
909 Rule::exact_key => {
910 let n = inner.into_inner().next().ok_or_else(|| InktParseError {
911 message: "expected integer".into(),
912 line: 0,
913 col: 0,
914 })?;
915 let v: i32 = n
916 .as_str()
917 .parse()
918 .map_err(|_| err(&n, "invalid exact key"))?;
919 Ok(SelectKey::Exact(v))
920 }
921 Rule::keyword_key => {
922 let ident = inner.into_inner().next().ok_or_else(|| InktParseError {
923 message: "expected ident".into(),
924 line: 0,
925 col: 0,
926 })?;
927 Ok(SelectKey::Keyword(ident.as_str().to_owned()))
928 }
929 _ => Err(err(
930 &inner,
931 format!("unexpected select key: {:?}", inner.as_rule()),
932 )),
933 }
934}
935
936#[expect(clippy::needless_pass_by_value)]
937fn parse_plural_cat(pair: P<'_>) -> Result<PluralCategory, InktParseError> {
938 match pair.as_str() {
939 "Zero" => Ok(PluralCategory::Zero),
940 "One" => Ok(PluralCategory::One),
941 "Two" => Ok(PluralCategory::Two),
942 "Few" => Ok(PluralCategory::Few),
943 "Many" => Ok(PluralCategory::Many),
944 "Other" => Ok(PluralCategory::Other),
945 _ => Err(err(
946 &pair,
947 format!("unknown plural category: {}", pair.as_str()),
948 )),
949 }
950}
951
952fn parse_code_field(pair: P<'_>) -> Result<Vec<u8>, InktParseError> {
955 let mut bytecode = Vec::new();
956 for child in pair.into_inner() {
957 if child.as_rule() == Rule::instruction {
958 let op = parse_instruction(child)?;
959 op.encode(&mut bytecode);
960 }
961 }
962 Ok(bytecode)
963}
964
965#[expect(clippy::too_many_lines)]
966fn parse_instruction(pair: P<'_>) -> Result<Opcode, InktParseError> {
967 let mut inner = pair.into_inner();
968 let mnemonic_pair = inner.next().ok_or_else(|| InktParseError {
969 message: "expected opcode mnemonic".into(),
970 line: 0,
971 col: 0,
972 })?;
973 let mnemonic = mnemonic_pair.as_str();
974
975 let operands: Vec<P<'_>> = inner.collect();
976
977 match mnemonic {
978 "push_int" => Ok(Opcode::PushInt(parse_operand_i32(&operands, 0, mnemonic)?)),
980 "push_float" => Ok(Opcode::PushFloat(parse_operand_f32(
981 &operands, 0, mnemonic,
982 )?)),
983 "push_bool" => {
984 let s = operand_str(&operands, 0, mnemonic)?;
985 Ok(Opcode::PushBool(s == "true"))
986 }
987 "push_string" => Ok(Opcode::PushString(parse_operand_u16(
988 &operands, 0, mnemonic,
989 )?)),
990 "push_list" => Ok(Opcode::PushList(parse_operand_u16(&operands, 0, mnemonic)?)),
991 "push_divert_target" => Ok(Opcode::PushDivertTarget(parse_operand_def_id(
992 &operands, 0, mnemonic,
993 )?)),
994 "push_null" => Ok(Opcode::PushNull),
995 "pop" => Ok(Opcode::Pop),
996 "duplicate" => Ok(Opcode::Duplicate),
997
998 "add" => Ok(Opcode::Add),
1000 "subtract" => Ok(Opcode::Subtract),
1001 "multiply" => Ok(Opcode::Multiply),
1002 "divide" => Ok(Opcode::Divide),
1003 "modulo" => Ok(Opcode::Modulo),
1004 "negate" => Ok(Opcode::Negate),
1005
1006 "equal" => Ok(Opcode::Equal),
1008 "not_equal" => Ok(Opcode::NotEqual),
1009 "greater" => Ok(Opcode::Greater),
1010 "greater_or_equal" => Ok(Opcode::GreaterOrEqual),
1011 "less" => Ok(Opcode::Less),
1012 "less_or_equal" => Ok(Opcode::LessOrEqual),
1013
1014 "not" => Ok(Opcode::Not),
1016 "and" => Ok(Opcode::And),
1017 "or" => Ok(Opcode::Or),
1018
1019 "get_global" => Ok(Opcode::GetGlobal(parse_operand_def_id(
1021 &operands, 0, mnemonic,
1022 )?)),
1023 "set_global" => Ok(Opcode::SetGlobal(parse_operand_def_id(
1024 &operands, 0, mnemonic,
1025 )?)),
1026
1027 "declare_temp" => Ok(Opcode::DeclareTemp(parse_operand_u16(
1029 &operands, 0, mnemonic,
1030 )?)),
1031 "get_temp" => Ok(Opcode::GetTemp(parse_operand_u16(&operands, 0, mnemonic)?)),
1032 "set_temp" => Ok(Opcode::SetTemp(parse_operand_u16(&operands, 0, mnemonic)?)),
1033 "get_temp_raw" => Ok(Opcode::GetTempRaw(parse_operand_u16(
1034 &operands, 0, mnemonic,
1035 )?)),
1036
1037 "push_var_pointer" => Ok(Opcode::PushVarPointer(parse_operand_def_id(
1039 &operands, 0, mnemonic,
1040 )?)),
1041 "push_temp_pointer" => Ok(Opcode::PushTempPointer(parse_operand_u16(
1042 &operands, 0, mnemonic,
1043 )?)),
1044
1045 "jump" => Ok(Opcode::Jump(parse_operand_i32(&operands, 0, mnemonic)?)),
1047 "jump_if_false" => Ok(Opcode::JumpIfFalse(parse_operand_i32(
1048 &operands, 0, mnemonic,
1049 )?)),
1050 "goto" => Ok(Opcode::Goto(parse_operand_def_id(&operands, 0, mnemonic)?)),
1051 "goto_if" => Ok(Opcode::GotoIf(parse_operand_def_id(
1052 &operands, 0, mnemonic,
1053 )?)),
1054 "goto_variable" => Ok(Opcode::GotoVariable),
1055
1056 "enter_container" => Ok(Opcode::EnterContainer(parse_operand_def_id(
1058 &operands, 0, mnemonic,
1059 )?)),
1060 "exit_container" => Ok(Opcode::ExitContainer),
1061
1062 "call" => Ok(Opcode::Call(parse_operand_def_id(&operands, 0, mnemonic)?)),
1064 "return" => Ok(Opcode::Return),
1065 "tunnel_call" => Ok(Opcode::TunnelCall(parse_operand_def_id(
1066 &operands, 0, mnemonic,
1067 )?)),
1068 "tunnel_return" => Ok(Opcode::TunnelReturn),
1069 "tunnel_call_variable" => Ok(Opcode::TunnelCallVariable),
1070 "call_variable" => Ok(Opcode::CallVariable),
1071
1072 "thread_call" => Ok(Opcode::ThreadCall(parse_operand_def_id(
1074 &operands, 0, mnemonic,
1075 )?)),
1076 "thread_start" => Ok(Opcode::ThreadStart),
1077 "thread_done" => Ok(Opcode::ThreadDone),
1078
1079 "emit_line" => {
1081 let idx = parse_operand_u16(&operands, 0, mnemonic)?;
1082 let slots = parse_operand_u8(&operands, 1, mnemonic)?;
1083 Ok(Opcode::EmitLine(idx, slots))
1084 }
1085 "emit_value" => Ok(Opcode::EmitValue),
1086 "emit_newline" => Ok(Opcode::EmitNewline),
1087 "spring" => Ok(Opcode::Spring),
1088 "glue" => Ok(Opcode::Glue),
1089 "begin_tag" => Ok(Opcode::BeginTag),
1090 "end_tag" => Ok(Opcode::EndTag),
1091 "eval_line" => {
1092 let idx = parse_operand_u16(&operands, 0, mnemonic)?;
1093 let slots = parse_operand_u8(&operands, 1, mnemonic)?;
1094 Ok(Opcode::EvalLine(idx, slots))
1095 }
1096
1097 "begin_choice" => {
1099 let flags = parse_choice_flags_operand(&operands, 0, mnemonic)?;
1100 let target = parse_operand_def_id(&operands, 1, mnemonic)?;
1101 Ok(Opcode::BeginChoice(flags, target))
1102 }
1103 "end_choice" => Ok(Opcode::EndChoice),
1104
1105 "sequence" => {
1107 let kind_str = operand_str(&operands, 0, mnemonic)?;
1108 let kind = match kind_str {
1109 "cycle" => SequenceKind::Cycle,
1110 "stopping" => SequenceKind::Stopping,
1111 "once_only" => SequenceKind::OnceOnly,
1112 "shuffle" => SequenceKind::Shuffle,
1113 _ => {
1114 return Err(InktParseError {
1115 message: format!("unknown sequence kind: {kind_str}"),
1116 line: 0,
1117 col: 0,
1118 });
1119 }
1120 };
1121 let count: u8 =
1122 operand_str(&operands, 1, mnemonic)?
1123 .parse()
1124 .map_err(|_| InktParseError {
1125 message: "invalid sequence count".into(),
1126 line: 0,
1127 col: 0,
1128 })?;
1129 Ok(Opcode::Sequence(kind, count))
1130 }
1131 "sequence_branch" => Ok(Opcode::SequenceBranch(parse_operand_i32(
1132 &operands, 0, mnemonic,
1133 )?)),
1134
1135 "visit_count" => Ok(Opcode::VisitCount),
1137 "current_visit_count" => Ok(Opcode::CurrentVisitCount),
1138 "turns_since" => Ok(Opcode::TurnsSince),
1139 "turn_index" => Ok(Opcode::TurnIndex),
1140 "choice_count" => Ok(Opcode::ChoiceCount),
1141 "random" => Ok(Opcode::Random),
1142 "seed_random" => Ok(Opcode::SeedRandom),
1143
1144 "cast_to_int" => Ok(Opcode::CastToInt),
1146 "cast_to_float" => Ok(Opcode::CastToFloat),
1147 "floor" => Ok(Opcode::Floor),
1148 "ceiling" => Ok(Opcode::Ceiling),
1149 "pow" => Ok(Opcode::Pow),
1150 "min" => Ok(Opcode::Min),
1151 "max" => Ok(Opcode::Max),
1152
1153 "call_external" => {
1155 let id = parse_operand_def_id(&operands, 0, mnemonic)?;
1156 let kv_str = operand_str(&operands, 1, mnemonic)?;
1158 let argc_str = kv_str.strip_prefix("argc=").unwrap_or(kv_str);
1159 let argc: u8 = argc_str.parse().map_err(|_| InktParseError {
1160 message: format!("invalid argc in call_external: {kv_str}"),
1161 line: 0,
1162 col: 0,
1163 })?;
1164 Ok(Opcode::CallExternal(id, argc))
1165 }
1166
1167 "list_contains" => Ok(Opcode::ListContains),
1169 "list_not_contains" => Ok(Opcode::ListNotContains),
1170 "list_intersect" => Ok(Opcode::ListIntersect),
1171 "list_all" => Ok(Opcode::ListAll),
1172 "list_invert" => Ok(Opcode::ListInvert),
1173 "list_count" => Ok(Opcode::ListCount),
1174 "list_min" => Ok(Opcode::ListMin),
1175 "list_max" => Ok(Opcode::ListMax),
1176 "list_value" => Ok(Opcode::ListValue),
1177 "list_range" => Ok(Opcode::ListRange),
1178 "list_from_int" => Ok(Opcode::ListFromInt),
1179 "list_random" => Ok(Opcode::ListRandom),
1180
1181 "done" => Ok(Opcode::Done),
1183 "yield" => Ok(Opcode::Yield),
1184 "end" => Ok(Opcode::End),
1185 "nop" => Ok(Opcode::Nop),
1186
1187 "begin_string_eval" => Ok(Opcode::BeginStringEval),
1189 "end_string_eval" => Ok(Opcode::EndStringEval),
1190
1191 "begin_fragment" => Ok(Opcode::BeginFragment),
1193 "end_fragment" => Ok(Opcode::EndFragment),
1194
1195 "source_location" => {
1197 let s = operand_str(&operands, 0, mnemonic)?;
1199 let parts: Vec<&str> = s.split(':').collect();
1200 if parts.len() != 2 {
1201 return Err(InktParseError {
1202 message: format!("invalid source_location: {s}"),
1203 line: 0,
1204 col: 0,
1205 });
1206 }
1207 let line: u32 = parts[0].parse().map_err(|_| InktParseError {
1208 message: "invalid line".into(),
1209 line: 0,
1210 col: 0,
1211 })?;
1212 let col: u32 = parts[1].parse().map_err(|_| InktParseError {
1213 message: "invalid col".into(),
1214 line: 0,
1215 col: 0,
1216 })?;
1217 Ok(Opcode::SourceLocation(line, col))
1218 }
1219
1220 _ => Err(InktParseError {
1221 message: format!("unknown opcode: {mnemonic}"),
1222 line: mnemonic_pair.line_col().0,
1223 col: mnemonic_pair.line_col().1,
1224 }),
1225 }
1226}
1227
1228fn parse_choice_flags_operand(
1229 operands: &[P<'_>],
1230 idx: usize,
1231 context: &str,
1232) -> Result<ChoiceFlags, InktParseError> {
1233 let s = operand_str(operands, idx, context)?;
1234 let mut flags = ChoiceFlags {
1235 has_condition: false,
1236 has_start_content: false,
1237 has_choice_only_content: false,
1238 once_only: false,
1239 is_invisible_default: false,
1240 };
1241 if s == "none" {
1242 return Ok(flags);
1243 }
1244 for part in s.split('+') {
1245 match part {
1246 "cond" => flags.has_condition = true,
1247 "start" => flags.has_start_content = true,
1248 "choice_only" => flags.has_choice_only_content = true,
1249 "once" => flags.once_only = true,
1250 "invis_default" => flags.is_invisible_default = true,
1251 _ => {
1252 return Err(InktParseError {
1253 message: format!("unknown choice flag: {part}"),
1254 line: 0,
1255 col: 0,
1256 });
1257 }
1258 }
1259 }
1260 Ok(flags)
1261}
1262
1263fn operand_str<'a>(
1266 operands: &'a [P<'_>],
1267 idx: usize,
1268 context: &str,
1269) -> Result<&'a str, InktParseError> {
1270 let op = operands.get(idx).ok_or_else(|| InktParseError {
1271 message: format!("missing operand {idx} for {context}"),
1272 line: 0,
1273 col: 0,
1274 })?;
1275 let inner = op.clone().into_inner().next();
1277 match inner {
1278 Some(p) => Ok(p.as_str()),
1279 None => Ok(op.as_str()),
1280 }
1281}
1282
1283fn parse_operand_i32(operands: &[P<'_>], idx: usize, context: &str) -> Result<i32, InktParseError> {
1284 let s = operand_str(operands, idx, context)?;
1285 s.parse().map_err(|_| InktParseError {
1286 message: format!("invalid i32 operand for {context}: {s}"),
1287 line: 0,
1288 col: 0,
1289 })
1290}
1291
1292fn parse_operand_f32(operands: &[P<'_>], idx: usize, context: &str) -> Result<f32, InktParseError> {
1293 let s = operand_str(operands, idx, context)?;
1294 s.parse().map_err(|_| InktParseError {
1295 message: format!("invalid f32 operand for {context}: {s}"),
1296 line: 0,
1297 col: 0,
1298 })
1299}
1300
1301fn parse_operand_u8(operands: &[P<'_>], idx: usize, context: &str) -> Result<u8, InktParseError> {
1302 let s = operand_str(operands, idx, context)?;
1303 s.parse().map_err(|_| InktParseError {
1304 message: format!("invalid u8 operand for {context}: {s}"),
1305 line: 0,
1306 col: 0,
1307 })
1308}
1309
1310fn parse_operand_u16(operands: &[P<'_>], idx: usize, context: &str) -> Result<u16, InktParseError> {
1311 let s = operand_str(operands, idx, context)?;
1312 s.parse().map_err(|_| InktParseError {
1313 message: format!("invalid u16 operand for {context}: {s}"),
1314 line: 0,
1315 col: 0,
1316 })
1317}
1318
1319fn parse_operand_def_id(
1320 operands: &[P<'_>],
1321 idx: usize,
1322 context: &str,
1323) -> Result<DefinitionId, InktParseError> {
1324 let op = operands.get(idx).ok_or_else(|| InktParseError {
1325 message: format!("missing operand {idx} for {context}"),
1326 line: 0,
1327 col: 0,
1328 })?;
1329 let inner = op.clone().into_inner().next().unwrap_or_else(|| op.clone());
1331 parse_def_id(inner)
1332}
1333
1334#[expect(clippy::needless_pass_by_value)]
1337fn parse_def_id(pair: P<'_>) -> Result<DefinitionId, InktParseError> {
1338 let s = pair.as_str();
1339 if !s.starts_with('$') || s.len() < 4 {
1341 return Err(err(&pair, format!("invalid def_id: {s}")));
1342 }
1343 let tag_str = &s[1..3];
1344 let hash_str = &s[4..]; let tag_byte = u8::from_str_radix(tag_str, 16)
1347 .map_err(|_| err(&pair, format!("invalid tag: {tag_str}")))?;
1348 let hash = u64::from_str_radix(hash_str, 16)
1349 .map_err(|_| err(&pair, format!("invalid hash: {hash_str}")))?;
1350
1351 let tag = crate::id::DefinitionTag::from_u8(tag_byte)
1352 .ok_or_else(|| err(&pair, format!("unknown tag byte: {tag_byte:#04x}")))?;
1353
1354 Ok(DefinitionId::new(tag, hash))
1355}
1356
1357fn parse_hex_u32(s: &str) -> u32 {
1358 let hex = s.strip_prefix("0x").unwrap_or(s);
1359 u32::from_str_radix(hex, 16).unwrap_or(0)
1360}
1361
1362fn parse_hex_u64(s: &str) -> Result<u64, InktParseError> {
1363 let hex = s.strip_prefix("0x").unwrap_or(s);
1364 u64::from_str_radix(hex, 16).map_err(|_| InktParseError {
1365 message: format!("invalid hex: {s}"),
1366 line: 0,
1367 col: 0,
1368 })
1369}
1370
1371fn parse_u16(pair: &P<'_>) -> Result<u16, InktParseError> {
1372 pair.as_str().parse().map_err(|_| err(pair, "invalid u16"))
1373}
1374
1375fn unescape_string(s: &str) -> String {
1376 let inner = &s[1..s.len() - 1];
1378 let mut out = String::with_capacity(inner.len());
1379 let mut chars = inner.chars();
1380 while let Some(c) = chars.next() {
1381 if c == '\\' {
1382 match chars.next() {
1383 Some('\\') | None => out.push('\\'),
1384 Some('"') => out.push('"'),
1385 Some('n') => out.push('\n'),
1386 Some('t') => out.push('\t'),
1387 Some('r') => out.push('\r'),
1388 Some(other) => {
1389 out.push('\\');
1390 out.push(other);
1391 }
1392 }
1393 } else {
1394 out.push(c);
1395 }
1396 }
1397 out
1398}
1399
1400fn next_rule<'a>(
1401 iter: &mut impl Iterator<Item = P<'a>>,
1402 expected: Rule,
1403 context: &str,
1404) -> Result<P<'a>, InktParseError> {
1405 for pair in iter.by_ref() {
1406 if pair.as_rule() == expected {
1407 return Ok(pair);
1408 }
1409 }
1410 Err(InktParseError {
1411 message: format!("expected {expected:?} in {context}"),
1412 line: 0,
1413 col: 0,
1414 })
1415}