Expand interpreter

main
Shad Amethyst 12 months 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 /// Represents the instruction pointer
#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Debug)] #[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] #[non_exhaustive]
pub enum StopCondition { pub enum StopCondition {
Steps(usize), Steps(usize),
End,
Never, Never,
} }
impl StopCondition { impl StopCondition {
fn should_stop(&self, steps: usize) -> bool { fn should_stop(&self, steps: usize, state: &ProgramState<impl Rng>) -> bool {
match self { match self {
Self::Steps(max_steps) => steps >= *max_steps, Self::Steps(max_steps) => steps >= *max_steps,
Self::End => state.ended,
Self::Never => false, 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> { pub fn run(program: &MindustryProgram, stop_condition: StopCondition) -> HashMap<String, Value> {
let compiled = compile(program); 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 steps = 0;
let mut variables = HashMap::new();
while !stop_condition.should_stop(steps) { while !stop_condition.should_stop(steps, &state) {
let _ = step(&compiled, &mut variables, &mut counter); let _ = step(&compiled, &mut state);
steps = steps.saturating_add(1); steps = steps.saturating_add(1);
} }
variables state.variables
} }
pub fn step( pub fn step(
program: &CompiledProgram<'_>, program: &CompiledProgram<'_>,
variables: &mut HashMap<String, Value>, state: &mut ProgramState<impl Rng>,
counter: &mut Counter,
) -> Result<(), String> { ) -> Result<(), String> {
let Some(instruction) = program.get_instruction(*counter).or_else(|| { let Some(instruction) = program.get_instruction(state.counter) else {
*counter = Counter(0); state.counter = Counter(0);
program.get_instruction(*counter) state.ended = true;
}) else { return Ok(())
// Program is empty
return Err(String::from("Program is empty"));
}; };
match instruction { match instruction {
@ -136,37 +168,96 @@ pub fn step(
} }
MindustryOperation::Jump(label) => { MindustryOperation::Jump(label) => {
if let Some(target) = program.labels.get(label) { if let Some(target) = program.labels.get(label) {
*counter = *target; state.counter = *target;
return Ok(()); return Ok(());
} else { } else {
counter.inc(); state.counter.inc();
return Err(format!("Label {} is invalid", label)); return Err(format!("Label {} is invalid", label));
} }
} }
MindustryOperation::JumpIf(label, operation, lhs, rhs) => { MindustryOperation::JumpIf(label, operation, lhs, rhs) => {
if let Some(target) = program.labels.get(label) { if let Some(target) = program.labels.get(label) {
if f64::from(eval_operation(*operation, lhs, rhs, variables, counter)) != 0.0f64 { if f64::from(eval_operation(*operation, lhs, rhs, state)) != 0.0f64 {
*counter = *target; state.counter = *target;
return Ok(()); return Ok(());
} }
} else { } else {
counter.inc(); state.counter.inc();
return Err(format!("Label {} is invalid", label)); 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!(), _ => unimplemented!(),
} }
Ok(()) 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 { match operand {
Operand::Variable(name) => { Operand::Variable(name) => {
if name == "@counter" { if name == "@counter" {
Value::Number(counter.0 as f64) Value::Number(state.counter.0 as f64)
} else { } else {
variables.get(name).cloned().unwrap_or_default() state.variables.get(name).cloned().unwrap_or_default()
} }
} }
Operand::String(string) => Value::String(string.clone()), Operand::String(string) => Value::String(string.clone()),
@ -179,11 +270,10 @@ fn eval_operation(
op: Operator, op: Operator,
lhs: &Operand, lhs: &Operand,
rhs: &Operand, rhs: &Operand,
variables: &mut HashMap<String, Value>, state: &ProgramState<impl Rng>,
counter: &mut Counter,
) -> Value { ) -> Value {
let lhs: f64 = eval_operand(lhs, variables, counter).into(); let lhs: f64 = eval_operand(lhs, state).into();
let rhs: f64 = eval_operand(rhs, variables, counter).into(); let rhs: f64 = eval_operand(rhs, state).into();
Value::Number(match op { Value::Number(match op {
Operator::Add => lhs + rhs, Operator::Add => lhs + rhs,
@ -207,3 +297,20 @@ fn eval_operation(
Operator::Or => (lhs != 0.0 || rhs != 0.0) as u64 as f64, 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