229 lines
5.1 KiB
Rust
229 lines
5.1 KiB
Rust
use crate::{
|
|
plus,
|
|
preprocess::peg::{Alt, And, Dot, EOF, NoCase, Not, Opt, Parser, Rule, Tag},
|
|
star,
|
|
world::Stat,
|
|
};
|
|
|
|
pub fn print_labels(b: &Stat) {
|
|
let mut parser = Parser::new(&b.code);
|
|
if !program.parse(&mut parser) {
|
|
eprintln!("Couldn't parse stat's code: {:?}", b.code);
|
|
return;
|
|
}
|
|
|
|
for capture in parser.iter() {
|
|
if capture.kind() == "label" {
|
|
println!("- {}", capture.text());
|
|
}
|
|
}
|
|
}
|
|
|
|
fn program() -> impl Rule {
|
|
(Opt((line, star!(("\n", line)))), EOF)
|
|
}
|
|
|
|
fn line() -> impl Rule {
|
|
Alt((send, label_line, any_line))
|
|
}
|
|
|
|
fn send() -> impl Rule {
|
|
("#", w, NoCase("send"), ww, Tag("ref", label_name), w, eol)
|
|
}
|
|
|
|
fn any_line() -> impl Rule {
|
|
(star!(Not("\n"), Dot), eol)
|
|
}
|
|
|
|
fn label_line() -> impl Rule {
|
|
(":", Tag("label", label_name), eol)
|
|
}
|
|
|
|
fn tile_kind() -> impl Rule {
|
|
(Opt((tile_color, ww)), tile_base_kind)
|
|
}
|
|
|
|
fn tile_color() -> impl Rule {
|
|
(
|
|
Alt(("blue", "green", "cyan", "red", "purple", "yellow", "white")),
|
|
Not('a'..='z'),
|
|
)
|
|
}
|
|
|
|
fn tile_base_kind() -> impl Rule {
|
|
let ac = (
|
|
And('a'..='c'),
|
|
Alt((
|
|
"ammo",
|
|
"bear",
|
|
"blinkwall",
|
|
"bomb",
|
|
"boulder",
|
|
"breakable",
|
|
"bullet",
|
|
"clockwise",
|
|
"counter",
|
|
)),
|
|
);
|
|
let dk = (
|
|
And('d'..='k'),
|
|
Alt((
|
|
"door",
|
|
"duplicator",
|
|
"empty",
|
|
"energizer",
|
|
"fake",
|
|
"forest",
|
|
"gem",
|
|
"head",
|
|
"invisible",
|
|
"key",
|
|
)),
|
|
);
|
|
let lr = (
|
|
And('l'..='r'),
|
|
Alt((
|
|
"line", "lion", "monitor", "normal", "object", "passage", "player", "pusher",
|
|
"ricochet", "ruffian",
|
|
)),
|
|
);
|
|
let s = (
|
|
And("s"),
|
|
Alt((
|
|
"scroll",
|
|
"segment",
|
|
"shark",
|
|
"sliderew",
|
|
"sliderns",
|
|
"slime",
|
|
"solid",
|
|
"spinninggun",
|
|
"star",
|
|
)),
|
|
);
|
|
let tz = (
|
|
And('t'..='z'),
|
|
Alt(("tiger", "torch", "transporter", "water")),
|
|
);
|
|
Alt((ac, dk, lr, s, tz))
|
|
}
|
|
|
|
fn condition() -> impl Rule {
|
|
let base = Alt((
|
|
"alligned",
|
|
("any", ww, tile_kind),
|
|
("blocked", ww, direction),
|
|
"contact",
|
|
"energized",
|
|
));
|
|
(star!("not", ww), base)
|
|
}
|
|
|
|
fn direction() -> impl Rule {
|
|
let modifier = Alt(("cw", "ccw", "rndp", "opp"));
|
|
(star!(modifier, ww), base_direction, Not('a'..='z'))
|
|
}
|
|
|
|
fn base_direction() -> impl Rule {
|
|
// Some directions are prefixes of others, which can break parsing.
|
|
// To prevent this, we roughly order the strings from longest to shortest.
|
|
let dynamic = Alt(("flow", "rndne", "rndns", "rnd", "seek"));
|
|
let long = Alt(("north", "south", "east", "west", "idle"));
|
|
let short = Alt(("n", "s", "e", "w", "i"));
|
|
(Alt((dynamic, long, short)), Not('a'..='z'))
|
|
}
|
|
|
|
fn label_name() -> impl Rule {
|
|
let namespace = (plus!(word_char), "~");
|
|
(
|
|
Opt(namespace),
|
|
star!(word_char),
|
|
Opt((".", plus!(word_char))),
|
|
)
|
|
}
|
|
|
|
fn word_char() -> impl Rule {
|
|
Alt(('A'..='Z', 'a'..='z', '0'..='9', "_"))
|
|
}
|
|
|
|
fn eol() -> impl Rule {
|
|
Alt((And("\n"), EOF))
|
|
}
|
|
|
|
fn w() -> impl Rule {
|
|
star!(" ")
|
|
}
|
|
|
|
fn ww() -> impl Rule {
|
|
plus!(" ")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use insta::assert_debug_snapshot;
|
|
|
|
use crate::preprocess::peg::Ref;
|
|
|
|
use super::*;
|
|
|
|
fn parse<T: Rule>(rule: &T, input: &str) {
|
|
let mut p = Parser::new(input);
|
|
let rule = (Ref(rule), EOF);
|
|
assert!(rule.parse(&mut p));
|
|
}
|
|
|
|
fn parse_err<T: Rule>(rule: &T, input: &str) {
|
|
let mut p = Parser::new(input);
|
|
let rule = (Ref(rule), EOF);
|
|
assert!(!rule.parse(&mut p));
|
|
}
|
|
|
|
#[test]
|
|
fn test_label_name() {
|
|
parse(&label_name, "foo");
|
|
parse(&label_name, ".loop");
|
|
parse(&label_name, "foo.loop");
|
|
parse(&label_name, "ns~foo");
|
|
parse(&label_name, "ns~.loop");
|
|
|
|
parse_err(&label_name, "foo.");
|
|
parse_err(&label_name, "foo.bar.baz");
|
|
parse_err(&label_name, "foo~bar~baz");
|
|
parse_err(&label_name, "~foo");
|
|
}
|
|
|
|
#[test]
|
|
fn test_direction() {
|
|
parse(&direction, "n");
|
|
parse(&direction, "north");
|
|
parse(&direction, "rndp rndne");
|
|
parse(&direction, "opp seek");
|
|
parse(&direction, "cw cw cw flow");
|
|
}
|
|
|
|
#[test]
|
|
fn test_condition() {
|
|
parse(&condition, "alligned");
|
|
parse(&condition, "blocked seek");
|
|
parse(&condition, "not blocked rndp seek");
|
|
parse(&condition, "any red lion");
|
|
parse(&condition, "any bear");
|
|
}
|
|
|
|
#[test]
|
|
fn test_references() {
|
|
let mut p = Parser::new("#send foo");
|
|
assert!(program.parse(&mut p));
|
|
let result: Vec<_> = p
|
|
.iter()
|
|
.filter(|x| x.kind() == "ref")
|
|
.map(|x| x.text())
|
|
.collect();
|
|
assert_debug_snapshot!(result, @r#"
|
|
[
|
|
"foo",
|
|
]
|
|
"#);
|
|
}
|
|
}
|