roar 0.1.0

A toolkit for procedural world generation (with a focus on voxel games)
Documentation
use crate::result::{RoarError, RoarResult};

#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
#[repr(u8)]
pub enum Op {
    Load = 0x6c,
    LoadConst = 0x63,
    Set = 0x73,
    Get = 0x67,
    Swap = 0x77,
    Perlin = 0x70,
    Fbm = 0x66,
    RidgedMulti = 0x72,
    Value = 0x76,
    Add = 0x2b,
    Sub = 0x2d,
    Mul = 0x2a,
    Div = 0x2f,
    Min = 0x6e,
    Max = 0x78,
    Pwr = 0x5e,
    Neg = 0x21,
    RightShift = 0x3e,
    LeftShift = 0x3c,
    GetCtx1 = 0x31,
    GetCtx2 = 0x32,
    SetCtx4 = 0x34,
    SetCtx5 = 0x35,
    PutOnCurve = 0x75,
}
pub mod ops {
    use super::Op;
    pub const LOAD: u8 = Op::Load as u8;
    pub const LOAD_CONST: u8 = Op::LoadConst as u8;
    pub const SET: u8 = Op::Set as u8;
    pub const GET: u8 = Op::Get as u8;
    pub const SWAP: u8 = Op::Swap as u8;
    pub const PERLIN: u8 = Op::Perlin as u8;
    pub const FBM: u8 = Op::Fbm as u8;
    pub const RIDGED_MULTI: u8 = Op::RidgedMulti as u8;
    pub const VALUE: u8 = Op::Value as u8;
    pub const ADD: u8 = Op::Add as u8;
    pub const SUB: u8 = Op::Sub as u8;
    pub const MUL: u8 = Op::Mul as u8;
    pub const DIV: u8 = Op::Div as u8;
    pub const MIN: u8 = Op::Min as u8;
    pub const MAX: u8 = Op::Max as u8;
    pub const PWR: u8 = Op::Pwr as u8;
    pub const NEG: u8 = Op::Neg as u8;
    pub const RIGHT_SHIFT: u8 = Op::RightShift as u8;
    pub const LEFT_SHIFT: u8 = Op::LeftShift as u8;
    pub const GET_CTX1: u8 = Op::GetCtx1 as u8;
    pub const GET_CTX2: u8 = Op::GetCtx2 as u8;
    pub const SET_CTX4: u8 = Op::SetCtx4 as u8;
    pub const SET_CTX5: u8 = Op::SetCtx5 as u8;
    pub const PUT_ON_CURVE: u8 = Op::PutOnCurve as u8;
}

pub fn parse_bytecode(bytecode: &[u8]) -> RoarResult<Vec<Op>> {
    use ops::*;
    let mut ops = vec![];
    for char in bytecode {
        let op = match *char {
            LOAD => Op::Load,
            LOAD_CONST => Op::LoadConst,
            SET => Op::Set,
            GET => Op::Get,
            SWAP => Op::Swap,
            PERLIN => Op::Perlin,
            FBM => Op::Fbm,
            RIDGED_MULTI => Op::RidgedMulti,
            VALUE => Op::Value,
            ADD => Op::Add,
            SUB => Op::Sub,
            MUL => Op::Mul,
            DIV => Op::Div,
            MIN => Op::Min,
            MAX => Op::Max,
            PWR => Op::Pwr,
            NEG => Op::Neg,
            RIGHT_SHIFT => Op::RightShift,
            LEFT_SHIFT => Op::LeftShift,
            GET_CTX1 => Op::GetCtx1,
            GET_CTX2 => Op::GetCtx2,
            SET_CTX4 => Op::SetCtx4,
            SET_CTX5 => Op::SetCtx5,
            PUT_ON_CURVE => Op::PutOnCurve,
            0x20 | 0x0a | 0x09 => continue,
            _ => return Err(RoarError::ParsingUnexpectedCode),
        };
        ops.push(op);
    }
    Ok(ops)
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub enum BytecodeValue {
    F64(f64),
    I64(i64),
}

impl BytecodeValue {
    pub fn as_f64(&self) -> Option<f64> {
        match self {
            BytecodeValue::F64(val) => Some(*val),
            BytecodeValue::I64(_) => None,
        }
    }
    pub fn as_i64(&self) -> Option<i64> {
        match self {
            BytecodeValue::I64(val) => Some(*val),
            BytecodeValue::F64(_) => None,
        }
    }
}

pub fn parse_input_section(input: &[char]) -> RoarResult<Vec<BytecodeValue>> {
    let mut values = vec![];
    let mut i = 0;
    loop {
        if i >= input.len() {
            break;
        }
        let c = input[i];
        i += 1;

        let v = match c {
            'i' => {
                let mut read_buf = 0i64;
                if i >= input.len() {
                    return Err(RoarError::ParsingInUnexpectedEof);
                }
                let sign = if input[i] == '-' {
                    i += 1;
                    -1
                } else {
                    1
                };
                loop {
                    if i >= input.len() {
                        break;
                    }
                    let c = input[i];
                    if c.is_digit(10) {
                        i += 1;
                        read_buf *= 10;
                        read_buf += (c as i64) - 48;
                    } else {
                        break;
                    }
                }
                BytecodeValue::I64(read_buf * sign)
            }
            'f' => {
                let mut read_buf = 0f64;
                if i >= input.len() {
                    return Err(RoarError::ParsingInUnexpectedEof);
                }

                let sign = if input[i] == '-' {
                    i += 1;
                    -1.0
                } else {
                    1.0
                };
                let mut dec = 0i32;
                loop {
                    if i >= input.len() {
                        break;
                    }
                    let c = input[i];
                    if c.is_digit(10) {
                        i += 1;
                        read_buf *= 10.0;
                        read_buf += ((c as u8) - 48) as f64;
                        if dec > 0 {
                            dec += 1;
                        }
                    } else if c == '.' {
                        i += 1;
                        if dec > 0 {
                            return Err(RoarError::ParsingInUnexpectedChar);
                        }
                        dec += 1;
                    } else {
                        break;
                    }
                }
                BytecodeValue::F64(read_buf as f64 / (10f64).powi(dec - 1) * sign)
            }
            '\n' | ' ' | '\t' => continue,
            _ => return Err(RoarError::ParsingInUnexpectedChar),
        };
        values.push(v);
    }

    Ok(values)
}