wotw_seedgen/languages/logic/
parser.rs

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/// Syntax Tree representing an areas file
23///
24/// This is one needed component that has to be passed to [`logic::build`](crate::logic::build)
25///
26/// Use [`Areas::parse`] to parse a string into this format
27#[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    /// Parses the input string into the [`Areas`] representation
125    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)),  // The game has buggy logic for when you're allowed to regenerate and there should never be a reason to explicitely force an amount of Regenerates
624                                _ => 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), // This will get checked against the available states later
639                            },
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, &macros, &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, &macros, &states, parser)?;
749                    }
750                }
751                for connection in &mut anchor.connections {
752                    fill_group(&mut connection.requirements, &macros, &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}