corewars_parser/
phase.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! This module defines the parser state machine. Each phase of the parser
//! is a submodule within this module.

use std::convert::TryFrom;

mod comment;
mod evaluation;
mod expansion;

use corewars_core::load_file;

use super::error::Error;

/// The data type that is passed through the parser phases. This is a simple state
/// machine, which transitions to the next state by passing through a parser phase.
#[derive(Debug)]
pub struct Phase<PhaseState> {
    /// The original input to the parser, which can be used for spans / string views
    buffer: String,
    /// State specific to the current phase of the state machine
    pub state: PhaseState,
}

/// The initial state of parsing, before any preprocessing has occurred.
pub struct Raw;

impl From<&str> for Phase<Raw> {
    fn from(buf: &str) -> Self {
        Phase {
            buffer: buf.to_string(),
            state: Raw,
        }
    }
}

/// The Phase after comments have been removed and metadata parsed from comments.
/// This phase also parses ORG and END, and removes any text after END
#[derive(Debug, Default, PartialEq, Eq)]
pub struct CommentsRemoved {
    pub lines: Vec<String>,
    pub metadata: load_file::Metadata,
    pub origin: Option<String>,
}

impl From<Phase<Raw>> for Phase<CommentsRemoved> {
    fn from(prev: Phase<Raw>) -> Self {
        let state = comment::extract_from_string(&prev.buffer);
        Self {
            buffer: prev.buffer,
            state,
        }
    }
}

/// The phase in which labels are collected and expanded. Resulting struct
/// contains metadata from previous phase and the expanded lines
#[derive(Debug, Default)]
pub struct Expanded {
    /// The expanded lines of text to be parsed later
    lines: Vec<String>,

    /// Metadata gathered in previous phase
    metadata: load_file::Metadata,

    /// The entrypoint to the program, gathered in previous phase. This is still
    /// a string because it may be an expression to be evaluated later
    origin: Option<String>,
}

impl From<Phase<CommentsRemoved>> for Phase<Expanded> {
    fn from(prev: Phase<CommentsRemoved>) -> Self {
        let lines = expansion::expand(prev.state.lines, prev.state.origin);

        Self {
            buffer: prev.buffer,
            state: Expanded {
                lines: lines.text,
                origin: lines.origin,
                metadata: prev.state.metadata,
            },
        }
    }
}

/// The program after all expressions have been evaluated. This stage handles
/// arithmetic and boolean logic, as well as parsing regular integer values.
#[derive(Debug, Default)]
pub struct Evaluated {
    /// Metadata gathered in previous phase
    metadata: load_file::Metadata,

    /// The parsed program
    program: load_file::Program,
}

impl TryFrom<Phase<Expanded>> for Phase<Evaluated> {
    type Error = Error;

    fn try_from(prev: Phase<Expanded>) -> Result<Self, Error> {
        let instructions = evaluation::evaluate(prev.state.lines)?;
        let origin = prev
            .state
            .origin
            .as_ref()
            .map(|s| evaluation::evaluate_expression(s))
            .transpose()?;

        // TODO evaluate assertions

        Ok(Self {
            buffer: prev.buffer,
            state: Evaluated {
                metadata: prev.state.metadata,
                program: load_file::Program {
                    instructions,
                    origin,
                },
            },
        })
    }
}

/// The final resulting output of the parser, which is suitable for simulation.
#[derive(Debug)]
pub struct Output {
    pub warrior: load_file::Warrior,
}

impl From<Phase<Evaluated>> for Phase<Output> {
    fn from(prev: Phase<Evaluated>) -> Self {
        Self {
            buffer: prev.buffer,
            state: Output {
                warrior: load_file::Warrior {
                    metadata: prev.state.metadata,
                    program: prev.state.program,
                },
            },
        }
    }
}