amvm 0.1.0

Apika's My Virtual Machine. A virtual machine with Intermediate Lenguage
Documentation
use crate::error::error_msgs;
use crate::{
    CommandExpression, Compilable, Parser, ParserError, Value, VariableKind, COMMAND_SEPARATOR,
};
use crate::{VAR_CONST, VAR_LET};
use std::fmt;

pub static CMD_DCLR_VAR: char = '\x51';
pub static CMD_ASGN_VAR: char = '\x52';
pub static CMD_PUTS: char = '\x53';
pub static CMD_EVAL: char = '\x54';
pub static CMD_SCOPE: char = '\x55';
pub static CMD_LOOP: char = '\x56';
pub static CMD_COND: char = '\x57';
pub static CMD_BREAK: char = '\x58';

#[derive(Debug, Clone, PartialEq)]
pub enum Command {
    Break,
    AssignVariable {
        name: Value,
        value: CommandExpression,
    },
    Conditional {
        condition: CommandExpression,
        body: Vec<Command>,
        otherwise: Option<Vec<Command>>,
    },
    DeclareVariable {
        kind: VariableKind,
        name: Value,
        value: CommandExpression,
    },
    Evaluate {
        expr: CommandExpression,
    },
    Loop {
        body: Vec<Command>,
    },
    Puts {
        value: CommandExpression,
    },
    Scope {
        body: Vec<Command>,
    },
}

impl Command {
    pub fn get_kind(&self) -> char {
        match self {
            Self::AssignVariable { .. } => CMD_ASGN_VAR,
            Self::DeclareVariable { .. } => CMD_DCLR_VAR,
            Self::Evaluate { .. } => CMD_EVAL,
            Self::Puts { .. } => CMD_PUTS,
            Self::Scope { .. } => CMD_SCOPE,
            Self::Loop { .. } => CMD_LOOP,
            Self::Conditional { .. } => CMD_COND,
            Self::Break { .. } => CMD_BREAK,
        }
    }

    pub fn visit_asgn(parser: &mut Parser) -> Result<Self, ParserError> {
        let name = Value::visit(parser)?;
        let value = CommandExpression::visit(parser)?;

        Ok(Command::AssignVariable { name, value })
    }

    pub fn visit_var(parser: &mut Parser) -> Result<Self, ParserError> {
        let kind = parser.consume().ok_or_else(|| {
            parser.error_corrupt(
                error_msgs::ERROR_INVALID_CMD_DECL,
                "Can't get variable kind",
                1,
            )
        })?;
        let kind = match kind {
            b if b == VAR_CONST => VariableKind::Const,
            b if b == VAR_LET => VariableKind::Let,
            b => {
                return Err(parser.error_corrupt(
                    error_msgs::ERROR_UNKNOWN_VAR_KIND,
                    format!("{b:?}"),
                    1,
                ))
            }
        };

        let name = Value::visit(parser)?;
        let value = CommandExpression::visit(parser)?;

        Ok(Command::DeclareVariable { name, kind, value })
    }

    fn visit_scope(parser: &mut Parser) -> Result<Vec<Self>, ParserError> {
        let mut body: Vec<Command> = vec![];
        loop {
            if parser.peek(0) == Some(COMMAND_SEPARATOR) {
                parser.pointer += 1;
                break;
            }

            let cmd = Command::visit(parser)?;
            body.push(cmd);
        }

        Ok(body)
    }

