Working export

Now I just need to add more features
main
Shad Amethyst 9 months ago
parent 987c5f63b0
commit 1cb25f2d4f

@ -0,0 +1,3 @@
10 LET X = 0
20 X = X + 1
30 GOTO 20

@ -0,0 +1,292 @@
use regex::Regex;
use crate::parse::{BasicAstBlock, BasicAstExpression, Operator};
#[derive(Debug, Clone)]
pub enum Operand {
Variable(String),
String(String),
Integer(i64),
Float(f64),
}
impl std::fmt::Display for Operand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Operand::Variable(name) => write!(f, "{}", name),
Operand::String(string) => {
todo!();
// write!(f, "\"{}\"", string),
}
Operand::Integer(int) => write!(f, "{}", int),
Operand::Float(float) => write!(f, "{}", float),
}
}
}
#[derive(Debug, Clone)]
pub enum MindustryOperation {
JumpLabel(String),
Jump(String),
JumpIf(String, Operator, Operand, Operand),
Operator(String, Operator, Operand, Operand),
Set(String, Operand),
}
#[derive(Debug, Clone)]
pub struct MindustryProgram(Vec<MindustryOperation>);
impl MindustryProgram {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn push(&mut self, operation: MindustryOperation) {
self.0.push(operation);
}
pub fn append(&mut self, other: &mut Self) {
self.0.append(&mut other.0);
}
}
impl From<Vec<MindustryOperation>> for MindustryProgram {
fn from(value: Vec<MindustryOperation>) -> Self {
Self(value)
}
}
pub struct Namer {
var_index: usize,
label_index: usize,
prefix: String,
}
impl Default for Namer {
fn default() -> Self {
Self {
var_index: 0,
label_index: 0,
prefix: String::from("main"),
}
}
}
impl Namer {
fn temporary(&mut self) -> String {
let res = format!("{}__tmp_{}", self.prefix, self.var_index);
self.var_index += 1;
res
}
fn label(&mut self, suffix: &str) -> String {
let res = format!("{}__label_{}_{}", self.prefix, self.label_index, suffix);
self.label_index += 1;
res
}
}
fn translate_expression(
expression: &BasicAstExpression,
namer: &mut Namer,
target_name: String,
) -> MindustryProgram {
match expression {
BasicAstExpression::Integer(int) => {
vec![MindustryOperation::Set(target_name, Operand::Integer(*int))].into()
}
BasicAstExpression::Float(float) => {
vec![MindustryOperation::Set(target_name, Operand::Float(*float))].into()
}
BasicAstExpression::Variable(name) => vec![MindustryOperation::Set(
target_name,
Operand::Variable(name.clone()),
)]
.into(),
BasicAstExpression::Binary(op, left, right) => {
let left_name = namer.temporary();
let right_name = namer.temporary();
let mut res = translate_expression(left.as_ref(), namer, left_name.clone());
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),
));
res
}
}
}
pub fn translate_ast(basic_ast: &BasicAstBlock, namer: &mut Namer) -> MindustryProgram {
use crate::parse::BasicAstInstruction as Instr;
let mut res = MindustryProgram::new();
for instruction in basic_ast.instructions.iter() {
match instruction {
Instr::JumpLabel(label) => {
res.push(MindustryOperation::JumpLabel(label.clone()));
}
Instr::Jump(to) => {
res.push(MindustryOperation::Jump(to.clone()));
}
Instr::Assign(name, expression) => {
let mut instructions = translate_expression(expression, namer, name.clone());
res.append(&mut instructions);
}
Instr::IfThenElse(condition, true_branch, false_branch) => {
let condition_name = namer.temporary();
res.append(&mut translate_expression(
condition,
namer,
condition_name.clone(),
));
if false_branch.instructions.len() > 0 {
let else_label = namer.label("else");
let end_label = namer.label("endif");
res.push(MindustryOperation::JumpIf(
else_label.clone(),
Operator::Neq,
Operand::Variable(condition_name),
Operand::Variable(String::from("true")),
));
res.append(&mut translate_ast(true_branch, namer));
res.push(MindustryOperation::Jump(end_label.clone()));
res.push(MindustryOperation::JumpLabel(else_label));
res.append(&mut translate_ast(false_branch, namer));
res.push(MindustryOperation::JumpLabel(end_label));
} else {
let end_label = namer.label("endif");
res.push(MindustryOperation::JumpIf(
end_label.clone(),
Operator::Neq,
Operand::Variable(condition_name),
Operand::Variable(String::from("true")),
));
res.append(&mut translate_ast(true_branch, namer));
res.push(MindustryOperation::JumpLabel(end_label));
}
}
}
}
res
}
impl std::fmt::Display for MindustryProgram {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let numeric = Regex::new(r"^[0-9]+$").unwrap();
let numeric_jumps =
self.0
.iter()
.filter_map(|op| match op {
MindustryOperation::Jump(target)
| MindustryOperation::JumpIf(target, _, _, _) => Some(target),
_ => None,
})
.filter(|target| numeric.is_match(target))
.cloned()
.collect::<Vec<_>>();
for instruction in self.0.iter() {
match instruction {
MindustryOperation::JumpLabel(label) => {
if numeric.is_match(label) {
if numeric_jumps.contains(label) {
writeln!(f, "line__{}:", label)?;
}
} else {
writeln!(f, "{}:", label)?;
}
}
MindustryOperation::Jump(label) => {
if numeric.is_match(label) {
writeln!(f, "jump line__{} always 0 0", label)?;
} else {
writeln!(f, "jump {} always 0 0", label)?;
}
}
MindustryOperation::JumpIf(label, operator, lhs, rhs) => {
if numeric.is_match(label) {
writeln!(
f,
"jump line__{} {} {} {}",
label,
format_condition(*operator),
lhs,
rhs
)?;
} else {
writeln!(
f,
"jump {} {} {} {}",
label,
format_condition(*operator),
lhs,
rhs
)?;
}
}
MindustryOperation::Operator(name, operator, lhs, rhs) => {
writeln!(
f,
"op {} {} {} {}",
format_operator(*operator),
name,
lhs,
rhs
)?;
}
MindustryOperation::Set(name, value) => {
writeln!(f, "set {} {}", name, value)?;
}
}
}
Ok(())
}
}
fn format_condition(operator: Operator) -> &'static str {
match operator {
Operator::Eq => "equal",
Operator::Neq => "notEqual",
Operator::Lt => "lessThan",
Operator::Lte => "lessThanEqual",
Operator::Gt => "greaterThan",
Operator::Gte => "greaterThanEqual",
x => {
panic!("Operator {:?} is not a condition!", x);
}
}
}
fn format_operator(operator: Operator) -> &'static str {
match operator {
Operator::Eq => "equal",
Operator::Neq => "notEqual",
Operator::Lt => "lessThan",
Operator::Lte => "lessThanEqual",
Operator::Gt => "greaterThan",
Operator::Gte => "greaterThanEqual",
Operator::Add => "add",
Operator::Sub => "sub",
Operator::Mul => "mul",
Operator::Div => "div",
Operator::Mod => "mod",
Operator::RShift => "shr",
Operator::LShift => "shl",
}
}

