1use std::ops::Range;
2use std::str::FromStr;
3
4use smallvec::SmallVec;
5use wotw_seedgen_derive::{Display, FromStr};
6
7use crate::item;
8use crate::languages::parser::{
9 parse_ident, parse_number, parse_value, read_ident, ParseErrorCollection,
10};
11use crate::languages::{ParseError, TokenKind};
12use crate::settings::{Difficulty, Trick};
13use crate::util::{Enemy, NodeKind, Position, RefillValue};
14
15use super::tokenizer::{tokenize, TokenStream};
16
17type Parser<'a> = crate::languages::Parser<'a, TokenStream<'a>>;
18fn new(input: &str) -> Parser {
19 crate::languages::Parser::new(input, tokenize(input))
20}
21
22#[derive(Debug, Clone)]
28pub struct Areas<'a> {
29 pub(super) contents: Vec<AreaContent<'a>>,
30}
31#[derive(Debug, Clone)]
32pub enum AreaContent<'a> {
33 Requirement(NamedGroup<'a>),
34 Region(NamedGroup<'a>),
35 Anchor(Anchor<'a>),
36}
37#[derive(Debug, Clone)]
38pub struct NamedGroup<'a> {
39 pub name: &'a str,
40 pub group: Group<'a>,
41}
42#[derive(Debug, Clone)]
43pub struct Anchor<'a> {
44 pub identifier: &'a str,
45 pub position: Option<Position>,
46 pub can_spawn: bool,
47 pub teleport_restriction: Option<Group<'a>>,
48 pub refills: Vec<Refill<'a>>,
49 pub connections: Vec<Connection<'a>>,
50}
51#[derive(Debug, Clone)]
52pub struct Refill<'a> {
53 pub value: RefillValue,
54 pub requirements: Option<Group<'a>>,
55}
56#[derive(Debug, Clone)]
57pub struct Connection<'a> {
58 pub kind: NodeKind,
59 pub identifier: &'a str,
60 pub requirements: Group<'a>,
61}
62#[derive(Debug, Clone)]
63pub struct Group<'a> {
64 pub lines: Vec<Line<'a>>,
65}
66#[derive(Debug, Clone)]
67pub struct Line<'a> {
68 pub ands: Vec<Requirement<'a>>,
69 pub ors: Vec<Requirement<'a>>,
70 pub group: Option<Group<'a>>,
71}
72#[derive(Debug, Clone)]
73pub struct Requirement<'a> {
74 pub value: RequirementValue<'a>,
75 pub range: Range<usize>,
76}
77#[derive(Debug, Clone)]
78pub enum RequirementValue<'a> {
79 Free,
80 Impossible,
81 Macro(&'a str),
82 Difficulty(Difficulty),
83 Trick(Trick),
84 Skill(Skill),
85 UseSkill(Skill, u32),
86 SpiritLight(u32),
87 Resource(Resource, u32),
88 Shard(Shard),
89 Teleporter(Teleporter),
90 Water,
91 State(&'a str),
92 Damage(u32),
93 Danger(u32),
94 Combat(SmallVec<[(Enemy, u8); 12]>),
95 Boss(u32),
96 BreakWall(u32),
97 BreakCrystal,
98 ShurikenBreak(u32),
99 SentryBreak(u32),
100 HammerBreak,
101 SpearBreak,
102 SentryJump(u32),
103 SwordSentryJump(u32),
104 HammerSentryJump(u32),
105 SentryBurn(u32),
106 LaunchSwap,
107 SentrySwap(u32),
108 FlashSwap,
109 BlazeSwap(u32),
110 WaveDash,
111 GrenadeJump,
112 GrenadeCancel,
113 BowCancel,
114 HammerJump,
115 SwordJump,
116 GrenadeRedirect(u32),
117 SentryRedirect(u32),
118 GlideJump,
119 GlideHammerJump,
120 SpearJump(u32),
121}
122
123impl<'a> Areas<'a> {
124 pub fn parse(input: &'a str) -> Result<Areas<'a>, ParseErrorCollection> {
126 let mut contents = Vec::new();
127 let mut errors = ParseErrorCollection::default();
128 let mut parser = new(input);
129 loop {
130 parser.skip_while(|kind| kind == TokenKind::Newline || kind == TokenKind::Whitespace);
131 if parser.current_token().kind == TokenKind::Eof {
132 break;
133 }
134 match parse_content(&mut parser) {
135 Ok(content) => contents.push(content),
136 Err(err) => errors.push(err),
137 }
138 }
139
140 fill_macros_and_states(&mut contents, &parser).unwrap_or_else(|err| errors.push(err));
141
142 match errors.is_empty() {
143 true => Ok(Self { contents }),
144 false => Err(errors),
145 }
146 }
147}
148
149impl<'a> Anchor<'a> {
150 pub fn region(&self) -> &'a str {
151 self.identifier
152 .split_once('.')
153 .map_or(self.identifier, |parts| parts.0)
154 }
155}
156
157#[derive(Display)]
158enum Suggestion {
159 Content,
160 Identifier,
161 Integer,
162 Float,
163 Requirement,
164 Enemy,
165 AnchorContent,
166 Refill,
167}
168
169fn recover(parser: &mut Parser, recover_if: fn(TokenKind) -> bool) {
170 let mut depth = 0;
171 loop {
172 let token = parser.current_token();
173 if token.kind == TokenKind::Eof {
174 break;
175 }
176 if token.kind == TokenKind::Indent {
177 depth += 1
178 }
179 if depth > 0 {
180 if matches!(token.kind, TokenKind::Dedent { .. }) {
181 depth -= 1
182 }
183 } else if recover_if(token.kind) {
184 break;
185 }
186 parser.next_token();
187 }
188}
189fn check_dedent(parser: &mut Parser) -> Result<bool, ParseError> {
190 let token = parser.current_token();
191 match token.kind {
192 TokenKind::Dedent { matching } => {
193 let token = parser.next_token();
194 match matching {
195 true => Ok(true),
196 false => Err(parser.error("malformed Indent", token.range)),
197 }
198 }
199 _ => Ok(false),
200 }
201}
202
203#[derive(FromStr)]
204#[ParseFromIdentifier]
205enum ContentKind {
206 Requirement,
207 Region,
208 Anchor,
209}
210fn parse_content<'a>(parser: &mut Parser<'a>) -> Result<AreaContent<'a>, ParseError> {
211 let content_kind = parse_ident!(parser, Suggestion::Content)?;
212 parser.eat_or_suggest(TokenKind::Whitespace, Suggestion::Content)?;
213 match content_kind {
214 ContentKind::Requirement => parse_macro(parser),
215 ContentKind::Region => parse_region(parser),
216 ContentKind::Anchor => parse_anchor(parser),
217 }
218}
219fn parse_macro<'a>(parser: &mut Parser<'a>) -> Result<AreaContent<'a>, ParseError> {
220 let name = read_ident!(parser, Suggestion::Identifier)?;
221 let group = parse_group(parser)?;
222 Ok(AreaContent::Requirement(NamedGroup { name, group }))
223}
224fn parse_region<'a>(parser: &mut Parser<'a>) -> Result<AreaContent<'a>, ParseError> {
225 let name = read_ident!(parser, Suggestion::Identifier)?;
226 let group = parse_group(parser)?;
227 Ok(AreaContent::Region(NamedGroup { name, group }))
228}
229fn parse_anchor<'a>(parser: &mut Parser<'a>) -> Result<AreaContent<'a>, ParseError> {
230 let token_range = parser.current_token().range.clone();
231 let identifier = read_ident!(parser, Suggestion::Identifier)?;
232 if identifier == "Random" || identifier == "FullyRandom" {
233 return Err(parser.error(
234 "An anchor cannot be named \"Random\" or \"FullyRandom\"",
235 token_range,
236 ));
237 }
238 parser.skip(TokenKind::Whitespace);
239 let position = parse_anchor_position(parser)?;
240 parser.skip(TokenKind::Whitespace);
241 parser.eat(TokenKind::Indent)?;
242
243 let mut can_spawn = true;
244 let mut teleport_restriction = None;
245 let mut refills = Vec::new();
246 let mut connections = Vec::new();
247
248 loop {
249 if check_dedent(parser)? {
250 break;
251 }
252
253 let start = parser.current_token().range.start;
254
255 match parse_anchor_content(parser) {
256 Ok(content) => match content {
257 AnchorContent::NoSpawn => can_spawn = false,
258 AnchorContent::TpRestriction(requirement) => {
259 if teleport_restriction.replace(requirement).is_some() {
260 let range = start..parser.current_token().range.start;
261 recover(parser, |kind| matches!(kind, TokenKind::Dedent { .. }));
262 check_dedent(parser)?;
263 return Err(
264 parser.error("An anchor may only have one teleport restriction", range)
265 );
266 }
267 }
268 AnchorContent::Refill(refill) => refills.push(refill),
269 AnchorContent::Connection(connection) => connections.push(connection),
270 },
271 Err(err) => {
272 recover(parser, |kind| matches!(kind, TokenKind::Dedent { .. }));
273 check_dedent(parser)?;
274 return Err(err);
275 }
276 }
277 }
278
279 Ok(AreaContent::Anchor(Anchor {
280 identifier,
281 position,
282 can_spawn,
283 teleport_restriction,
284 refills,
285 connections,
286 }))
287}
288fn parse_anchor_position(parser: &mut Parser) -> Result<Option<Position>, ParseError> {
289 let token = parser.next_token();
290 let position = match token.kind {
291 TokenKind::Identifier if parser.read_token(&token) == "at" => {
292 parser.eat(TokenKind::Whitespace)?;
293 let x = parse_number!(parser, Suggestion::Float)?;
294 parser.eat(TokenKind::Comma)?;
295 parser.skip(TokenKind::Whitespace);
296 let y = parse_number!(parser, Suggestion::Float)?;
297 parser.eat_or_suggest(TokenKind::Colon, Suggestion::Float)?;
298 Some(Position { x, y })
299 }
300 TokenKind::Colon => None,
301 _ => return Err(parser.error("Expected Colon or at", token.range)),
302 };
303 Ok(position)
304}
305#[derive(FromStr)]
306#[ParseFromIdentifier]
307enum AnchorContentKind {
308 NoSpawn,
309 TpRestriction,
310 Refill,
311 State,
312 Quest,
313 Pickup,
314 Conn,
315}
316enum AnchorContent<'a> {
317 NoSpawn,
318 TpRestriction(Group<'a>),
319 Refill(Refill<'a>),
320 Connection(Connection<'a>),
321}
322fn parse_anchor_content<'a>(parser: &mut Parser<'a>) -> Result<AnchorContent<'a>, ParseError> {
323 let kind = parse_ident!(parser, Suggestion::AnchorContent)?;
324 let content = match kind {
325 AnchorContentKind::NoSpawn => {
326 parser.skip(TokenKind::Whitespace);
327 parser.eat_or_suggest(TokenKind::Newline, Suggestion::AnchorContent)?;
328 AnchorContent::NoSpawn
329 }
330 AnchorContentKind::TpRestriction => AnchorContent::TpRestriction(parse_group(parser)?),
331 AnchorContentKind::Refill => AnchorContent::Refill(parse_anchor_refill(parser)?),
332 AnchorContentKind::State => {
333 AnchorContent::Connection(parse_anchor_connection(parser, NodeKind::State)?)
334 }
335 AnchorContentKind::Quest => {
336 AnchorContent::Connection(parse_anchor_connection(parser, NodeKind::Quest)?)
337 }
338 AnchorContentKind::Pickup => {
339 AnchorContent::Connection(parse_anchor_connection(parser, NodeKind::Pickup)?)
340 }
341 AnchorContentKind::Conn => {
342 AnchorContent::Connection(parse_anchor_connection(parser, NodeKind::Anchor)?)
343 }
344 };
345 Ok(content)
346}
347fn parse_optional_group<'a>(parser: &mut Parser<'a>) -> Result<Option<Group<'a>>, ParseError> {
348 let group = match parser.current_token().kind {
349 TokenKind::Colon => Some(parse_group(parser)?),
350 _ => {
351 parser.skip(TokenKind::Whitespace);
352 parser.eat_or_suggest(TokenKind::Newline, Suggestion::Refill)?;
353 None
354 }
355 };
356
357 Ok(group)
358}
359#[derive(FromStr)]
360#[ParseFromIdentifier]
361enum RefillKind {
362 Full,
363 Checkpoint,
364 Health,
365 Energy,
366}
367fn parse_anchor_refill<'a>(parser: &mut Parser<'a>) -> Result<Refill<'a>, ParseError> {
368 parser.eat_or_suggest(TokenKind::Whitespace, Suggestion::AnchorContent)?;
369 let value = parse_refill_value(parser)?;
370 let requirements = parse_optional_group(parser)?;
371
372 Ok(Refill {
373 value,
374 requirements,
375 })
376}
377fn parse_refill_value(parser: &mut Parser) -> Result<RefillValue, ParseError> {
378 let kind = parse_ident!(parser, Suggestion::Refill)?;
379 match kind {
380 RefillKind::Full => Ok(RefillValue::Full),
381 RefillKind::Checkpoint => Ok(RefillValue::Checkpoint),
382 RefillKind::Health => {
383 parse_value!(parser, Suggestion::Float, Suggestion::Refill).map(RefillValue::Health)
384 }
385 RefillKind::Energy => {
386 parse_value!(parser, Suggestion::Float, Suggestion::Refill).map(RefillValue::Energy)
387 }
388 }
389}
390fn parse_anchor_connection<'a>(
391 parser: &mut Parser<'a>,
392 kind: NodeKind,
393) -> Result<Connection<'a>, ParseError> {
394 parser.eat_or_suggest(TokenKind::Whitespace, Suggestion::AnchorContent)?;
395 let identifier = read_ident!(parser, Suggestion::Identifier)?;
396 let requirements = parse_group(parser)?;
397 Ok(Connection {
398 kind,
399 identifier,
400 requirements,
401 })
402}
403
404fn parse_group<'a>(parser: &mut Parser<'a>) -> Result<Group<'a>, ParseError> {
405 parser.eat(TokenKind::Colon)?;
406 parser.skip(TokenKind::Whitespace);
407
408 let mut lines = Vec::new();
409 if parser.current_token().kind == TokenKind::Indent {
410 parser.next_token();
411
412 loop {
413 match parse_line(parser) {
414 Ok(line) => {
415 lines.push(line);
416 if check_dedent(parser)? {
417 break;
418 }
419 }
420 Err(err) => {
421 recover(parser, |kind| matches!(kind, TokenKind::Dedent { .. }));
422 check_dedent(parser)?;
423 return Err(err);
424 }
425 }
426 }
427 } else {
428 let line = parse_line(parser)?;
429 lines.push(line);
430 }
431
432 Ok(Group { lines })
433}
434
435fn parse_line<'a>(parser: &mut Parser<'a>) -> Result<Line<'a>, ParseError> {
436 let mut ands = Vec::new();
437 let mut ors = Vec::with_capacity(1);
438 let mut has_seen_or = false;
439
440 let group = loop {
441 let requirement = parse_requirement(parser)?;
442 parser.skip(TokenKind::Whitespace);
443
444 let token = parser.current_token();
445 match parser.current_token().kind {
446 TokenKind::Comma => match has_seen_or {
447 true => return Err(parser.error("Comma after OR", token.range.clone())),
448 false => ands.push(requirement),
449 },
450 TokenKind::Identifier if parser.read_token(token) == "OR" => {
451 ors.push(requirement);
452 has_seen_or = true;
453 }
454 TokenKind::Newline => {
455 ors.push(requirement);
456 parser.next_token();
457 break None;
458 }
459 TokenKind::Colon => {
460 let group = parse_group(parser)?;
461 ors.push(requirement);
462 break Some(group);
463 }
464 TokenKind::Indent => return Err(parser.error("unexpected Indent", token.range.clone())),
465 _ => return Err(parser.error("expected separator or Newline", token.range.clone())),
466 }
467 parser.next_token();
468 parser.skip(TokenKind::Whitespace);
469 };
470
471 Ok(Line { ands, ors, group })
472}
473
474#[derive(Display, FromStr)]
475#[ParseFromIdentifier]
476pub enum RequirementKind {
477 Free,
478 Impossible,
479 SpiritLight,
480 Water,
481 Damage,
482 Danger,
483 Combat,
484 Boss,
485 BreakWall,
486 BreakCrystal,
487 ShurikenBreak,
488 SentryBreak,
489 HammerBreak,
490 SpearBreak,
491 SentryJump,
492 SwordSJump,
493 HammerSJump,
494 SentryBurn,
495 LaunchSwap,
496 SentrySwap,
497 FlashSwap,
498 BlazeSwap,
499 WaveDash,
500 GrenadeJump,
501 GrenadeCancel,
502 BowCancel,
503 HammerJump,
504 SwordJump,
505 GrenadeRedirect,
506 SentryRedirect,
507 GlideJump,
508 GlideHammerJump,
509 SpearJump,
510}
511#[derive(Debug, Clone, Copy, FromStr)]
512#[ParseFromIdentifier]
513pub enum Resource {
514 Health = 0,
515 Energy = 1,
516 Ore = 2,
517 Keystone = 3,
518 ShardSlot = 4,
519}
520impl From<Resource> for item::Resource {
521 fn from(resource: Resource) -> item::Resource {
522 item::Resource::try_from(resource as u8).unwrap()
523 }
524}
525#[derive(Debug, Clone, Copy, FromStr)]
526#[ParseFromIdentifier]
527pub enum Skill {
528 Bash = 0,
529 WallJump = 3,
530 DoubleJump = 5,
531 Launch = 8,
532 Glide = 14,
533 WaterBreath = 23,
534 Grenade = 51,
535 Grapple = 57,
536 Flash = 62,
537 Spear = 74,
538 Regenerate = 77,
539 Bow = 97,
540 Hammer = 98,
541 Sword = 100,
542 Burrow = 101,
543 Dash = 102,
544 WaterDash = 104,
545 Shuriken = 106,
546 Seir = 108,
547 Blaze = 115,
548 Sentry = 116,
549 Flap = 118,
550}
551impl From<Skill> for item::Skill {
552 fn from(skill: Skill) -> item::Skill {
553 item::Skill::try_from(skill as u8).unwrap()
554 }
555}
556#[derive(Debug, Clone, Copy, FromStr)]
557#[ParseFromIdentifier]
558pub enum Shard {
559 TripleJump = 2,
560 Bounty = 4,
561 Magnet = 8,
562 Quickshot = 14,
563 LifeHarvest = 23,
564 EnergyHarvest = 25,
565 Sense = 30,
566 UltraBash = 32,
567 UltraGrapple = 33,
568 Thorn = 35,
569 Catalyst = 36,
570 Turmoil = 38,
571 Sticky = 39,
572 Deflector = 44,
573 Fracture = 46,
574 Arcing = 47,
575}
576impl From<Shard> for item::Shard {
577 fn from(shard: Shard) -> item::Shard {
578 item::Shard::try_from(shard as u8).unwrap()
579 }
580}
581#[derive(Debug, Clone, Copy, FromStr)]
582#[ParseFromIdentifier]
583pub enum Teleporter {
584 MarshTP = 16,
585 DenTP = 1,
586 HollowTP = 5,
587 GladesTP = 17,
588 WellspringTP = 3,
589 BurrowsTP = 0,
590 WestWoodsTP = 7,
591 EastWoodsTP = 8,
592 ReachTP = 4,
593 DepthsTP = 6,
594 EastPoolsTP = 2,
595 WestPoolsTP = 13,
596 WestWastesTP = 9,
597 EastWastesTP = 10,
598 OuterRuinsTP = 11,
599 InnerRuinsTP = 14,
600 WillowTP = 12,
601 ShriekTP = 15,
602}
603impl From<Teleporter> for item::Teleporter {
604 fn from(shard: Teleporter) -> item::Teleporter {
605 item::Teleporter::try_from(shard as u8).unwrap()
606 }
607}
608fn parse_requirement<'a>(parser: &mut Parser<'a>) -> Result<Requirement<'a>, ParseError> {
609 let start = parser.current_token().range.start;
610 let identifier = read_ident!(parser, Suggestion::Requirement)?;
611 let value = match RequirementKind::from_str(identifier) {
612 Ok(kind) => parse_special_requirement(parser, &kind)?,
613 Err(_) => match Difficulty::from_str(identifier) {
614 Ok(difficulty) => RequirementValue::Difficulty(difficulty),
615 Err(_) => match Trick::from_str(identifier) {
616 Ok(trick) => RequirementValue::Trick(trick),
617 Err(_) => match Skill::from_str(identifier) {
618 Ok(skill) => match parser.current_token().kind {
619 TokenKind::Eq => {
620 let value =
621 parse_value!(parser, Suggestion::Integer, Suggestion::Requirement)?;
622 match skill {
623 Skill::Regenerate => return Err(parser.error("Explicit Regenerate amounts are forbidden, try removing the value", start..parser.current_token().range.start)), _ => RequirementValue::UseSkill(skill, value),
625 }
626 }
627 _ => RequirementValue::Skill(skill),
628 },
629 Err(_) => match Resource::from_str(identifier) {
630 Ok(resource) => RequirementValue::Resource(
631 resource,
632 parse_value!(parser, Suggestion::Integer, Suggestion::Requirement)?,
633 ),
634 Err(_) => match Shard::from_str(identifier) {
635 Ok(shard) => RequirementValue::Shard(shard),
636 Err(_) => match Teleporter::from_str(identifier) {
637 Ok(teleporter) => RequirementValue::Teleporter(teleporter),
638 Err(_) => RequirementValue::State(identifier), },
640 },
641 },
642 },
643 },
644 },
645 };
646 let range = start..parser.current_token().range.start;
647 Ok(Requirement { value, range })
648}
649fn parse_special_requirement<'a>(
650 parser: &mut Parser<'a>,
651 kind: &RequirementKind,
652) -> Result<RequirementValue<'a>, ParseError> {
653 let requirement = match kind {
654 RequirementKind::Free => RequirementValue::Free,
655 RequirementKind::Impossible => RequirementValue::Impossible,
656 RequirementKind::Water => RequirementValue::Water,
657 RequirementKind::BreakCrystal => RequirementValue::BreakCrystal,
658 RequirementKind::HammerBreak => RequirementValue::HammerBreak,
659 RequirementKind::SpearBreak => RequirementValue::SpearBreak,
660 RequirementKind::LaunchSwap => RequirementValue::LaunchSwap,
661 RequirementKind::FlashSwap => RequirementValue::FlashSwap,
662 RequirementKind::WaveDash => RequirementValue::WaveDash,
663 RequirementKind::GrenadeJump => RequirementValue::GrenadeJump,
664 RequirementKind::GrenadeCancel => RequirementValue::GrenadeCancel,
665 RequirementKind::BowCancel => RequirementValue::BowCancel,
666 RequirementKind::HammerJump => RequirementValue::HammerJump,
667 RequirementKind::SwordJump => RequirementValue::SwordJump,
668 RequirementKind::GlideJump => RequirementValue::GlideJump,
669 RequirementKind::GlideHammerJump => RequirementValue::GlideHammerJump,
670 RequirementKind::Combat => parse_combat_requirement(parser)?,
671 _ => {
672 let amount = parse_value!(parser, Suggestion::Integer, Suggestion::Requirement)?;
673 match kind {
674 RequirementKind::SpiritLight => RequirementValue::SpiritLight(amount),
675 RequirementKind::Damage => RequirementValue::Damage(amount),
676 RequirementKind::Danger => RequirementValue::Danger(amount),
677 RequirementKind::Boss => RequirementValue::Boss(amount),
678 RequirementKind::BreakWall => RequirementValue::BreakWall(amount),
679 RequirementKind::ShurikenBreak => RequirementValue::ShurikenBreak(amount),
680 RequirementKind::SentryBreak => RequirementValue::SentryBreak(amount),
681 RequirementKind::SentryJump => RequirementValue::SentryJump(amount),
682 RequirementKind::SwordSJump => RequirementValue::SwordSentryJump(amount),
683 RequirementKind::HammerSJump => RequirementValue::HammerSentryJump(amount),
684 RequirementKind::SentryBurn => RequirementValue::SentryBurn(amount),
685 RequirementKind::SentrySwap => RequirementValue::SentrySwap(amount),
686 RequirementKind::BlazeSwap => RequirementValue::BlazeSwap(amount),
687 RequirementKind::GrenadeRedirect => RequirementValue::GrenadeRedirect(amount),
688 RequirementKind::SentryRedirect => RequirementValue::SentryRedirect(amount),
689 RequirementKind::SpearJump => RequirementValue::SpearJump(amount),
690 _ => panic!("Missing implementation for requirement {}", kind),
691 }
692 }
693 };
694 Ok(requirement)
695}
696fn parse_combat_requirement<'a>(
697 parser: &mut Parser<'a>,
698) -> Result<RequirementValue<'a>, ParseError> {
699 parser.eat_or_suggest(TokenKind::Eq, Suggestion::Requirement)?;
700 let mut enemies = SmallVec::new();
701 loop {
702 let amount = if parser.current_token().kind == TokenKind::Number {
703 let amount = parse_number!(parser, Suggestion::Integer)?;
704 parser.eat_or_suggest(TokenKind::X, Suggestion::Integer)?;
705 amount
706 } else {
707 1
708 };
709 let enemy = parse_ident!(parser, Suggestion::Enemy)?;
710 enemies.push((enemy, amount));
711 if parser.current_token().kind == TokenKind::Plus {
712 parser.next_token();
713 continue;
714 }
715 break;
716 }
717 Ok(RequirementValue::Combat(enemies))
718}
719
720fn fill_macros_and_states(
721 contents: &mut Vec<AreaContent>,
722 parser: &Parser,
723) -> Result<(), ParseError> {
724 let mut macros = Vec::new();
725 let mut states = Vec::new();
726 for content in contents.iter_mut() {
727 match content {
728 AreaContent::Requirement(named_group) => macros.push(named_group.name),
729 AreaContent::Anchor(anchor) => {
730 for connection in &anchor.connections {
731 if connection.kind == NodeKind::State || connection.kind == NodeKind::Quest {
732 states.push(connection.identifier);
733 }
734 }
735 }
736 AreaContent::Region(_) => {}
737 }
738 }
739
740 for content in contents {
741 match content {
742 AreaContent::Requirement(named_group) | AreaContent::Region(named_group) => {
743 fill_group(&mut named_group.group, ¯os, &states, parser)?
744 }
745 AreaContent::Anchor(anchor) => {
746 for refill in &mut anchor.refills {
747 for requirement in &mut refill.requirements {
748 fill_group(requirement, ¯os, &states, parser)?;
749 }
750 }
751 for connection in &mut anchor.connections {
752 fill_group(&mut connection.requirements, ¯os, &states, parser)?;
753 }
754 }
755 }
756 }
757
758 Ok(())
759}
760fn fill_group(
761 group: &mut Group,
762 macros: &[&str],
763 states: &[&str],
764 parser: &Parser,
765) -> Result<(), ParseError> {
766 for line in &mut group.lines {
767 for and in &mut line.ands {
768 fill_requirement(and, macros, states, parser)?;
769 }
770 for or in &mut line.ors {
771 fill_requirement(or, macros, states, parser)?;
772 }
773 for group in &mut line.group {
774 fill_group(group, macros, states, parser)?;
775 }
776 }
777 Ok(())
778}
779fn fill_requirement(
780 requirement: &mut Requirement,
781 macros: &[&str],
782 states: &[&str],
783 parser: &Parser,
784) -> Result<(), ParseError> {
785 if let RequirementValue::State(identifier) = requirement.value {
786 if macros.contains(&identifier) {
787 requirement.value = RequirementValue::Macro(identifier);
788 } else if !states.contains(&identifier) {
789 return Err(parser.error("unknown requirement", requirement.range.clone()));
790 };
791 }
792 Ok(())
793}
794
795#[cfg(test)]
796mod tests {
797 use super::*;
798
799 #[test]
800 fn logic_parse() {
801 let input = std::fs::read_to_string("areas.wotw").unwrap();
802 if let Err(err) = Areas::parse(&input) {
803 panic!("{}", err.verbose_display());
804 }
805 }
806}