Get the parser working in Rust
This commit is contained in:
parent
54163136e1
commit
9fcb145b55
1
src/rust-base/.gitignore
vendored
Normal file
1
src/rust-base/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
target/
|
61
src/rust-base/Cargo.lock
generated
Normal file
61
src/rust-base/Cargo.lock
generated
Normal file
@ -0,0 +1,61 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "peg"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af728fe826811af3b38c37e93de6d104485953ea373d656eebae53d6987fcd2c"
|
||||
dependencies = [
|
||||
"peg-macros",
|
||||
"peg-runtime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peg-macros"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4536be147b770b824895cbad934fccce8e49f14b4c4946eaa46a6e4a12fcdc16"
|
||||
dependencies = [
|
||||
"peg-runtime",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peg-runtime"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "storybook"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"peg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
10
src/rust-base/Cargo.toml
Normal file
10
src/rust-base/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "storybook"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
peg = "0.8.0"
|
19
src/rust-base/my_header.h
Normal file
19
src/rust-base/my_header.h
Normal file
@ -0,0 +1,19 @@
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <new>
|
||||
|
||||
struct Page;
|
||||
|
||||
template<typename T = void, typename E = void>
|
||||
struct Result;
|
||||
|
||||
template<typename T = void>
|
||||
struct Vec;
|
||||
|
||||
extern "C" {
|
||||
|
||||
Result<Vec<Page>, ParseError<LineCol>> parse_story(const str *string);
|
||||
|
||||
} // extern "C"
|
173
src/rust-base/src/lib.rs
Normal file
173
src/rust-base/src/lib.rs
Normal file
@ -0,0 +1,173 @@
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Page {
|
||||
id: String,
|
||||
body: String,
|
||||
footer: Footer,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Footer {
|
||||
Ending,
|
||||
Goto(String),
|
||||
Choices(Vec<Choice>)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Choice {
|
||||
option: u32,
|
||||
flavor: String,
|
||||
redirect: String,
|
||||
stat_check: Option<StatCheck>,
|
||||
stat_change: Option<StatChange>
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct StatCheck {
|
||||
stat: String,
|
||||
value: i32,
|
||||
rel: char,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct StatChange {
|
||||
stat: String,
|
||||
addend: i32,
|
||||
}
|
||||
|
||||
|
||||
peg::parser! {
|
||||
grammar storybook_parser() for str {
|
||||
pub rule story() -> Vec<Page>
|
||||
= blankline()* ps:page()+ eof() { ps }
|
||||
rule page() -> Page
|
||||
= h:header() b:body() f:footer() blankline()* {
|
||||
Page {
|
||||
id: h,
|
||||
body: b,
|
||||
footer: f
|
||||
}
|
||||
}
|
||||
rule header() -> String
|
||||
= i:id() __ blankline()* { i }
|
||||
rule id() -> String
|
||||
= i:$(['A'..='Z'|'_']+) _ { String::from(i) }
|
||||
rule body() -> String
|
||||
= b:$((!footer() [_])+) { String::from(b.trim()) }
|
||||
rule footer() -> Footer
|
||||
= "THE END" __ { Footer::Ending } /
|
||||
"GOTO" _ i:id() __ { Footer::Goto(i) } /
|
||||
c:choice()+ { Footer::Choices(c) }
|
||||
rule choice() -> Choice
|
||||
= o:$(['0'..='9']+) ")" _ f:$((!redirect() [_])+) r:redirect() __ {
|
||||
let (redirect, stat_change, stat_check) = r;
|
||||
Choice {
|
||||
option: o.parse().unwrap(),
|
||||
flavor: String::from(f.trim()),
|
||||
redirect,
|
||||
stat_change,
|
||||
stat_check
|
||||
}
|
||||
}
|
||||
rule redirect() -> (String, Option<StatChange>, Option<StatCheck>)
|
||||
= ck:stat_check()? "[" i:id() "]" _ cg:stat_change()? { (i, cg, ck) }
|
||||
rule stat_change() -> StatChange
|
||||
= "(" a:$(['+'|'-'] ['0'..='9']+) _ n:stat_name() ")" _ { StatChange {
|
||||
addend: a.parse().unwrap(),
|
||||
stat: n
|
||||
}}
|
||||
rule stat_check() -> StatCheck
|
||||
= "<" n:stat_name() _ v:$(['0'..='9']+) r:['+'|'-'] ">" _ { StatCheck {
|
||||
stat: n,
|
||||
value: v.parse().unwrap(),
|
||||
rel: r
|
||||
}}
|
||||
rule stat_name() -> String
|
||||
= n:$(['a'..='z'|'A'..='Z']+) { String::from(n) }
|
||||
rule blankline()
|
||||
= _ __
|
||||
rule _
|
||||
= [' '|'\t']*
|
||||
rule __
|
||||
= "\r\n" / "\r" / "\n"
|
||||
rule eof()
|
||||
= ![_]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_story(string: &str) -> Result<Vec<Page>,
|
||||
peg::error::ParseError<
|
||||
peg::str::LineCol>> {
|
||||
storybook_parser::story(string)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
const STORY: &str = r"
|
||||
START
|
||||
|
||||
One day, Dane took a really big shit.
|
||||
|
||||
GOTO DIALOG
|
||||
|
||||
DIALOG
|
||||
|
||||
Dane: 'Wow, that was a really big shit!'
|
||||
|
||||
1) Die [DIE]
|
||||
2) Don't die <Strength 100+> [DONTDIE] (-1 Honor)
|
||||
|
||||
DIE
|
||||
|
||||
Then he died.
|
||||
|
||||
Fuck you, Dane!
|
||||
|
||||
THE END
|
||||
";
|
||||
#[test]
|
||||
fn storybook_parser_test() {
|
||||
assert_eq!(
|
||||
parse_story(STORY),
|
||||
Ok(vec![
|
||||
Page {
|
||||
id: String::from("START"),
|
||||
body: String::from("One day, Dane took a really big shit."),
|
||||
footer: Footer::Goto(String::from("DIALOG"))
|
||||
},
|
||||
Page {
|
||||
id: String::from("DIALOG"),
|
||||
body: String::from("Dane: 'Wow, that was a really big shit!'"),
|
||||
footer: Footer::Choices(vec![
|
||||
Choice {
|
||||
option: 1,
|
||||
flavor: String::from("Die"),
|
||||
redirect: String::from("DIE"),
|
||||
stat_check: None,
|
||||
stat_change: None,
|
||||
},
|
||||
Choice {
|
||||
option: 2,
|
||||
flavor: String::from("Don't die"),
|
||||
redirect: String::from("DONTDIE"),
|
||||
stat_change: Some(StatChange {
|
||||
stat: String::from("Honor"),
|
||||
addend: -1,
|
||||
}),
|
||||
stat_check: Some(StatCheck {
|
||||
stat: String::from("Strength"),
|
||||
value: 100,
|
||||
rel: '+',
|
||||
})
|
||||
}
|
||||
])
|
||||
},
|
||||
Page {
|
||||
id: String::from("DIE"),
|
||||
body: String::from("Then he died.\n\nFuck you, Dane!"),
|
||||
footer: Footer::Ending,
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user