Skip to content

Instantly share code, notes, and snippets.

@zesterer
Created June 30, 2019 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zesterer/e92b4f8c1cd5ee25dc72eb74a6c7015a to your computer and use it in GitHub Desktop.
Save zesterer/e92b4f8c1cd5ee25dc72eb74a6c7015a to your computer and use it in GitHub Desktop.
Natter Interpreter
use std::{
sync::mpsc,
thread::{self, JoinHandle},
collections::{HashMap, HashSet},
};
// Error
#[derive(Debug)]
pub enum Error {
ParseError(ParseError),
}
impl From<ParseError> for Error {
fn from(err: ParseError) -> Self {
Error::ParseError(err)
}
}
#[derive(Clone, Debug)]
pub enum ParseError {
Misc(usize, String),
UnexpectedEof,
NoError,
First,
}
impl ParseError {
fn max(&self, other: Self) -> Self {
match (self, &other) {
(ParseError::Misc(a, _), ParseError::Misc(b, _)) => if a > b {
self.clone()
} else {
other
},
(First, _) => other,
(ParseError::UnexpectedEof, _) => self.clone(),
(_, First) => self.clone(),
(_, ParseError::UnexpectedEof) => other,
_ => other,
}
}
}
// I/O
pub enum Input {
Choice(usize),
Text(String),
}
pub enum Output {
Speech { name: String, text: String },
ChoiceRequest(Vec<String>),
TextRequest,
Finish,
}
// Engine
pub struct Engine {
send_input: Option<mpsc::SyncSender<Input>>,
recv_output: mpsc::Receiver<Output>,
worker_handle: Option<JoinHandle<()>>,
}
impl Engine {
pub fn new(module: Module, initial_frame: String) -> Self {
let (send_input, recv_input) = mpsc::sync_channel(1);
let (send_output, recv_output) = mpsc::sync_channel(1);
let wh = thread::spawn(move ||
Self::worker(recv_input, send_output, module, initial_frame)
);
Self {
send_input: Some(send_input),
recv_output,
worker_handle: Some(wh),
}
}
pub fn next_output(&mut self) -> Output {
self.recv_output.recv().unwrap_or(Output::Finish)
}
pub fn input(&mut self, input: Input) {
self.send_input
.as_ref()
.and_then(|si| si.send(input).ok())
.unwrap();
}
fn worker(
recv_input: mpsc::Receiver<Input>,
send_output: mpsc::SyncSender<Output>,
module: Module,
initial_frame: String,
) {
struct Io<'a> {
recv: &'a mpsc::Receiver<Input>,
send: &'a mpsc::SyncSender<Output>,
}
type Context = HashSet<String>;
fn exec_block(block: &Block, ctx: &mut Context, io: &Io, module: &Module) -> bool {
for s in &block.statements {
match s {
Statement::Speech { name, text } => {
io.send.send(Output::Speech { name: name.clone(), text: text.clone() }).unwrap();
},
Statement::Choice(choices) => {
let choice_strs = choices.iter().map(|(s, _)| s.clone()).collect();
io.send.send(Output::ChoiceRequest(choice_strs)).unwrap();
if let Input::Choice(n) = io.recv.recv().unwrap() {
if let Some((_, block)) = choices.get(n) {
if exec_block(block, ctx, io, module) {
return true;
}
} else {
panic!("Choice was not in range!");
}
} else {
panic!("Requested a choice, but didn't receive one");
}
},
Statement::Set(tag) => {
ctx.insert(tag.clone());
},
Statement::Unset(tag) => {
ctx.remove(tag);
},
Statement::Call(frame) => {
if exec_block(
if let Some(block) = module.frames.get(frame.as_str()) {
block
} else {
panic!("Frame does not exist!");
},
ctx,
io,
module,
) {
return true;
}
},
Statement::If { predicate, true_block, false_block } => {
if ctx.contains(predicate.as_str()) {
if exec_block(true_block, ctx, io, module) {
return true;
}
} else {
if exec_block(false_block, ctx, io, module) {
return true;
}
}
},
Statement::While { predicate, block } => {
while ctx.contains(predicate.as_str()) {
if exec_block(block, ctx, io, module) {
return true;
}
}
},
Statement::Loop { block } => {
loop {
if exec_block(block, ctx, io, module) {
return true;
}
}
},
Statement::Finish => {
return true;
},
s => panic!("Unimplemented statement: {:?}", s),
}
}
false
}
let mut ctx = Context::new();
exec_block(
if let Some(block) = module.frames.get(&initial_frame) {
block
} else {
panic!("Initial frame does not exist!");
},
&mut ctx,
&Io { recv: &recv_input, send: &send_output },
&module,
);
// End the dialogue
send_output.send(Output::Finish).unwrap();
}
}
impl Drop for Engine {
fn drop(&mut self) {
self.send_input.take();
self.worker_handle.take().map(|wh| wh.join());
}
}
// Module
#[derive(Clone, Debug)]
enum BoolExpr {
State(String),
Not(Box<BoolExpr>),
Or(Box<BoolExpr>, Box<BoolExpr>),
And(Box<BoolExpr>, Box<BoolExpr>),
}
#[derive(Clone, Debug)]
enum Statement {
Speech {
name: String,
text: String,
},
If {
predicate: String,
true_block: Box<Block>,
false_block: Box<Block>,
},
While {
predicate: String,
block: Box<Block>,
},
Loop { block: Box<Block> },
Choice(Vec<(String, Box<Block>)>),
Set(String),
Unset(String),
Call(String),
Finish,
}
#[derive(Clone, Debug)]
struct Block {
statements: Vec<Statement>,
}
#[derive(Clone)]
pub struct Module {
frames: HashMap<String, Block>,
}
#[derive(Clone, Debug)]
enum Line {
FrameDecl(String),
Speech(String, String),
Choice,
Branch(String),
Call(String),
Set(String),
Unset(String),
If(String),
While(String),
Loop,
Else,
End,
Finish,
Comment,
}
impl Module {
fn lex_lines(code: &str) -> Result<Vec<(usize, Line)>, Error> {
Ok(code
.lines()
.enumerate()
.map(|(num, l)| (num + 1, l.trim_start()))
.filter(|(_, l)| l.len() > 0)
.map(|(num, l)| match l.chars().next() {
Some(':') => if l[1..].len() > 0 && l[1..].chars().all(|c| c.is_alphanumeric() || c == '_') {
Ok((num, Line::FrameDecl(l[1..].into())))
} else {
Err(ParseError::Misc(num, format!("Invalid frame identifier '{}'", &l[1..])))
},
Some('@') => {
let space_idx = l.find(' ').unwrap_or(1);
if space_idx > 1 && l[1..space_idx].chars().all(|c| c.is_alphanumeric() || c == '_') {
Ok((num, Line::Speech(l[1..space_idx].into(), l[space_idx..].trim_start().into())))
} else {
Err(ParseError::Misc(num, format!("Invalid speaker identifier '{}'", &l[0..space_idx])))
}
},
Some('>') => if l[1..].trim_start().len() > 0 {
Ok((num, Line::Branch(l[1..].into())))
} else {
Err(ParseError::Misc(num, "Choice branch must have identifying text".into()))
},
Some('#') => Ok((num, Line::Comment)),
Some(c) if c.is_alphanumeric() => match l.split(' ').next().unwrap_or("").trim() {
"choice" => Ok((num, Line::Choice)),
"loop" => Ok((num, Line::Loop)),
"else" => Ok((num, Line::Else)),
"end" => Ok((num, Line::End)),
"finish" => Ok((num, Line::Finish)),
op @ "call" | op @ "set" | op @ "unset" | op @ "if" | op @ "while" => if l.split(' ').count() == 2 {
let arg = l.split(' ').nth(1).unwrap();
if arg.len() > 0 && arg.chars().all(|c| c.is_alphanumeric() || c == '_') {
match op {
"call" => Ok((num, Line::Call(arg.into()))),
"set" => Ok((num, Line::Set(arg.into()))),
"unset" => Ok((num, Line::Unset(arg.into()))),
"if" => Ok((num, Line::If(arg.into()))),
"while" => Ok((num, Line::While(arg.into()))),
_ => panic!("This isn't supposed to be possible"),
}
} else {
Err(ParseError::Misc(num, format!("Invalid frame target identifier '{}'", arg)))
}
} else {
Err(ParseError::Misc(num, "Too many arguments for goto flow control instruction".into()))
},
s => Err(ParseError::Misc(num, format!("Unknown flow control instruction '{}'", s))),
},
_ => Err(ParseError::Misc(num, "Invalid syntax".into())),
})
.collect::<Result<Vec<_>, _>>()?)
}
pub fn parse_from_code(code: &str) -> Result<Self, Error> {
type LineIter<'a> = std::slice::Iter<'a, (usize, Line)>;
fn read_line<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, usize, Line), ParseError> {
match iter.next().cloned() {
Some((num, line)) => Ok((iter, num, line)),
None => return Err(ParseError::UnexpectedEof),
}
}
fn read_speech_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
match read_line(iter)? {
(iter, _, Line::Speech(name, text)) => Ok((iter, Statement::Speech { name, text })),
(_, num, _) => return Err(ParseError::Misc(num, "Expected speech statement".into())),
}
}
fn read_call_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
match read_line(iter)? {
(iter, _, Line::Call(arg)) => Ok((iter, Statement::Call(arg))),
(_, num, _) => return Err(ParseError::Misc(num, "Expected call statement".into())),
}
}
fn read_set_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
match read_line(iter)? {
(iter, _, Line::Set(arg)) => Ok((iter, Statement::Set(arg))),
(_, num, _) => return Err(ParseError::Misc(num, "Expected set statement".into())),
}
}
fn read_unset_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
match read_line(iter)? {
(iter, _, Line::Unset(arg)) => Ok((iter, Statement::Unset(arg))),
(_, num, _) => return Err(ParseError::Misc(num, "Expected unset statement".into())),
}
}
fn read_finish_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
match read_line(iter)? {
(iter, _, Line::Finish) => Ok((iter, Statement::Finish)),
(_, num, _) => return Err(ParseError::Misc(num, "Expected finish statement".into())),
}
}
fn read_if_statement<'a>(iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
let (iter, predicate) = match read_line(iter)? {
(iter, _, Line::If(predicate)) => (iter, predicate),
(_, num, _) => return Err(ParseError::Misc(num, "Expected 'if'".into())),
};
let (iter, true_block) = read_block(iter.clone())?;
let iter = match read_line(iter.clone()) {
Ok((iter, _, Line::Else)) => iter,
Ok((iter, _, Line::End)) => {
return Ok((iter, Statement::If { predicate, true_block: Box::new(true_block), false_block: Box::new(Block { statements: vec![] }) }));
},
Ok((_, num, _)) => return Err(ParseError::Misc(num, "Expected 'else' or 'end'".into())),
Err(err) => return Err(err),
};
let (iter, false_block) = read_block(iter.clone())?;
match read_line(iter.clone()) {
Ok((iter, _, Line::End)) => {
return Ok((iter, Statement::If { predicate, true_block: Box::new(true_block), false_block: Box::new(false_block) }));
},
Ok((_, num, _)) => return Err(ParseError::Misc(num, "Expected 'end' to end if".into())),
Err(err) => return Err(err),
};
}
fn read_while_statement<'a>(iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
let (iter, predicate) = match read_line(iter)? {
(iter, _, Line::While(predicate)) => (iter, predicate),
(_, num, _) => return Err(ParseError::Misc(num, "Expected 'while'".into())),
};
let (iter, block) = read_block(iter.clone())?;
let iter = match read_line(iter.clone()) {
Ok((iter, _, Line::End)) => {
return Ok((iter, Statement::While { predicate, block: Box::new(block) }));
},
Ok((_, num, _)) => return Err(ParseError::Misc(num, "Expected 'end' to end while".into())),
Err(err) => return Err(err),
};
}
fn read_loop_statement<'a>(iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
let iter = match read_line(iter)? {
(iter, _, Line::Loop) => iter,
(_, num, _) => return Err(ParseError::Misc(num, "Expected 'loop'".into())),
};
let (iter, block) = read_block(iter.clone())?;
let iter = match read_line(iter.clone()) {
Ok((iter, _, Line::End)) => {
return Ok((iter, Statement::Loop { block: Box::new(block) }));
},
Ok((_, num, _)) => return Err(ParseError::Misc(num, "Expected 'end' to end loop".into())),
Err(err) => return Err(err),
};
}
fn read_choice_statement<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Statement), ParseError> {
iter = match read_line(iter)? {
(iter, _, Line::Choice) => iter,
(_, num, _) => return Err(ParseError::Misc(num, "Expected 'choice'".into())),
};
let mut branches = vec![];
let mut max_err = ParseError::First;
loop {
let branch_tag = match read_line(iter.clone()) {
Ok((new_iter, _, Line::Branch(tag))) => {
iter = new_iter;
tag
},
Ok((iter, _, Line::End)) => return Ok((iter, Statement::Choice(branches))),
Ok((_, num, _)) => return Err(ParseError::Misc(num, "Expected choice branch or 'end'".into())),
Err(err) => return Err(max_err.max(err)),
};
max_err = max_err.max(match read_block(iter.clone()) {
Ok((new_iter, block)) => {
branches.push((branch_tag, Box::new(block)));
iter = new_iter;
continue;
},
Err(err) => err,
});
}
}
fn read_block<'a>(mut iter: LineIter<'a>) -> Result<(LineIter<'a>, Block), ParseError> {
let mut statements = vec![];
let mut max_err = ParseError::First;
loop {
max_err = max_err.max(match read_speech_statement(iter.clone()) {
Ok((new_iter, speech)) => {
statements.push(speech);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_choice_statement(iter.clone()) {
Ok((new_iter, choice)) => {
statements.push(choice);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_if_statement(iter.clone()) {
Ok((new_iter, if_s)) => {
statements.push(if_s);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_while_statement(iter.clone()) {
Ok((new_iter, while_s)) => {
statements.push(while_s);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_loop_statement(iter.clone()) {
Ok((new_iter, loop_s)) => {
statements.push(loop_s);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_call_statement(iter.clone()) {
Ok((new_iter, choice)) => {
statements.push(choice);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_set_statement(iter.clone()) {
Ok((new_iter, set)) => {
statements.push(set);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_unset_statement(iter.clone()) {
Ok((new_iter, unset)) => {
statements.push(unset);
iter = new_iter;
continue;
},
Err(err) => err,
});
max_err = max_err.max(match read_finish_statement(iter.clone()) {
Ok((new_iter, finish)) => {
statements.push(finish);
iter = new_iter;
continue;
},
Err(err) => err,
});
match read_line(iter.clone()) {
Ok((new_iter, _, Line::Comment)) => {
iter = new_iter;
continue;
},
Err(err) => max_err = max_err.max(err),
_ => {},
}
return Ok((iter, Block { statements }));
}
}
fn read_frame<'a>(iter: LineIter<'a>) -> Result<(LineIter<'a>, String, Block), ParseError> {
let (iter, name) = match read_line(iter).map_err(|_| ParseError::NoError)? {
(iter, _, Line::FrameDecl(name)) => (iter, name),
(_, num, _) => return Err(ParseError::Misc(num, "Expected frame declaration".into())),
};
let (iter, block) = read_block(iter.clone())?;
let (iter, name) = match read_line(iter)? {
(iter, _, Line::End) => (iter, name),
(_, num, _) => return Err(ParseError::Misc(num, "Expected 'end'".into())),
};
Ok((iter, name, block))
}
let lines = Self::lex_lines(code)?;
let mut iter = lines.iter();
let mut frames = HashMap::new();
loop {
match read_frame(iter) {
Ok((new_iter, name, block)) => {
frames.insert(name, block);
iter = new_iter;
},
Err(ParseError::NoError) => break,
Err(err) => return Err(err.into()),
}
}
Ok(Self {
frames,
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment