This commit is contained in:
Anuken 2020-08-07 17:50:54 -04:00
parent 33fdea7b7d
commit 090e0f35dc
7 changed files with 234 additions and 92 deletions

View File

@ -0,0 +1,99 @@
package mindustry.logic;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import mindustry.io.*;
import mindustry.logic.LCanvas.*;
import mindustry.logic.LExecutor.*;
/** "Compiles" a sequence of statements into instructions. */
public class LAssembler{
private transient int lastVar;
/** Maps names to variable IDs. */
ObjectMap<String, BVar> vars = new ObjectMap<>();
/** All instructions to be executed. */
LInstruction[] instructions;
/** Statement UI elements being processed. */
@Nullable Seq<StatementElem> elements;
public LAssembler(){
//add default constants
putConst("false", 0);
putConst("true", 1);
putConst("null", null);
}
public static LAssembler assemble(Seq<StatementElem> seq){
LAssembler out = new LAssembler();
out.elements = seq;
out.instructions = seq.map(s -> s.st.build(out)).toArray(LInstruction.class);
return out;
}
//TODO this is awful and confusing
public static LAssembler fromJson(String json){
LAssembler asm = new LAssembler();
LStatement[] statements = JsonIO.read(LStatement[].class, json);
for(LStatement s : statements){
s.afterLoad(asm);
}
asm.instructions = Seq.with(statements).map(l -> l.build(asm)).toArray(LInstruction.class);
return asm;
}
public String toJson(){
Seq<LStatement> states = elements.map(s -> s.st);
states.each(s -> s.beforeSave(this));
return JsonIO.write(states.toArray(LStatement.class));
}
/** @return a variable ID by name.
* This may be a constant variable referring to a number or object. */
public int var(String symbol){
symbol = symbol.trim();
try{
double value = Double.parseDouble(symbol);
//this creates a hidden const variable with the specified value
String key = "___" + value;
return putConst(key, value).id;
}catch(NumberFormatException e){
return putVar(symbol).id;
}
}
/** Adds a constant value by name. */
private BVar putConst(String name, Object value){
BVar var = putVar(name);
var.constant = true;
var.value = value;
return var;
}
/** Registers a variable name mapping. */
private BVar putVar(String name){
if(vars.containsKey(name)){
return vars.get(name);
}else{
BVar var = new BVar(lastVar++);
vars.put(name, var);
return var;
}
}
/** A saved variable. */
public static class BVar{
public int id;
public boolean constant;
public Object value;
public BVar(int id){
this.id = id;
}
BVar(){}
}
}

View File

@ -1,49 +0,0 @@
package mindustry.logic;
import arc.struct.*;
/** "Compiles" a sequence of statements into instructions. */
public class LBuilder{
private int lastVar;
/** Maps names to variable IDs. */
private ObjectIntMap<String> vars = new ObjectIntMap<>();
/** Maps variable IDs to their constant value. */
private IntMap<Object> constants = new IntMap<>();
public LBuilder(){
//add default constant variables
putConst("false", 0);
putConst("true", 1);
}
/** @return a variable ID by name.
* This may be a constant variable referring to a number or object. */
public int var(String symbol){
try{
double value = Double.parseDouble(symbol);
//this creates a hidden const variable with the specified value
String key = "___" + value;
return putConst(key, value);
}catch(NumberFormatException e){
return putVar(symbol);
}
}
/** Adds a constant value by name. */
private int putConst(String name, double value){
int id = putVar(name);
constants.put(id, value);
return id;
}
/** Registers a variable name mapping. */
private int putVar(String name){
if(vars.containsKey(name)){
return vars.get(name);
}else{
int id = lastVar++;
vars.put(name, id);
return id;
}
}
}

View File