    pub fn visit(parser: &mut Parser) -> Result<Self, ParserError> {
        let b = parser.consume().ok_or_else(|| {
            parser.error_corrupt(
                error_msgs::ERROR_INVALID_CMD_DECL,
                "Can't get command kind",
                1,
            )
        })?;

        match b {
            b if b == CMD_BREAK => Ok(Command::Break),
            b if b == CMD_ASGN_VAR => Self::visit_asgn(parser),
            b if b == CMD_DCLR_VAR => Self::visit_var(parser),
            b if b == CMD_PUTS => Ok(Command::Puts {
                value: CommandExpression::visit(parser)?,
            }),
            b if b == CMD_SCOPE => Ok(Command::Scope {
                body: Self::visit_scope(parser)?,
            }),
            b if b == CMD_LOOP => Ok(Command::Loop {
                body: Self::visit_scope(parser)?,
            }),
            b if b == CMD_COND => Ok(Command::Conditional {
                condition: CommandExpression::visit(parser)?,
                body: Self::visit_scope(parser)?,
                otherwise: if parser.peek(0) != Some(COMMAND_SEPARATOR) {
                    Some(Self::visit_scope(parser)?)
                } else {
                    let _ = parser.consume();
                    None
                },
            }),
            _ => Err(parser.error_corrupt(
                error_msgs::ERROR_INVALID_CMD_DECL,
                format!("Unknown command {:02x?}", b as u8),
                1,
            )),
        }
    }
}
impl Compilable for Command {
    fn compile_bytecode(&self) -> Box<str> {
        match self {
            Self::DeclareVariable { name, value, kind } => {
                if !name.is_string() {
                    panic!("Variable name should be string");
                }
                let kind = kind.compile_bytecode();
                let name = name.compile_bytecode();
                let value = value.compile_bytecode();

                Box::from(format!("{CMD_DCLR_VAR}{kind}{name}{value}"))
            }
            Self::AssignVariable { name, value } => {
                if !name.is_string() {
                    panic!("Variable name should be string");
                }
                let name = name.compile_bytecode();
                let value = value.compile_bytecode();
                Box::from(format!("{CMD_ASGN_VAR}{name}{value}"))
            }
            Self::Puts { value } => {
                let value = value.compile_bytecode();
                Box::from(format!("{CMD_PUTS}{value}"))
            }
            Self::Scope { body } => {
                let value = body.compile_bytecode();
                Box::from(format!("{CMD_SCOPE}{value}{COMMAND_SEPARATOR}"))
            }
            Self::Loop { body } => {
                let value = body.compile_bytecode();
                Box::from(format!("{CMD_LOOP}{value}{COMMAND_SEPARATOR}"))
            }
            Self::Conditional {
                condition,
                body,
                otherwise,
            } => {
                let condition = condition.compile_bytecode();
                let body = body.compile_bytecode();
                let otherwise = if let Some(otherwise) = otherwise {
                    let otherwise = otherwise.compile_bytecode();
                    format!("{otherwise}{COMMAND_SEPARATOR}")
                } else {
                    COMMAND_SEPARATOR.to_string()
                };

                Box::from(format!(
                    "{CMD_COND}{condition}{body}{COMMAND_SEPARATOR}{otherwise}"
                ))
            }
            Self::Break => Box::from(CMD_BREAK.to_string()),
            _ => todo!("{self:#?}"),
        }
    }
}

impl fmt::Display for Command {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fn fmt_body(f: &mut fmt::Formatter, body: &Vec<Command>) -> fmt::Result {
            for (i, cmd) in body.iter().enumerate() {
                let ib = format!("\x1b[32m{i:03x}\x1b[0m");
                let cmd = format!("{cmd}");
                let mut cmd = cmd
                    .split('\n')
                    .map(|c| format!(".{ib}{c}\n"))
                    .collect::<String>();

                if i == body.len() - 1 {
                    cmd.pop();
                }
                f.write_str(&cmd)?;
            }

            Ok(())
        }

        match self {
            Self::Break => f.write_str(": Break"),
            Self::AssignVariable { name, value } => {
                f.write_fmt(format_args!(": AssignVariable({name}, {value})"))
            }
            Self::DeclareVariable { name, value, kind } => {
                f.write_fmt(format_args!(": DeclareVariable({kind}, {name}, {value})"))
            }
            Self::Evaluate { expr } => f.write_fmt(format_args!(": Evaluate({expr})")),
            Self::Puts { value } => f.write_fmt(format_args!(": Puts({value})")),
            Self::Scope { body } => {
                f.write_str(": Scope:\n")?;

                fmt_body(f, body)
            }
            Self::Loop { body } => {
                f.write_str(": Loop:\n")?;

                fmt_body(f, body)
            }
            Self::Conditional {
                condition,
                body,
                otherwise,
            } => {
                f.write_fmt(format_args!(": Conditional({condition}):\n"))?;

                fmt_body(f, body)?;

                if let Some(otherwise) = otherwise {
                    f.write_str("\n: Else:\n")?;
                    fmt_body(f, otherwise)?;
                }

                Ok(())
            }
        }
    }
}