Browse Source

added drawing,getlink,read/write

rlbr-dev
Larry Xue 5 years ago
parent
commit
833d0cac64
  1. 39
      README.md
  2. 111
      c2logic/compiler.py
  3. 11
      c2logic/consts.py
  4. 48
      c2logic/instructions.py
  5. 9
      examples/drawing.c
  6. 20
      include/builtins.h

39
README.md

@ -6,44 +6,47 @@ Compiles C code to Mindustry logic. Still in beta, so compiled output may not be
`pip install git+https://github.com/SuperStormer/c2logic`
# Usage
# Documentation
Run the command line tool using:
`c2logic filename -O optimization_level`
where `filename` is a string and `optimization_level` is an optional integer
where `filename` is a string and `optimization_level` is an optional integer.
Optimization Level:
0. completely unoptimized.
1. the default
- modify variables without using a temporary
2. turns on some potentially unsafe optimizations
- augmented assignment and pre/postincrement/decrement don't modify \_\_rax
- augmented assignment and pre/postincrement/decrement don't modify `__rax`
- returning from main becomes equivalent to `end`
Locals are rewritten as _<varname>_<func_name>. Globals are unchanged.
Locals are rewritten as `_<varname>_<func_name>`. Globals are unchanged.
Special Variables:
- \_\_rax: similar to x86 rax
- \_\_rbx: stores left hand side of binary ops to avoid clobbering by the right side
- \_\_retaddr\_\*: stores return address of func call
- `__rax`: similar to x86 rax
- `__rbx`: stores left hand side of binary ops to avoid clobbering by the right side
- `__retaddr__<func_name>`: stores return address of func call
When developing your script, you can include `c2logic/builtins.h` located in the python include directory(location depends on system, mine is at `~/.local/include/python3.8/`)
When writing your code, you must include `c2logic/builtins.h`, which is located in the python include directory (location depends on system, mine is at `~/.local/include/python3.8/`).
See [examples](./examples) for API sample usage.
See [include/builtins.h](./include/builtins.h) for API definitions and [examples](./examples) for API sample usage.
# Documentation
# Supported Features
See `include/builtins.h` for API definitions.
- all Mindustry instructions as of BE
- all control flow structures except goto
- functions
- local/global variables
# Unsupported Features
- drawing
- getlink
- memory cell read/write
- structs
- enums
Some of these features may be worked around using `asm()`.
- structs - split it into multiple variables
- enums - use an int plus macros
- block scoped variables - just use locals
- typedefs - use macros
- pointers - don't use them
- goto - don't use it

111
c2logic/compiler.py

