pub use pest::error::Error;
pub use pest::iterators::{Pair, Pairs};
use pest::Parser;
pub type DuckyScriptTokens<'a> = Pair<'a, ducky::Rule>;
pub type DuckyScriptError = Error<ducky::Rule>;
#[allow(missing_docs)]
pub mod ducky {
use pest_derive::Parser;
#[derive(Parser)]
#[grammar = "grammars/duckyscript.pest"]
pub struct DuckyScriptParser;
}
#[derive(Debug, Clone)]
pub enum Lit {
Int(u32),
Str(String),
Bool(bool),
}
#[derive(Debug, Clone)]
pub enum Variable {
Script(String),
Builtin(String),
}
#[derive(Debug, Clone)]
pub enum Value {
Lit(Lit),
Variable(Variable),
}
#[derive(Debug, Clone)]
pub enum Operator {
Add,
Subtract,
GreaterThan,
LessThan,
Equals,
}
#[derive(Debug, Clone)]
pub enum Expr {
Value(Value),
OperationalExpr(Value, Operator, Value),
}
#[derive(Debug, Clone)]
pub enum BuiltinFn {
LedOn,
LedOff,
LedG,
LedR,
Delay(Value),
StopPayload,
}
#[derive(Debug, Clone)]
pub enum Function {
Builtin(BuiltinFn),
UserDefined(String),
}
pub type Statements = Vec<Statement>;
#[derive(Debug, Clone)]
pub enum Statement {
ButtonDef(Statements),
CallFn(Function),
WhileBlock(Expr, Statements),
VariableDef(String, Expr),
}
pub fn get_duckyscript_tokens(script: &str) -> Result<DuckyScriptTokens, Error<ducky::Rule>> {
Ok(
ducky::DuckyScriptParser::parse(ducky::Rule::script, script)?
.next()
.unwrap(),
)
}
#[derive(Debug, Clone, Default)]
pub struct DuckyScript(pub Statements);
impl DuckyScript {
pub fn parse_str(script: &str) -> Result<Self, Error<ducky::Rule>> {
let tokens = get_duckyscript_tokens(script)?;
Ok(Self::parse_tokens(tokens))
}
pub fn parse_tokens(script: DuckyScriptTokens) -> Self {
Self(Self::parse_statements(script.into_inner()))
}
}
impl DuckyScript {
fn parse_statements(tokens: Pairs<ducky::Rule>) -> Vec<Statement> {
tokens.map_while(Self::parse_single_statement).collect()
}
fn parse_single_statement(statement: Pair<ducky::Rule>) -> Option<Statement> {
let rule = statement.as_rule();
let mut inner = statement.into_inner();
let s = match rule {
ducky::Rule::button_def => Statement::ButtonDef(Self::parse_statements(inner)),
ducky::Rule::builtin_fn => Self::parse_statement_fn_builtin(inner.next().unwrap()),
ducky::Rule::while_block => Self::parse_statement_while_block(inner),
ducky::Rule::variable_def => Self::parse_statement_variable_def(inner),
ducky::Rule::EOI => return None,
s => unreachable!("parse_single_statement {:?}", s),
};
Some(s)
}
fn parse_statement_fn_builtin(statement: Pair<ducky::Rule>) -> Statement {
let f = match statement.as_rule() {
ducky::Rule::led_on => BuiltinFn::LedOn,
ducky::Rule::led_off => BuiltinFn::LedOff,
ducky::Rule::led_g => BuiltinFn::LedG,
ducky::Rule::led_r => BuiltinFn::LedR,
ducky::Rule::delay => {
BuiltinFn::Delay(Self::parse_value(statement.into_inner().next().unwrap()))
}
ducky::Rule::stop_payload => BuiltinFn::StopPayload,
s => unreachable!("parse_builtin_fn {:?}", s),
};
Statement::CallFn(Function::Builtin(f))
}
fn parse_statement_while_block(mut statements: Pairs<ducky::Rule>) -> Statement {
let expr = Self::parse_expr(statements.next().unwrap());
let inner_statements = Self::parse_statements(statements);
Statement::WhileBlock(expr, inner_statements)
}
fn parse_statement_variable_def(mut statements: Pairs<ducky::Rule>) -> Statement {
let name = statements.next().unwrap().as_str();
let value = Self::parse_expr(statements.next().unwrap());
Statement::VariableDef(name.into(), value)
}
fn parse_expr(statement: Pair<ducky::Rule>) -> Expr {
let rule = statement.as_rule();
let mut inner = statement.into_inner();
match rule {
ducky::Rule::expr_value => Expr::Value(Self::parse_value(inner.next().unwrap())),
ducky::Rule::expr_conditional => {
let lval = Self::parse_value(inner.next().unwrap());
let operator = match inner.next().unwrap().as_str() {
"+" => Operator::Add,
">" => Operator::GreaterThan,
"<" => Operator::LessThan,
"==" => Operator::Equals,
o => unreachable!("operator {}", o),
};
let rval = Self::parse_value(inner.next().unwrap());
Expr::OperationalExpr(lval, operator, rval)
}
e => unreachable!("parse_expr {:?}", e),
}
}
fn parse_value(statement: Pair<ducky::Rule>) -> Value {
let rule = statement.as_rule();
match rule {
ducky::Rule::lit_int => Self::parse_value_lit_int(statement),
ducky::Rule::lit_bool => Self::parse_value_lit_bool(statement),
ducky::Rule::script_variable => {
Value::Variable(Variable::Script(statement.as_str().into()))
}
s => unreachable!("parse_value {:?}", s),
}
}
fn parse_value_lit_int(statement: Pair<ducky::Rule>) -> Value {
Value::Lit(Lit::Int(statement.as_str().parse().unwrap()))
}
fn parse_value_lit_bool(statement: Pair<ducky::Rule>) -> Value {
Value::Lit(Lit::Bool(
statement.as_str().to_lowercase().parse().unwrap(),
))
}
}