Expand interpreter

main
Shad Amethyst 1 year ago
parent fe676ea58f
commit 4990f0142b

@ -1,6 +1,12 @@
use std::collections::HashMap;
use std::fmt::Write;
use std::{cell::RefCell, collections::HashMap};
use crate::{prelude::Operator, repr::mlog::*};
use rand::Rng;
use crate::{
prelude::{Operator, UnaryOperator},
repr::mlog::*,
};
/// Represents the instruction pointer
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Debug)]
@ -62,16 +68,40 @@ impl From<Value> for f64 {
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Number(x) => write!(f, "{}", x),
Value::String(x) => write!(f, "{}", x),
Value::Symbol(_) => Ok(()),
Value::Null => Ok(()),
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct ProgramState<R: Rng> {
pub variables: HashMap<String, Value>,
pub counter: Counter,
pub rng: RefCell<R>,
pub ended: bool,
}
#[non_exhaustive]
pub enum StopCondition {
Steps(usize),
End,
Never,
}
impl StopCondition {
fn should_stop(&self, steps: usize) -> bool {
fn should_stop(&self, steps: usize, state: &ProgramState<impl Rng>) -> bool {
match self {
Self::Steps(max_steps) => steps >= *max_steps,
Self::End => state.ended,
Self::Never => false,
}
}
@ -105,29 +135,31 @@ fn compile<'a>(program: &'a MindustryProgram) -> CompiledProgram<'a> {
pub fn run(program: &MindustryProgram, stop_condition: StopCondition) -> HashMap<String, Value> {
let compiled = compile(program);
let mut counter = Counter(0);
let mut state = ProgramState {
counter: Counter(0),
variables: HashMap::new(),
rng: RefCell::new(rand::thread_rng()),
ended: false,
};
let mut steps = 0;
let mut variables = HashMap::new();
while !stop_condition.should_stop(steps) {
let _ = step(&compiled, &mut variables, &mut counter);
while !stop_condition.should_stop(steps, &state) {
let _ = step(&compiled, &mut state);
steps = steps.saturating_add(1);
}
variables
state.variables
}
pub fn step(
program: &CompiledProgram<'_>,
variables: &mut HashMap<String, Value>,
counter: &mut Counter,
state: &mut ProgramState<impl Rng>,
) -> Result<(), String> {
let Some(instruction) = program.get_instruction(*counter).or_else(|| {
*counter = Counter(0);
program.get_instruction(*counter)
}) else {
// Program is empty
return Err(String::from("Program is empty"));
let Some(instruction) = program.get_instruction(state.counter) else {
state.counter = Counter(0);
state.ended = true;
return Ok(())
};
match instruction {
@ -136,37 +168,96 @@ pub fn step(
}
MindustryOperation::Jump(label) => {
if let Some(target) = program.labels.get(label) {
*counter = *target;
state.counter = *target;
return Ok(());
} else {
counter.inc();
state.counter.inc();
return Err(format!("Label {} is invalid", label));
}
}
MindustryOperation::JumpIf(label, operation, lhs, rhs) => {
if let Some(target) = program.labels.get(label) {
if f64::from(eval_operation(*operation, lhs, rhs, variables, counter)) != 0.0f64 {
*counter = *target;
if f64::from(eval_operation(*operation, lhs, rhs, state)) != 0.0f64 {
state.counter = *target;
return Ok(());
}
} else {
counter.inc();
state.counter.inc();
return Err(format!("Label {} is invalid", label));
}
}
MindustryOperation::Operator(out_name, operation, lhs, rhs) => {
let result = eval_operation(*operation, lhs, rhs, state);
state.variables.insert(out_name.clone(), result);
}
MindustryOperation::UnaryOperator(out_name, operation, value) => {
let result = eval_unary_operation(*operation, value, state);
state.variables.insert(out_name.clone(), result);
}
MindustryOperation::Set(out_name, value) => {
let value = eval_operand(value, state);
state.variables.insert(out_name.clone(), value);
}
MindustryOperation::End => {
state.counter = Counter(0);
state.ended = true;
}
MindustryOperation::Generic(_, _) => {}
MindustryOperation::GenericMut(_, name, _) => {
return Err(format!("Mutating generic operation '{}' encountered", name));
}
MindustryOperation::Control(property, operands) => {
if matches!(property.as_str(), "enabled" | "control" | "config") {
let Some(Operand::Variable(target)) = operands.get(0) else {
return Err(format!("Expected variable name as first operand to Control, got {:?}", operands.get(0)));
};
let value = eval_operand(
operands
.get(1)
.ok_or_else(|| String::from("Missing second operand to Control"))?,
state,
);
state
.variables
.insert(format!("{}.__{}", target, property), value);
} else {
return Err(format!("Control operation {} not supported", property));
}
}
MindustryOperation::Print(value) => {
let value = eval_operand(value, state);
if !matches!(state.variables.get("@print"), Some(Value::String(_))) {
state
.variables
.insert(String::from("@print"), Value::String(String::new()));
}
let Some(Value::String(ref mut buffer)) = state.variables.get_mut("@print") else {
unreachable!();
};
write!(buffer, "{}", value).unwrap_or_else(|_| unreachable!());
}
MindustryOperation::PrintFlush(_) => {
state
.variables
.insert(String::from("@print"), Value::String(String::new()));
}
_ => unimplemented!(),
}
Ok(())
}
fn eval_operand(operand: &Operand, variables: &HashMap<String, Value>, counter: &Counter) -> Value {
fn eval_operand(operand: &Operand, state: &ProgramState<impl Rng>) -> Value {
match operand {
Operand::Variable(name) => {
if name == "@counter" {
Value::Number(counter.0 as f64)
Value::Number(state.counter.0 as f64)
} else {
variables.get(name).cloned().unwrap_or_default()
state.variables.get(name).cloned().unwrap_or_default()
}
}
Operand::String(string) => Value::String(string.clone()),
@ -179,11 +270,10 @@ fn eval_operation(
op: Operator,
lhs: &Operand,
rhs: &Operand,
variables: &mut HashMap<String, Value>,
counter: &mut Counter,
state: &ProgramState<impl Rng>,
) -> Value {
let lhs: f64 = eval_operand(lhs, variables, counter).into();
let rhs: f64 = eval_operand(rhs, variables, counter).into();
let lhs: f64 = eval_operand(lhs, state).into();
let rhs: f64 = eval_operand(rhs, state).into();
Value::Number(match op {
Operator::Add => lhs + rhs,
@ -207,3 +297,20 @@ fn eval_operation(
Operator::Or => (lhs != 0.0 || rhs != 0.0) as u64 as f64,
})
}
fn eval_unary_operation(
op: UnaryOperator,
value: &Operand,
state: &ProgramState<impl Rng>,
) -> Value {
let value: f64 = eval_operand(value, state).into();
Value::Number(match op {
UnaryOperator::Floor => value.floor(),
UnaryOperator::Round => value.round(),
UnaryOperator::Ceil => value.ceil(),
UnaryOperator::Rand => state.rng.borrow_mut().gen_range(0.0..value),
UnaryOperator::Sqrt => value.sqrt(),
UnaryOperator::Not => todo!(),
})
}

Loading…
Cancel
Save