Dot operator for Sensor

main
Shad Amethyst 1 year ago
parent ed5de3aaa1
commit 00f992d459

@ -1,9 +1,7 @@
{
"comments": {
// symbol used for single line comment. Remove this entry if your language does not support line comments
"lineComment": "//",
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "/*", "*/" ]
"lineComment": "REM",
},
// symbols used as brackets
"brackets": [
@ -27,4 +25,4 @@
["\"", "\""],
["'", "'"]
]
}
}

@ -23,7 +23,7 @@
},
{
"comment": "Keyword",
"match": "(?i)(\\b((END ?)?IF|(END )?SELECT|(RESUME )?NEXT|CASE|CLOSE|DO|ELSE|FOR|GOSUB|GOTO|LOOP|ON|OPEN|RETURN|THEN|TO|UNTIL|WHILE)\\b)",
"match": "(?i)(\\b((END ?)?IF|(END )?SELECT|(RESUME )?NEXT|CASE|CLOSE|DO|ELSE|FOR|GOSUB|GOTO|LOOP|ON|RETURN|THEN|TO|WHILE|WEND|END)\\b)",
"name": "keyword.control.minbasic"
},
{
@ -33,7 +33,7 @@
},
{
"comment": "Operator",
"match": "(?i)((\\+|=|<|>|<>|AND|OR))",
"match": "(?i)(([+\\-*/\\.%]|[!=]=|<[=<]?|>[=>]?|<>|AND|OR))",
"name": "keyword.operator.minbasic"
},
{

@ -403,7 +403,7 @@ macro_rules! impl_op_basic_ast_expression {
type Output = BasicAstExpression;
fn $fn_name(self, other: Self) -> Self {
Self::Binary($self_op, Box::new(self), Box::new(other))
Self::Binary($self_op.into(), Box::new(self), Box::new(other))
}
}
};
@ -469,7 +469,7 @@ pub(crate) fn parse_expression(
))
}
} else if let Some(binary_operator) =
Operator::from_fn_name(fn_name_lowercase.as_str())
BasicOperator::from_fn_name(fn_name_lowercase.as_str())
{
if arguments.len() != 2 {
Err(ParseError::InvalidArgumentCount(

@ -8,7 +8,7 @@ fn test_tokenize_basic() {
vec![
BasicToken::NewLine,
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Name(String::from("world")),
],
);
@ -20,7 +20,7 @@ fn test_tokenize_basic() {
BasicToken::Name(String::from("thing")),
BasicToken::Assign,
BasicToken::Name(String::from("thing")),
BasicToken::Operator(Operator::Div),
BasicToken::Operator(Operator::Div.into()),
BasicToken::Integer(2)
],
);
@ -33,7 +33,7 @@ fn test_tokenize_basic() {
BasicToken::Name(String::from("thing")),
BasicToken::Assign,
BasicToken::Name(String::from("thing")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Float(0.5),
BasicToken::NewLine,
BasicToken::Goto,
@ -51,7 +51,7 @@ fn test_tokenize_basic() {
BasicToken::NewLine,
BasicToken::If,
BasicToken::Name(String::from("x")),
BasicToken::Operator(Operator::Gt),
BasicToken::Operator(Operator::Gt.into()),
BasicToken::Integer(0),
BasicToken::Then,
BasicToken::NewLine,
@ -70,7 +70,7 @@ fn test_tokenize_basic() {
BasicToken::NewLine,
BasicToken::If,
BasicToken::Name(String::from("x")),
BasicToken::Operator(Operator::Gt),
BasicToken::Operator(Operator::Gt.into()),
BasicToken::Integer(0),
BasicToken::Then,
BasicToken::NewLine,
@ -147,11 +147,11 @@ fn test_operator_precedence() {
assert_eq!(
test_parse([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Integer(1),
]),
BasicAstExpression::Binary(
Operator::Add,
Operator::Add.into(),
Box::new(BasicAstExpression::Variable(String::from("hello"))),
Box::new(BasicAstExpression::Integer(1)),
)
@ -160,16 +160,16 @@ fn test_operator_precedence() {
assert_eq!(
test_parse([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Integer(2),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Mul.into()),
BasicToken::Name(String::from("world")),
]),
BasicAstExpression::Binary(
Operator::Add,
Operator::Add.into(),
Box::new(BasicAstExpression::Variable(String::from("hello"))),
Box::new(BasicAstExpression::Binary(
Operator::Mul,
Operator::Mul.into(),
Box::new(BasicAstExpression::Integer(2)),
Box::new(BasicAstExpression::Variable(String::from("world"))),
)),
@ -179,15 +179,15 @@ fn test_operator_precedence() {
assert_eq!(
test_parse([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Mul.into()),
BasicToken::Integer(2),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Name(String::from("world")),
]),
BasicAstExpression::Binary(
Operator::Add,
Operator::Add.into(),
Box::new(BasicAstExpression::Binary(
Operator::Mul,
Operator::Mul.into(),
Box::new(BasicAstExpression::Variable(String::from("hello"))),
Box::new(BasicAstExpression::Integer(2)),
)),
@ -198,18 +198,18 @@ fn test_operator_precedence() {
assert_eq!(
test_parse([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Mul.into()),
BasicToken::OpenParen,
BasicToken::Integer(2),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Name(String::from("world")),
BasicToken::CloseParen,
]),
BasicAstExpression::Binary(
Operator::Mul,
Operator::Mul.into(),
Box::new(BasicAstExpression::Variable(String::from("hello"))),
Box::new(BasicAstExpression::Binary(
Operator::Add,
Operator::Add.into(),
Box::new(BasicAstExpression::Integer(2)),
Box::new(BasicAstExpression::Variable(String::from("world"))),
)),
@ -219,18 +219,18 @@ fn test_operator_precedence() {
assert_eq!(
test_parse([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::OpenParen,
BasicToken::Name(String::from("world")),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Mul.into()),
BasicToken::Integer(2),
BasicToken::CloseParen,
]),
BasicAstExpression::Binary(
Operator::Add,
Operator::Add.into(),
Box::new(BasicAstExpression::Variable(String::from("hello"))),
Box::new(BasicAstExpression::Binary(
Operator::Mul,
Operator::Mul.into(),
Box::new(BasicAstExpression::Variable(String::from("world"))),
Box::new(BasicAstExpression::Integer(2)),
)),
@ -240,7 +240,7 @@ fn test_operator_precedence() {
assert_eq!(
test_err([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
]),
ParseError::ExpectedOperand
);
@ -248,10 +248,10 @@ fn test_operator_precedence() {
assert_eq!(
test_err([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::OpenParen,
BasicToken::Name(String::from("world")),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Mul.into()),
BasicToken::Integer(2),
]),
ParseError::MissingToken(BasicToken::CloseParen)
@ -260,16 +260,16 @@ fn test_operator_precedence() {
assert_eq!(
test_err([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Mul),
BasicToken::Operator(Operator::Add.into()),
BasicToken::Operator(Operator::Mul.into()),
]),
ParseError::UnexpectedToken(BasicToken::Operator(Operator::Mul))
ParseError::UnexpectedToken(BasicToken::Operator(Operator::Mul.into()))
);
assert!(matches!(
test_err([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::OpenParen,
]),
ParseError::ExpectedOperand | ParseError::MissingToken(BasicToken::CloseParen)
@ -278,7 +278,7 @@ fn test_operator_precedence() {
assert!(matches!(
test_err([
BasicToken::Name(String::from("hello")),
BasicToken::Operator(Operator::Add),
BasicToken::Operator(Operator::Add.into()),
BasicToken::OpenParen,
BasicToken::CloseParen
]),
@ -286,8 +286,11 @@ fn test_operator_precedence() {
));
assert_eq!(
test_err([BasicToken::Operator(Operator::Add), BasicToken::Integer(2)]),
ParseError::UnexpectedToken(BasicToken::Operator(Operator::Add))
test_err([
BasicToken::Operator(Operator::Add.into()),
BasicToken::Integer(2)
]),
ParseError::UnexpectedToken(BasicToken::Operator(Operator::Add.into()))
);
}
@ -321,7 +324,7 @@ fn test_ast_basics() {
test_build_ast("IF X < 0 THEN\nX = 0-X\nEND IF"),
BasicAstBlock::new([BasicAstInstruction::IfThenElse(
BasicAstExpression::Binary(
Operator::Lt,
Operator::Lt.into(),
Box::new(BasicAstExpression::Variable(String::from("X"))),
Box::new(BasicAstExpression::Integer(0))
),

@ -31,7 +31,7 @@ pub enum BasicToken {
Float(f64),
Name(String),
String(String),
Operator(Operator),
Operator(BasicOperator),
}
/// Transforms a raw string into a sequence of `BasicToken`s
@ -90,7 +90,7 @@ pub fn tokenize(raw: &str) -> Result<Vec<BasicToken>, ParseError> {
let match_integer = Regex::new(r"^[0-9]+").unwrap();
let match_assign = Regex::new(r"^=").unwrap();
let match_comma = Regex::new(r"^,").unwrap();
let match_operator = Regex::new(r"^(?:[+\-*/%]|//|[<>]=?|[!=]=|<>|<<|>>|&&|\|\|)").unwrap();
let match_operator = Regex::new(r"^(?:[+\-*/%\.]|//|[<>]=?|[!=]=|<>|<<|>>|&&|\|\|)").unwrap();
let match_label_end = Regex::new(r"^:").unwrap();
let match_paren = Regex::new(r"^(?:\(|\))").unwrap();
// TODO: handle escapes
@ -126,8 +126,8 @@ pub fn tokenize(raw: &str) -> Result<Vec<BasicToken>, ParseError> {
"loop" => BasicToken::Loop,
"gosub" => BasicToken::GoSub,
"return" => BasicToken::Return,
"and" => BasicToken::Operator(Operator::And),
"or" => BasicToken::Operator(Operator::Or),
"and" => BasicToken::Operator(Operator::And.into()),
"or" => BasicToken::Operator(Operator::Or.into()),
_ => unreachable!("{}", word),
}),
match_end => (BasicToken::End),
@ -136,22 +136,23 @@ pub fn tokenize(raw: &str) -> Result<Vec<BasicToken>, ParseError> {
match_integer(int) => (BasicToken::Integer(int.parse().unwrap())),
match_comma => (BasicToken::Comma),
match_operator(op) => (BasicToken::Operator(match op {
"+" => Operator::Add,
"-" => Operator::Sub,
"*" => Operator::Mul,
"//" => Operator::IDiv,
"/" => Operator::Div,
"%" => Operator::Mod,
"<" => Operator::Lt,
"<=" => Operator::Lte,
">" => Operator::Gt,
">=" => Operator::Gte,
"<<" => Operator::LShift,
">>" => Operator::RShift,
"==" => Operator::Eq,
"<>" | "!=" => Operator::Neq,
"&&" => Operator::And,
"||" => Operator::Or,
"+" => Operator::Add.into(),
"-" => Operator::Sub.into(),
"*" => Operator::Mul.into(),
"//" => Operator::IDiv.into(),
"/" => Operator::Div.into(),
"%" => Operator::Mod.into(),
"<" => Operator::Lt.into(),
"<=" => Operator::Lte.into(),
">" => Operator::Gt.into(),
">=" => Operator::Gte.into(),
"<<" => Operator::LShift.into(),
">>" => Operator::RShift.into(),
"==" => Operator::Eq.into(),
"<>" | "!=" => Operator::Neq.into(),
"&&" => Operator::And.into(),
"||" => Operator::Or.into(),
"." => BasicOperator::Sensor,
_ => unreachable!(),
})),
match_assign => (BasicToken::Assign),

@ -6,7 +6,11 @@ pub enum BasicAstExpression {
Float(f64),
Variable(String),
String(String),
Binary(Operator, Box<BasicAstExpression>, Box<BasicAstExpression>),
Binary(
BasicOperator,
Box<BasicAstExpression>,
Box<BasicAstExpression>,
),
Unary(UnaryOperator, Box<BasicAstExpression>),
}

@ -40,6 +40,8 @@ pub enum MindustryOperation {
End,
Operator(String, Operator, Operand, Operand),
UnaryOperator(String, UnaryOperator, Operand),
/// Copies the value of rhs into lhs
Set(String, Operand),
/// Reads the value at the `index`-th place in `cell`
Read {
@ -60,8 +62,14 @@ pub enum MindustryOperation {
/// Available to world processors only - flushes the print buffer to a global buffer
WorldPrintFlush(WorldPrintFlush),
// TODO: add RandOperator
Set(String, Operand),
/// Reads `key` from `object` and puts its result into `out_name`.
/// `object` may be a connection, an entity, a block, a unit type, etc.
Sensor {
out_name: String,
object: Operand,
key: Operand,
},
/// A generic operation, with the following invariants:
/// - all of the operands are read-only
/// - there is no external dependency to other variables
@ -74,63 +82,78 @@ pub enum MindustryOperation {
GenericMut(String, String, Vec<Operand>),
}
impl MindustryOperation {
pub(crate) fn operands(&self) -> Box<[&Operand]> {
match self {
Self::Jump(_) | Self::JumpLabel(_) | Self::End => Box::new([]),
Self::JumpIf(_label, _operator, lhs, rhs) => Box::new([lhs, rhs]),
Self::Operator(_target, _operator, lhs, rhs) => Box::new([lhs, rhs]),
Self::UnaryOperator(_target, _operator, value) => Box::new([value]),
Self::Set(_target, value) => Box::new([value]),
Self::Generic(_name, operands) => {
operands.iter().collect::<Vec<_>>().into_boxed_slice()
macro_rules! impl_operands {
(
both: {
$( $pattern:pat => $value:expr ),* $(,)?
},
mut: {
$( $pattern_mut:pat => $value_mut:expr ),* $(,)?
},
ref: {
$( $pattern_ref:pat => $value_ref:expr ),* $(,)?
} $(,)?
) => {
impl MindustryOperation {
pub(crate) fn operands(&self) -> Vec<&Operand> {
match self {
$(
$pattern => $value
),* ,
$(
$pattern_ref => $value_ref
),*
}
}
Self::GenericMut(_name, _out_name, operands) => {
operands.iter().collect::<Vec<_>>().into_boxed_slice()
pub(crate) fn operands_mut(&mut self) -> Vec<&mut Operand> {
match self {
$(
$pattern => $value
),* ,
$(
$pattern_mut => $value_mut
),*
}
}
Self::PrintFlush(cell) => Box::new([cell]),
Self::WorldPrintFlush(wpf) => match wpf {
WorldPrintFlush::Notify => Box::new([]),
WorldPrintFlush::Mission => Box::new([]),
WorldPrintFlush::Announce(time) => Box::new([time]),
WorldPrintFlush::Toast(time) => Box::new([time]),
},
Self::Print(operand) => Box::new([operand]),
Self::Read {
out_name: _,
cell,
index,
} => Box::new([cell, index]),
Self::Write { value, cell, index } => Box::new([value, cell, index]),
}
}
}
pub(crate) fn operands_mut(&mut self) -> Vec<&mut Operand> {
match self {
Self::Jump(_) | Self::JumpLabel(_) | Self::End => vec![],
Self::JumpIf(_label, _operator, lhs, rhs) => vec![lhs, rhs],
Self::Operator(_target, _operator, lhs, rhs) => vec![lhs, rhs],
Self::UnaryOperator(_target, _operator, value) => vec![value],
Self::Set(_target, value) => vec![value],
Self::Generic(_name, operands) => operands.iter_mut().collect::<Vec<_>>(),
Self::GenericMut(_name, _out_name, operands) => operands.iter_mut().collect::<Vec<_>>(),
Self::PrintFlush(cell) => vec![cell],
Self::WorldPrintFlush(wpf) => match wpf {
WorldPrintFlush::Notify => vec![],
WorldPrintFlush::Mission => vec![],
WorldPrintFlush::Announce(time) => vec![time],
WorldPrintFlush::Toast(time) => vec![time],
},
Self::Print(operand) => vec![operand],
Self::Read {
out_name: _,
cell,
index,
} => vec![cell, index],
Self::Write { value, cell, index } => vec![value, cell, index],
}
impl_operands!(
both: {
Self::Jump(_) | Self::JumpLabel(_) | Self::End => vec![],
Self::JumpIf(_label, _operator, lhs, rhs) => vec![lhs, rhs],
Self::Operator(_target, _operator, lhs, rhs) => vec![lhs, rhs],
Self::UnaryOperator(_target, _operator, value) => vec![value],
Self::Set(_target, value) => vec![value],
Self::PrintFlush(cell) => vec![cell],
Self::WorldPrintFlush(wpf) => match wpf {
WorldPrintFlush::Notify => vec![],
WorldPrintFlush::Mission => vec![],
WorldPrintFlush::Announce(time) => vec![time],
WorldPrintFlush::Toast(time) => vec![time],
},
Self::Print(operand) => vec![operand],
Self::Read {
out_name: _,
cell,
index,
} => vec![cell, index],
Self::Write { value, cell, index } => vec![value, cell, index],
Self::Sensor { out_name: _, object, key } => vec![object, key],
},
mut: {
Self::Generic(_name, operands) => operands.iter_mut().collect::<Vec<_>>(),
Self::GenericMut(_name, _out_name, operands) => operands.iter_mut().collect::<Vec<_>>(),
},
ref: {
Self::Generic(_name, operands) => operands.iter().collect::<Vec<_>>(),
Self::GenericMut(_name, _out_name, operands) => operands.iter().collect::<Vec<_>>(),
}
);
impl MindustryOperation {
pub(crate) fn mutates(&self, var_name: &str) -> bool {
match self {
Self::JumpLabel(_)
@ -152,6 +175,11 @@ impl MindustryOperation {
out_name,
cell: _,
index: _,
}
| Self::Sensor {
out_name,
object: _,
key: _,
} => out_name == var_name,
Self::Print(_) | Self::PrintFlush(_) | Self::WorldPrintFlush(_) => {
@ -177,6 +205,11 @@ impl MindustryOperation {
cell: _,
index: _,
}
| Self::Sensor {
out_name: _,
object: _,
key: _,
}
| Self::Print(_)
| Self::PrintFlush(_)
| Self::WorldPrintFlush(_)

@ -21,6 +21,30 @@ pub enum Operator {
Or,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum BasicOperator {
Operator(Operator),
Sensor,
}
impl BasicOperator {
pub(crate) fn precedence(self) -> u8 {
match self {
Self::Sensor => 20,
Self::Operator(op) => op.precedence(),
}
}
pub(crate) fn from_fn_name(raw: &str) -> Option<Self> {
match raw {
"max" => Some(Self::Operator(Operator::Max)),
"min" => Some(Self::Operator(Operator::Min)),
"pow" => Some(Self::Operator(Operator::Pow)),
_ => None,
}
}
}
impl Operator {
pub(crate) fn precedence(self) -> u8 {
match self {
@ -33,17 +57,25 @@ impl Operator {
_ => 128,
}
}
}
pub(crate) fn from_fn_name(raw: &str) -> Option<Self> {
match raw {
"max" => Some(Self::Max),
"min" => Some(Self::Min),
"pow" => Some(Self::Pow),
_ => None,
impl TryFrom<BasicOperator> for Operator {
type Error = ();
fn try_from(value: BasicOperator) -> Result<Self, Self::Error> {
match value {
BasicOperator::Operator(op) => Ok(op),
BasicOperator::Sensor => Err(()),
}
}
}
impl From<Operator> for BasicOperator {
fn from(value: Operator) -> Self {
Self::Operator(value)
}
}
pub(crate) fn format_operator(operator: Operator) -> &'static str {
match operator {
Operator::Eq => "equal",

@ -120,6 +120,9 @@ impl std::fmt::Display for MindustryProgram {
WorldPrintFlush::Toast(time) => writeln!(f, "message toast {}", time)?,
};
}
MindustryOperation::Sensor { out_name, object, key } => {
writeln!(f, "sensor {out_name} {object} {key}")?;
}
}
}

@ -66,12 +66,24 @@ fn translate_expression(
let mut right = translate_expression(right.as_ref(), namer, right_name.clone());
res.append(&mut right);
res.push(MindustryOperation::Operator(
target_name,
*op,
Operand::Variable(left_name),
Operand::Variable(right_name),
));
match op {
BasicOperator::Operator(op) => {
res.push(MindustryOperation::Operator(
target_name,
*op,
Operand::Variable(left_name),
Operand::Variable(right_name),
));
}
BasicOperator::Sensor => {
res.push(MindustryOperation::Sensor {
out_name: target_name,
object: Operand::Variable(left_name),
key: Operand::Variable(right_name),
});
}
}
res
}
@ -143,8 +155,8 @@ pub fn translate_ast(
String::from("__gosub_retaddr"),
Operator::Mul,
Operand::Variable(String::from("__gosub_retaddr")),
Operand::Integer(MAX_INSTRUCTION_COUNT as i64))
);
Operand::Integer(MAX_INSTRUCTION_COUNT as i64),
));
res.push(MindustryOperation::Operator(
String::from("__gosub_retaddr"),
Operator::Add,
@ -159,20 +171,20 @@ pub fn translate_ast(
String::from("__return"),
Operator::Mod,
Operand::Variable(String::from("__gosub_retaddr")),
Operand::Integer(MAX_INSTRUCTION_COUNT as i64))
);
Operand::Integer(MAX_INSTRUCTION_COUNT as i64),
));
res.push(MindustryOperation::Operator(
String::from("__gosub_retaddr"),
Operator::IDiv,
Operand::Variable(String::from("__gosub_retaddr")),
Operand::Integer(MAX_INSTRUCTION_COUNT as i64))
);
Operand::Integer(MAX_INSTRUCTION_COUNT as i64),
));
res.push(MindustryOperation::Operator(
String::from("@counter"),
Operator::Add,
Operand::Variable(String::from("__return")),
Operand::Integer(1))
);
Operand::Integer(1),
));
// Add a guard at the beginning of the program, to clear out the return address
has_return = true;
}
@ -414,7 +426,10 @@ pub fn translate_ast(
}
if has_return {
res.0.insert(0, MindustryOperation::Set(String::from("__gosub_retaddr"), Operand::Integer(0)));
res.0.insert(
0,
MindustryOperation::Set(String::from("__gosub_retaddr"), Operand::Integer(0)),
);
}
res

Loading…
Cancel
Save