Add obejct.key = value instructions

main
Shad Amethyst 12 months ago
parent d59eaaa385
commit 96fe0484f8

@ -0,0 +1,5 @@
REM Available to any processor, using the `control` instruction
reactor1.enabled = false
REM Only available to world processors, using the `setprop` instruction
@unit.health = @unit.health / 2

@ -1,4 +1,9 @@
use crate::{parse::ParseError, parse::ParseErrorKind, prelude::*, translate::{Namer, translate_expression}}; use crate::{
parse::ParseError,
parse::ParseErrorKind,
prelude::*,
translate::{translate_expression, Namer},
};
pub trait BuiltinFunction { pub trait BuiltinFunction {
fn validate_args( fn validate_args(
@ -27,13 +32,24 @@ macro_rules! expect_n_args {
}; };
} }
fn translate_arguments(arguments: &[BasicAstExpression], namer: &mut Namer, config: &Config) -> (Vec<String>, MindustryProgram) { fn translate_arguments(
let names = (0..arguments.len()).map(|_| namer.temporary()).collect::<Vec<_>>(); arguments: &[BasicAstExpression],
namer: &mut Namer,
config: &Config,
) -> (Vec<String>, MindustryProgram) {
let names = (0..arguments.len())
.map(|_| namer.temporary())
.collect::<Vec<_>>();
let mut res = MindustryProgram::new(); let mut res = MindustryProgram::new();
for (index, arg) in arguments.iter().enumerate() { for (index, arg) in arguments.iter().enumerate() {
res.append(&mut translate_expression(arg, namer, names[index].clone(), config)); res.append(&mut translate_expression(
arg,
namer,
names[index].clone(),
config,
));
} }
(names, res) (names, res)
@ -87,12 +103,10 @@ impl BuiltinFunction for SensorOperator {
match &args[1] { match &args[1] {
BasicAstExpression::Variable(_name) => Ok(()), BasicAstExpression::Variable(_name) => Ok(()),
other => { other => Err(ParseError::new(
Err(ParseError::new(
ParseErrorKind::InvalidArgument(other.clone()), ParseErrorKind::InvalidArgument(other.clone()),
call_span call_span,
)) )),
}
} }
} }
@ -112,7 +126,7 @@ impl BuiltinFunction for SensorOperator {
res.push(MindustryOperation::Sensor { res.push(MindustryOperation::Sensor {
out_name: target_name, out_name: target_name,
object: Operand::Variable(object_name), object: Operand::Variable(object_name),
key: Operand::Variable(format!("@{}", key)) key: Operand::Variable(format!("@{}", key)),
}); });
res res

@ -65,7 +65,7 @@ pub fn build_ast(
break; break;
}; };
match &drop_position(tokens.peek(3))[..] { match &drop_position(tokens.peek(4))[..] {
[BasicToken::NewLine, BasicToken::Integer(label), ..] => { [BasicToken::NewLine, BasicToken::Integer(label), ..] => {
tokens.take(2); tokens.take(2);
instructions.push(BasicAstInstruction::JumpLabel(label.to_string())); instructions.push(BasicAstInstruction::JumpLabel(label.to_string()));
@ -268,15 +268,33 @@ pub fn build_ast(
tokens.take(1); tokens.take(1);
instructions.push(BasicAstInstruction::Return); instructions.push(BasicAstInstruction::Return);
} }
// == Misc == // == Assign ==
[BasicToken::Name(variable_name), BasicToken::Assign, ..] => { [BasicToken::Let, BasicToken::Name(variable_name), BasicToken::Assign, ..]
| [BasicToken::Name(variable_name), BasicToken::Assign, ..] => {
if let [(BasicToken::Let, _)] = &tokens.peek(1)[..] {
tokens.take(3);
} else {
tokens.take(2); tokens.take(2);
}
let expression = parse_expression(&mut tokens, config)?; let expression = parse_expression(&mut tokens, config)?;
instructions.push(BasicAstInstruction::Assign( instructions.push(BasicAstInstruction::Assign(
variable_name.clone(), variable_name.clone(),
expression, expression,
)); ));
} }
[BasicToken::Name(variable_name), BasicToken::Operator(BasicOperator::Sensor), BasicToken::Name(key_name), BasicToken::Assign, ..] =>
{
let object = BasicAstExpression::Variable(variable_name.clone());
let key = SetPropOrControlKey::from(key_name.as_str());
tokens.take(4);
let value = parse_expression(&mut tokens, config)?;
instructions.push(BasicAstInstruction::SetPropOrControl(key, object, value));
}
// == Misc ==
[BasicToken::Print, ..] => { [BasicToken::Print, ..] => {
tokens.take(1); tokens.take(1);
@ -596,23 +614,23 @@ pub(crate) fn parse_expression(
} }
BasicOperator::Sensor => { BasicOperator::Sensor => {
if let Some(fn_config) = config.builtin_functions.get("__sensor_operator") { if let Some(fn_config) = config.builtin_functions.get("__sensor_operator") {
let arguments = vec![ let arguments = vec![ast, rhs];
ast,
rhs
];
fn_config.validate_args(&arguments, operator_pos)?; fn_config.validate_args(&arguments, operator_pos)?;
BasicAstExpression::BuiltinFunction(String::from("__sensor_operator"), arguments) BasicAstExpression::BuiltinFunction(
String::from("__sensor_operator"),
arguments,
)
} else { } else {
return Err(ParseError::new( return Err(ParseError::new(
ParseErrorKind::UnexpectedToken(BasicToken::Operator(BasicOperator::Sensor)), ParseErrorKind::UnexpectedToken(BasicToken::Operator(
operator_pos BasicOperator::Sensor,
)),
operator_pos,
)); ));
} }
} }
}; };
} }
Ok(ast) Ok(ast)

@ -21,6 +21,7 @@ fn test_tokenize_basic() {
test_drop_position(tokenize("let thing = thing / 2")), test_drop_position(tokenize("let thing = thing / 2")),
vec![ vec![
BasicToken::NewLine, BasicToken::NewLine,
BasicToken::Let,
BasicToken::Name(String::from("thing")), BasicToken::Name(String::from("thing")),
BasicToken::Assign, BasicToken::Assign,
BasicToken::Name(String::from("thing")), BasicToken::Name(String::from("thing")),

@ -32,6 +32,7 @@ pub enum BasicToken {
Name(String), Name(String),
String(String), String(String),
Operator(BasicOperator), Operator(BasicOperator),
Let,
} }
/// Transforms a raw string into a sequence of `BasicToken`s /// Transforms a raw string into a sequence of `BasicToken`s
@ -110,7 +111,7 @@ pub fn tokenize(raw: &str) -> Result<Vec<(BasicToken, Position)>, ParseError> {
// Main match clause for tokens // Main match clause for tokens
match_token!(line, res, line_index, ch; match_token!(line, res, line_index, ch;
match_space => (), match_space => (),
match_let => (), match_let => (BasicToken::Let),
match_comment => (), match_comment => (),
match_jump => (BasicToken::Goto), match_jump => (BasicToken::Goto),
match_word(word) => (match word.to_lowercase().as_str().trim() { match_word(word) => (match word.to_lowercase().as_str().trim() {

@ -6,15 +6,31 @@ pub enum BasicAstExpression {
Float(f64), Float(f64),
Variable(String), Variable(String),
String(String), String(String),
Binary( Binary(Operator, Box<BasicAstExpression>, Box<BasicAstExpression>),
Operator,
Box<BasicAstExpression>,
Box<BasicAstExpression>,
),
Unary(UnaryOperator, Box<BasicAstExpression>), Unary(UnaryOperator, Box<BasicAstExpression>),
BuiltinFunction(String, Vec<BasicAstExpression>), BuiltinFunction(String, Vec<BasicAstExpression>),
} }
/// The set of keys for `control` that accept one operand, and otherwise a key for `setprop`
#[derive(Clone, Debug, PartialEq)]
pub enum SetPropOrControlKey {
ControlEnabled,
ControlConfig,
ControlColor,
SetProp(String),
}
impl From<&str> for SetPropOrControlKey {
fn from(value: &str) -> Self {
match value {
"enabled" => Self::ControlEnabled,
"config" => Self::ControlConfig,
"color" => Self::ControlColor,
other => Self::SetProp(format!("@{}", other)),
}
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum BasicAstInstruction { pub enum BasicAstInstruction {
JumpLabel(String), JumpLabel(String),
@ -39,6 +55,7 @@ pub enum BasicAstInstruction {
}, },
While(BasicAstExpression, BasicAstBlock), While(BasicAstExpression, BasicAstBlock),
DoWhile(BasicAstExpression, BasicAstBlock), DoWhile(BasicAstExpression, BasicAstBlock),
SetPropOrControl(SetPropOrControlKey, BasicAstExpression, BasicAstExpression),
} }
#[derive(Clone, Debug, PartialEq, Default)] #[derive(Clone, Debug, PartialEq, Default)]

@ -72,6 +72,13 @@ pub enum MindustryOperation {
key: Operand, key: Operand,
}, },
/// Sets a property of a given block, only available to world processors
WorldSetProp {
key: Operand,
object: Operand,
value: Operand,
},
/// A generic operation, with the following invariants: /// A generic operation, with the following invariants:
/// - all of the operands are read-only /// - all of the operands are read-only
/// - there is no external dependency to other variables /// - there is no external dependency to other variables
@ -144,6 +151,7 @@ impl_operands!(
} => vec![cell, index], } => vec![cell, index],
Self::Write { value, cell, index } => vec![value, cell, index], Self::Write { value, cell, index } => vec![value, cell, index],
Self::Sensor { out_name: _, object, key } => vec![object, key], Self::Sensor { out_name: _, object, key } => vec![object, key],
Self::WorldSetProp { key, object, value } => vec![key, object, value],
}, },
mut: { mut: {
Self::Generic(_name, operands) => operands.iter_mut().collect::<Vec<_>>(), Self::Generic(_name, operands) => operands.iter_mut().collect::<Vec<_>>(),
@ -170,7 +178,12 @@ impl MindustryOperation {
cell: _, cell: _,
index: _, index: _,
} }
| Self::Control(_, _) => false, | Self::Control(_, _)
| Self::WorldSetProp {
value: _,
object: _,
key: _,
} => false,
Self::Operator(out_name, _, _, _) Self::Operator(out_name, _, _, _)
| Self::UnaryOperator(out_name, _, _) | Self::UnaryOperator(out_name, _, _)
@ -220,7 +233,12 @@ impl MindustryOperation {
| Self::WorldPrintFlush(_) | Self::WorldPrintFlush(_)
| Self::Generic(_, _) | Self::Generic(_, _)
| Self::GenericMut(_, _, _) | Self::GenericMut(_, _, _)
| Self::Control(_, _) => false, | Self::Control(_, _)
| Self::WorldSetProp {
value: _,
object: _,
key: _,
} => false,
Self::Set(var_name, _) Self::Set(var_name, _)
| Self::Operator(var_name, _, _, _) | Self::Operator(var_name, _, _, _)

@ -134,6 +134,9 @@ impl std::fmt::Display for MindustryProgram {
} }
writeln!(f)?; writeln!(f)?;
} }
MindustryOperation::WorldSetProp { key, object, value } => {
writeln!(f, "setprop {} {} {}", key, object, value)?;
}
} }
} }

@ -357,6 +357,50 @@ pub fn translate_ast(
res.push(MindustryOperation::Print(Operand::Variable(tmp_name))); res.push(MindustryOperation::Print(Operand::Variable(tmp_name)));
} }
} }
Instr::SetPropOrControl(key, object, value) => {
let object_name = namer.temporary();
res.append(&mut translate_expression(
object,
namer,
object_name.clone(),
config,
));
let value_name = namer.temporary();
res.append(&mut translate_expression(
value,
namer,
value_name.clone(),
config,
));
match key {
SetPropOrControlKey::ControlEnabled
| SetPropOrControlKey::ControlConfig
| SetPropOrControlKey::ControlColor => {
let key = match key {
SetPropOrControlKey::ControlEnabled => String::from("enabled"),
SetPropOrControlKey::ControlConfig => String::from("config"),
SetPropOrControlKey::ControlColor => String::from("color"),
_ => unreachable!(),
};
res.push(MindustryOperation::Control(
key,
vec![
Operand::Variable(object_name),
Operand::Variable(value_name),
],
));
}
SetPropOrControlKey::SetProp(key) => {
res.push(MindustryOperation::WorldSetProp {
key: Operand::Variable(key.clone()),
object: Operand::Variable(object_name),
value: Operand::Variable(value_name),
});
}
}
}
Instr::CallBuiltin(name, arguments) if name == "read" => translate_call!( Instr::CallBuiltin(name, arguments) if name == "read" => translate_call!(
"read", "read",
arguments, arguments,

@ -0,0 +1,9 @@
set main__tmp_0 reactor1
set main__tmp_1 false
control enabled main__tmp_0 main__tmp_1
set main__tmp_2 @unit
set main__tmp_6 @unit
sensor main__tmp_4 main__tmp_6 @health
set main__tmp_5 2
op div main__tmp_3 main__tmp_4 main__tmp_5
setprop @health main__tmp_2 main__tmp_3

@ -0,0 +1,6 @@
control enabled reactor1 false
set main__tmp_2 @unit
set main__tmp_6 @unit
sensor main__tmp_4 main__tmp_6 @health
op div main__tmp_3 main__tmp_4 2
setprop @health main__tmp_2 main__tmp_3
Loading…
Cancel
Save