@ -1,2 +1,3 @@
pub mod compile;
pub mod cursor;
pub mod parse;

@ -1,3 +1,15 @@
use basic_to_mindustry::{
compile::{translate_ast, Namer},
parse::{build_ast, tokenize},
};
fn main() {
println!("Hello, world!");
let path = std::env::args().nth(1).expect("Expected 1 argument");
let source = std::fs::read_to_string(path).expect("Couldn't read input file");
let tokens = tokenize(&source).unwrap();
let parsed = build_ast(&tokens).unwrap();
let transformed = translate_ast(&parsed, &mut Namer::default());
println!("{}", transformed);
}

@ -28,27 +28,13 @@ impl_op_basic_ast_expression!(std::ops::Mul, mul, Operator::Mul);
impl_op_basic_ast_expression!(std::ops::Div, div, Operator::Div);
#[derive(Clone, Debug, PartialEq)]
pub enum BasicAstOperation {
pub enum BasicAstInstruction {
JumpLabel(String),
Assign(String, BasicAstExpression),
Jump(String),
IfThenElse(BasicAstExpression, BasicAstBlock, BasicAstBlock),
}
#[derive(Clone, Debug, PartialEq)]
pub struct BasicAstInstruction {
pub label: Option<String>,
pub operation: BasicAstOperation,
}
impl From<BasicAstOperation> for BasicAstInstruction {
fn from(operation: BasicAstOperation) -> Self {
Self {
label: None,
operation,
}
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct BasicAstBlock {
pub instructions: Vec<BasicAstInstruction>,
@ -154,29 +140,27 @@ pub(crate) fn parse_expression(
pub fn build_ast(tokens: &[BasicToken]) -> Result<BasicAstBlock, ParseError> {
let mut tokens = Cursor::from(tokens);
let mut instructions = Vec::new();
let mut current_label: Option<String> = None;
while tokens.len() > 0 {
match tokens.peek(3) {
[BasicToken::NewLine, BasicToken::Integer(label), ..] => {
tokens.take(2);
current_label = Some(label.to_string());
instructions.push(BasicAstInstruction::JumpLabel(label.to_string()));
}
[BasicToken::NewLine, BasicToken::Name(label), BasicToken::LabelEnd, ..] => {
tokens.take(3);
current_label = Some(label.clone());
instructions.push(BasicAstInstruction::JumpLabel(label.clone()));
}
[BasicToken::NewLine, ..] => {
tokens.take(1);
current_label = None;
}
[BasicToken::Name(variable_name), BasicToken::Assign, ..] => {
tokens.take(2);
let expression = parse_expression(&mut tokens)?;
instructions.push(BasicAstInstruction {
label: current_label.take(),
operation: BasicAstOperation::Assign(variable_name.clone(), expression),
});
instructions.push(BasicAstInstruction::Assign(
variable_name.clone(),
expression,
));
}
[BasicToken::If, ..] => {
tokens.take(1);
@ -188,41 +172,29 @@ pub fn build_ast(tokens: &[BasicToken]) -> Result<BasicAstBlock, ParseError> {
let true_branch = build_ast(&tokens[(then_index + 1)..else_index])?;
let false_branch = build_ast(&tokens[(else_index + 1)..end_index])?;
instructions.push(BasicAstInstruction {
label: current_label.take(),
operation: BasicAstOperation::IfThenElse(
condition,
true_branch,
false_branch,
),
});
instructions.push(BasicAstInstruction::IfThenElse(
condition,
true_branch,
false_branch,
));
} else {
let true_branch = build_ast(&tokens[(then_index + 1)..end_index])?;
instructions.push(BasicAstInstruction {
label: current_label.take(),
operation: BasicAstOperation::IfThenElse(
condition,
true_branch,
BasicAstBlock::default(),
),
});
instructions.push(BasicAstInstruction::IfThenElse(
condition,
true_branch,
BasicAstBlock::default(),
));
}
tokens.take(end_index + 1);
}
[BasicToken::Goto, BasicToken::Integer(label), ..] => {
tokens.take(2);
instructions.push(BasicAstInstruction {
label: current_label.take(),
operation: BasicAstOperation::Jump(label.to_string()),
});
instructions.push(BasicAstInstruction::Jump(label.to_string()));
}
[BasicToken::Goto, BasicToken::Name(label), ..] => {
tokens.take(2);
instructions.push(BasicAstInstruction {
label: current_label.take(),
operation: BasicAstOperation::Jump(label.clone()),
});
instructions.push(BasicAstInstruction::Jump(label.clone()));
}
_ => {
if cfg!(test) {

@ -256,36 +256,33 @@ fn test_build_ast(raw: &str) -> BasicAstBlock {
fn test_ast_basics() {
assert_eq!(
test_build_ast("LET X = 2 * 5\n"),
BasicAstBlock::new([BasicAstOperation::Assign(
BasicAstBlock::new([BasicAstInstruction::Assign(
String::from("X"),
BasicAstExpression::Integer(2) * BasicAstExpression::Integer(5)
)
.into()])
)])
);
assert_eq!(
test_build_ast("IF X < 0 THEN\nX = 0-X\nEND IF"),
BasicAstBlock::new([BasicAstOperation::IfThenElse(
BasicAstBlock::new([BasicAstInstruction::IfThenElse(
BasicAstExpression::Binary(
Operator::Lt,
Box::new(BasicAstExpression::Variable(String::from("X"))),
Box::new(BasicAstExpression::Integer(0))
),
BasicAstBlock::new([BasicAstOperation::Assign(
BasicAstBlock::new([BasicAstInstruction::Assign(
String::from("X"),
BasicAstExpression::Integer(0) - BasicAstExpression::Variable(String::from("X"))
)
.into()]),
)]),
BasicAstBlock::default()
)
.into(),])
),])
);
assert_eq!(
test_build_ast("GOTO 10\nGOTO hello"),
BasicAstBlock::new([
BasicAstOperation::Jump(String::from("10")).into(),
BasicAstOperation::Jump(String::from("hello")).into(),
BasicAstInstruction::Jump(String::from("10")),
BasicAstInstruction::Jump(String::from("hello")),
])
);
}
@ -295,42 +292,26 @@ fn test_ast_labels() {
assert_eq!(
test_build_ast("10 LET X = 0\n20 LET Y = 2 * X\n"),
BasicAstBlock::new([
BasicAstInstruction {
label: Some(String::from("10")),
operation: BasicAstOperation::Assign(
String::from("X"),
BasicAstExpression::Integer(0)
)
},
BasicAstInstruction {
label: Some(String::from("20")),
operation: BasicAstOperation::Assign(
String::from("Y"),
BasicAstExpression::Integer(2)
* BasicAstExpression::Variable(String::from("X"))
)
},
BasicAstInstruction::JumpLabel(String::from("10")),
BasicAstInstruction::Assign(String::from("X"), BasicAstExpression::Integer(0)),
BasicAstInstruction::JumpLabel(String::from("20")),
BasicAstInstruction::Assign(
String::from("Y"),
BasicAstExpression::Integer(2) * BasicAstExpression::Variable(String::from("X"))
),
])
);
assert_eq!(
test_build_ast("start: LET X = 0\nmultiply: LET Y = 2 * X\n"),
BasicAstBlock::new([
BasicAstInstruction {
label: Some(String::from("start")),
operation: BasicAstOperation::Assign(
String::from("X"),
BasicAstExpression::Integer(0)
)
},
BasicAstInstruction {
label: Some(String::from("multiply")),
operation: BasicAstOperation::Assign(
String::from("Y"),
BasicAstExpression::Integer(2)
* BasicAstExpression::Variable(String::from("X"))
)
},
BasicAstInstruction::JumpLabel(String::from("start")),
BasicAstInstruction::Assign(String::from("X"), BasicAstExpression::Integer(0)),
BasicAstInstruction::JumpLabel(String::from("multiply")),
BasicAstInstruction::Assign(
String::from("Y"),
BasicAstExpression::Integer(2) * BasicAstExpression::Variable(String::from("X"))
),
])
);
}

Loading…
Cancel
Save