@ -7,10 +7,10 @@ from pycparser.c_ast import (
Compound, Constant, DeclList, Enum, FileAST, FuncDecl, Struct, TypeDecl
)
from .consts import builtins, func_binary_ops, func_unary_ops
from .consts import builtins, draw_funcs, func_binary_ops, func_unary_ops
from .instructions import (
BinaryOp, Enable, End, FunctionCall, Instruction, JumpCondition, Print, PrintFlush, Radar,
RawAsm, RelativeJump, Return, Sensor, Set, Shoot, UnaryOp
BinaryOp, Draw, DrawFlush, Enable, End, FunctionCall, GetLink, Instruction, JumpCondition,
Print, PrintFlush, Radar, RawAsm, Read, RelativeJump, Return, Sensor, Set, Shoot, UnaryOp, Write
)
@dataclass
@ -46,8 +46,6 @@ class Compiler(c_ast.NodeVisitor):
self.curr_function: Function = None
self.globals: list = None
self.loops: list = None
#self.loop_start: int = None
#self.loop_end_jumps: list = None
self.loop_end: int = None
def compile(self, filename: str):
@ -55,8 +53,6 @@ class Compiler(c_ast.NodeVisitor):
self.curr_function = None
self.globals = []
self.loops = []
#self.loop_start = None
#self.loop_end_jumps = None
self.loop_end = None
ast = parse_file(
filename, use_cpp=True, cpp_args=["-I", site.getuserbase() + "/include/python3.8"]
@ -122,8 +118,6 @@ class Compiler(c_ast.NodeVisitor):
if self.opt_level >= 1 and hasattr(top, "dest") and top.dest == "__rax":
#avoid indirection through __rax
self.curr_function.instructions[-1].dest = varname
#self.push(Set(varname, self.pop().src))
else:
self.push(Set(varname, "__rax"))
@ -168,6 +162,34 @@ class Compiler(c_ast.NodeVisitor):
break
return args
def get_unary_builtin_arg(self, args):
self.visit(args[0])
if self.can_avoid_indirection():
return self.pop().src
else:
return "__rax"
def get_binary_builtin_args(self, args, name):
left_name = f"__{name}_arg0"
self.visit(args[0])
self.set_to_rax(left_name)
self.visit(args[1])
left = left_name
right = "__rax"
if self.can_avoid_indirection():
right = self.pop().src
if self.can_avoid_indirection(left_name):
left = self.pop().src
return left, right
def get_multiple_psuedofunc_args(self, args):
argnames = []
for i, arg in enumerate(args):
self.visit(arg)
self.set_to_rax(f"__write_arg{i}")
argnames.append(f"__write_arg{i}")
return self.optimize_psuedofunc_args(argnames)
#visitors
def visit_FuncDef(self, node): # function definitions
func_name = node.decl.name
@ -311,11 +333,11 @@ class Compiler(c_ast.NodeVisitor):
self.curr_function.instructions
)
def visit_Break(self, node):
def visit_Break(self, node): #pylint: disable=unused-argument
self.push(RelativeJump(None, JumpCondition.always))
self.loops[-1].end_jumps.append(self.curr_offset())
def visit_Continue(self, node):
def visit_Continue(self, node): #pylint: disable=unused-argument
self.push(RelativeJump(self.loops[-1].start, JumpCondition.always))
def visit_Return(self, node):
@ -328,26 +350,18 @@ class Compiler(c_ast.NodeVisitor):
args = node.args.exprs
else:
args = []
#TODO avoid duplication in psuedo-function calls
#TODO avoid duplication in builtin calls
if name == "asm":
arg = args[0]
if not isinstance(arg, Constant) or arg.type != "string":
raise TypeError("Non-string argument to asm", node)
self.push(RawAsm(arg.value[1:-1]))
elif name in ("print", "printd"):
self.visit(args[0])
if self.can_avoid_indirection():
self.push(Print(self.pop().src))
else:
self.push(Print("__rax"))
self.push(Print(self.get_unary_builtin_arg(args)))
elif name == "printflush":
self.visit(args[0])
if self.can_avoid_indirection():
self.push(PrintFlush(self.pop().src))
else:
self.push(PrintFlush("__rax"))
self.push(PrintFlush(self.get_unary_builtin_arg(args)))
elif name == "radar":
args = []
argnames = []
for i, arg in enumerate(args):
if 1 <= i <= 4:
if not isinstance(arg, Constant) or arg.type != "string":
@ -356,9 +370,9 @@ class Compiler(c_ast.NodeVisitor):
else:
self.visit(arg)
self.set_to_rax(f"__radar_arg{i}")
args.append(f"__radar_arg{i}")
args = self.optimize_psuedofunc_args(args)
self.push(Radar("__rax", *args)) #pylint: disable=no-value-for-parameter
argnames.append(f"__radar_arg{i}")
argnames = self.optimize_psuedofunc_args(argnames)
self.push(Radar("__rax", *argnames)) #pylint: disable=no-value-for-parameter
elif name == "sensor":
self.visit(args[0])
self.set_to_rax("__sensor_arg0")
@ -374,36 +388,31 @@ class Compiler(c_ast.NodeVisitor):
left = self.pop().src
self.push(Sensor("__rax", left, right))
elif name == "enable":
self.visit(args[0])
self.set_to_rax("__enable_arg0")
self.visit(args[1])
left = "__enable_arg0"
right = "__rax"
if self.can_avoid_indirection():
right = self.pop().src
if self.can_avoid_indirection("__enable_arg0"):
left = self.pop().src
left, right = self.get_binary_builtin_args(args, "enable")
self.push(Enable(left, right))
elif name == "shoot":
args = []
for i, arg in enumerate(args):
self.visit(arg)
self.set_to_rax(f"__shoot_arg{i}")
args.append(f"__shoot_arg{i}")
args = self.optimize_psuedofunc_args(args)
self.push(Shoot(*args)) #pylint: disable=no-value-for-parameter
self.push(Shoot(*self.get_multiple_psuedofunc_args(args))) #pylint: disable=no-value-for-parameter
elif name == "get_link":
self.visit(args[0])
if self.can_avoid_indirection():
self.push(GetLink("__rax", self.pop().src))
else:
self.push(GetLink("__rax", "__rax"))
elif name == "read":
cell, index = self.get_binary_builtin_args(args, "read")
self.push(Read("__rax", cell, index))
elif name == "write":
self.push(Write(*self.get_multiple_psuedofunc_args(args))) #pylint: disable=no-value-for-parameter
elif name == "end":
self.push(End())
elif name in draw_funcs:
argnames = self.get_multiple_psuedofunc_args(args)
cmd = draw_funcs[name]
self.push(Draw(cmd, *argnames))
elif name == "drawflush":
self.push(DrawFlush(self.get_unary_builtin_arg(args)))
elif name in func_binary_ops:
self.visit(args[0])
self.set_to_rax("__binary_arg0")
self.visit(args[1])
left = "__binary_arg0"
right = "__rax"
if self.can_avoid_indirection():
right = self.pop().src
if self.can_avoid_indirection("__binary_arg0"):
left = self.pop().src
left, right = self.get_binary_builtin_args(args, "binary")
self.push(BinaryOp("__rax", left, right, name))
elif name in func_unary_ops:
self.visit(args[0])