@ -13,13 +13,16 @@ import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.io.*;
import mindustry.logic.LStatements.*;
import mindustry.ui.*;
public class LCanvas extends Table{
private static final Color backgroundCol = Pal.darkMetal.cpy().mul(0.1f), gridCol = Pal.darkMetal.cpy().mul(0.5f);
private static Seq<Runnable> postDraw = new Seq<>();
private Vec2 offset = new Vec2();
private DragLayout statements;
@ -32,11 +35,11 @@ public class LCanvas extends Table{
pane(statements).grow().get().setClip(false);
add(new LStatements.AssignStatement());
add(new AssignStatement());
add(new FetchBuildStatement());
add(new JumpStatement());
add(new ToggleStatement());
add(new LStatements.OpStatement());
add(new OpStatement());
}
private void drawGrid(){
@ -66,9 +69,35 @@ public class LCanvas extends Table{
}
void add(LStatement statement){
StatementElem e = new StatementElem(statement);
statements.addChild(new StatementElem(statement));
}
statements.addChild(e);
String save(){
return LAssembler.assemble(statements.getChildren().as()).toJson();
}
void load(String json){
statements.clearChildren();
LStatement[] statements = JsonIO.read(LStatement[].class, json);
for(LStatement st : statements){
add(st);
}
LAssembler asm = new LAssembler();
asm.elements = this.statements.getChildren().as();
for(LStatement st : statements){
st.afterLoad(asm);
}
this.statements.layout();
}
@Override
public void draw(){
postDraw.clear();
super.draw();
postDraw.each(Runnable::run);
}
public class DragLayout extends WidgetGroup{
@ -119,6 +148,7 @@ public class LCanvas extends Table{
seq.get(i).y -= shiftAmount;
}
}
}
@Override
@ -246,20 +276,22 @@ public class LCanvas extends Table{
}
public static class JumpButton extends ImageButton{
StatementElem to;
@NonNull Prov<StatementElem> to;
boolean selecting;
float mx, my;
public JumpButton(Color color, Cons<StatementElem> setter){
public JumpButton(Color color, @NonNull Prov<StatementElem> getter, Cons<StatementElem> setter){
super(Tex.logicNode, Styles.colori);
to = getter;
getStyle().imageUpColor = color;
addListener(new InputListener(){
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode code){
selecting = true;
setter.get(to = null);
setter.get(null);
mx = x;
my = y;
return true;
@ -277,18 +309,17 @@ public class LCanvas extends Table{
StatementElem elem = hovered();
if(elem != null && !isDescendantOf(elem)){
to = elem;
setter.get(elem);
}else{
to = null;
setter.get(null);
}
setter.get(to);
selecting = false;
}
});
update(() -> {
if(to != null && to.parent == null){
setter.get(to = null);
if(to.get() != null && to.get().parent == null){
setter.get(null);
}
});
}
@ -296,26 +327,39 @@ public class LCanvas extends Table{
@Override
public void draw(){
super.draw();
Element hover = to == null && selecting ? hovered() : to;
float tx = 0, ty = 0;
boolean draw = false;
if(hover != null){
tx = hover.getX(Align.right) + hover.translation.x;
ty = hover.getY(Align.right) + hover.translation.y;
draw = true;
}else if(selecting){
tx = x + mx;
ty = y + my;
draw = true;
}
if(draw){
drawCurve(x + width/2f, y + height/2f, tx, ty, color);
float s = width;
Tex.logicNode.draw(tx + s*0.75f, ty - s/2f, -s, s);
}
postDraw.add(() -> {
Element hover = to.get() == null && selecting ? hovered() : to.get();
float tx = 0, ty = 0;
boolean draw = false;
//capture coordinates for use in lambda
float rx = x, ry = y;
Element p = parent;
while(p != null){
rx += p.x;
ry += p.y;
p = p.parent;
}
if(hover != null){
tx = hover.getX(Align.right) + hover.translation.x;
ty = hover.getY(Align.right) + hover.translation.y;
draw = true;
}else if(selecting){
tx = rx + mx;
ty = ry + my;
draw = true;
}
if(draw){
drawCurve(rx + width/2f, ry + height/2f, tx, ty, color);
float s = width;
Tex.logicNode.draw(tx + s*0.75f, ty - s/2f, -s, s);
}
});
}
StatementElem hovered(){

View File

@ -20,6 +20,29 @@ public class LExecutor{
}
}
public void load(LAssembler builder){
vars = new Var[builder.vars.size];
instructions = builder.instructions;
counter = 0;
builder.vars.each((name, var) -> {
Var v = new Var();
vars[var.id] = v;
if(var.constant){
v.constant = true;
if(var.value instanceof Number){
v.numval = ((Number)var.value).doubleValue();
}else{
v.isobj = true;
v.objval = var.value;
}
}
});
}
//region utility
@Nullable Building building(int index){
Object o = vars[index].objval;
return o == null && o instanceof Building ? (Building)o : null;
@ -54,6 +77,8 @@ public class LExecutor{
v.isobj = true;
}
//endregion
static class Var{
boolean isobj, constant;

View File

@ -7,7 +7,15 @@ public abstract class LStatement{
public abstract void build(Table table);
public abstract LCategory category();
public abstract LExecutor.LInstruction build(LBuilder builder);
public abstract LExecutor.LInstruction build(LAssembler builder);
public void afterLoad(LAssembler assembler){
}
public void beforeSave(LAssembler assembler){
}
public String name(){
return getClass().getSimpleName().replace("Statement", "");

View File

@ -30,7 +30,7 @@ public class LStatements{
}
@Override
public LExecutor.LInstruction build(LBuilder builder){
public LInstruction build(LAssembler builder){
//TODO internal consts need to start with ___ and not be assignable to
return new LExecutor.AssignI(builder.var(from), builder.var(to));
}
@ -57,13 +57,13 @@ public class LStatements{
}
@Override
public LExecutor.LInstruction build(LBuilder builder){
public LExecutor.LInstruction build(LAssembler builder){
return new LExecutor.ToggleI(builder.var(target), builder.var(value));
}
}
public static class OpStatement extends LStatement{
public mindustry.logic.BinaryOp op = mindustry.logic.BinaryOp.add;
public BinaryOp op = BinaryOp.add;
public String a = "a", b = "b", dest = "result";
@Override
@ -79,7 +79,7 @@ public class LStatements{
TextButton[] button = {null};
button[0] = table.button(op.symbol, Styles.cleart, () -> {
op = mindustry.logic.BinaryOp.all[(op.ordinal() + 1) % BinaryOp.all.length];
op = BinaryOp.all[(op.ordinal() + 1) % BinaryOp.all.length];
button[0].setText(op.symbol);
}).size(50f, 30f).pad(4f).get();
@ -88,7 +88,7 @@ public class LStatements{
}
@Override
public LInstruction build(LBuilder builder){
public LInstruction build(LAssembler builder){
return new BinaryOpI(op,builder.var(a), builder.var(b), builder.var(dest));
}
@ -105,7 +105,7 @@ public class LStatements{
}
@Override
public LInstruction build(LBuilder builder){
public LInstruction build(LAssembler builder){
return new EndI();
}
@ -116,8 +116,9 @@ public class LStatements{
}
public static class JumpStatement extends LStatement{
public StatementElem dest;
public String condition = " true";
public transient StatementElem dest;
public int destIndex;
public String condition = "true";
@Override
public void build(Table table){
@ -125,12 +126,26 @@ public class LStatements{
table.field(condition, Styles.nodeField, str -> condition = str)
.size(100f, 40f).pad(2f).color(table.color);
table.add().growX();
table.add(new JumpButton(Color.white, s -> dest = s)).size(30).right().padRight(-17);
table.add(new JumpButton(Color.white, () -> dest, s -> dest = s)).size(30).right().padRight(-17);
}
//elements need separate conversion logic
@Override
public void afterLoad(LAssembler assembler){
if(assembler.elements != null){
dest = assembler.elements.get(destIndex);
}
}
@Override
public LInstruction build(LBuilder builder){
return new JumpI(builder.var(condition), dest == null ? -1 : dest.parent.getChildren().indexOf(dest));
public void beforeSave(LAssembler assembler){
destIndex = dest == null ? -1 : dest.parent.getChildren().indexOf(dest);
}
@Override
public LInstruction build(LAssembler builder){
beforeSave(builder);
return new JumpI(builder.var(condition),destIndex);
}
@Override
@ -159,7 +174,7 @@ public class LStatements{
}
@Override
public LExecutor.LInstruction build(LBuilder builder){
public LInstruction build(LAssembler builder){
return new FetchBuildI(builder.var(dest), builder.var(x), builder.var(y));
}

View File

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=c18b1b1bff478e44baabebe1a944b2fb4bd37bad
archash=746b415d20da18753c6f477361979621d4e78ac2