duckyscript 0.1.0

parser for duckyscript
Documentation
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(),
        ))
    }
}