11
c2logic/consts.py

@ -36,4 +36,13 @@ func_unary_ops = ["abs", "log", "log10", "sin", "cos", "tan", "floor", "ceil", "
binary_ops.update(dict(zip(func_binary_ops, func_binary_ops)))
unary_ops.update(dict(zip(func_unary_ops, func_unary_ops)))
builtins = ["print", "printd", "printflush", "radar", "sensor", "enable", "shoot", "end"]
draw_funcs = {
"draw" + func.lower(): func
for func in
["clear", "color", "stroke", "line", "rect", "lineRect", "poly", "linePoly", "triangle"]
}
builtins = [
"print", "printd", "printflush", "radar", "sensor", "enable", "shoot", "get_link", "read",
"write", "drawflush", "end"
] + list(draw_funcs.keys())

48
c2logic/instructions.py

@ -87,11 +87,11 @@ class Print(Instruction):
return f"print {self.val}"
class PrintFlush(Instruction):
def __init__(self, val: str):
self.val = val
def __init__(self, message: str):
self.message = message
def __str__(self):
return f"printflush {self.val}"
return f"printflush {self.message}"
class Radar(Instruction):
def __init__(
@ -135,6 +135,48 @@ class Shoot(Instruction):
def __str__(self):
return f"control shoot {self.obj} {self.x} {self.y} {self.shoot} 0"
class GetLink(Instruction):
def __init__(self, dest: str, index: str):
self.dest = dest
self.index = index
def __str__(self):
return f"getlink {self.dest} {self.index}"
class Read(Instruction):
def __init__(self, dest: str, src: str, index: str):
self.dest = dest
self.src = src
self.index = index
def __str__(self):
return f"read {self.dest} {self.src} {self.index}"
class Write(Instruction):
def __init__(self, dest: str, src: str, index: str):
self.dest = dest
self.src = src
self.index = index
def __str__(self):
return f"write {self.dest} {self.src} {self.index}"
class Draw(Instruction):
def __init__(self, cmd: str, *args):
self.cmd = cmd
self.args = args
def __str__(self):
args = list(self.args) + ['0'] * (6 - len(self.args))
return f"draw {self.cmd} {' '.join(args)}"
class DrawFlush(Instruction):
def __init__(self, display: str):
self.display = display
def __str__(self):
return f"drawflush {self.display}"
class End(Instruction):
def __str__(self):
return "end"

9
examples/drawing.c

@ -0,0 +1,9 @@
#include "c2logic/builtins.h"
extern struct MindustryObject display1;
void main(void) {
drawcolor(128, 128, 0);
drawpoly(40, 40, 6, 10, 0);
drawcolor(0, 128, 128);
drawpoly(40, 40, 3, 10, 180);
drawflush(display1);
}

20
include/builtins.h

@ -1,18 +1,34 @@
#ifndef MINDUSTRY_H
#define MINDUSTRY_H
struct MindustryObject {};
// void _asm(char* code);
// builtin instructions
void print(char* s);
void printd(double s);
void printflush(struct MindustryObject msg_block);
void printflush(struct MindustryObject message);
struct MindustryObject radar(struct MindustryObject obj, char* target1, char* target2,
char* target3, char* sort, double index);
double sensor(struct MindustryObject obj, char* prop);
void enable(struct MindustryObject obj, double enabled);
void shoot(struct MindustryObject obj, double x, double y, double shoot);
struct MindustryObject get_link(double index);
double read(struct MindustryObject cell, double index);
void write(struct MindustryObject cell, double index, double val);
void drawclear(double r, double g, double b);
void drawcolor(double r, double g, double b);
void drawstroke(double width);
void drawline(double x1, double y1, double x2, double y2);
void drawrect(double x, double y, double w, double h);
void drawlinerect(double x, double y, double w, double h);
void drawpoly(double x, double y, double sides, double radius, double rotation);
void drawlinepoly(double x, double y, double sides, double radius, double rotation);
void drawtriangle(double x1, double y1, double x2, double y2, double x3, double y3);
void drawflush(struct MindustryObject display);
void end();
// builtin binary operators
double pow(double x, double y);
double max(double x, double y);

Loading…
Cancel
Save