Package com.sk89q.craftbook.mech.ic.plc.types

Source Code of com.sk89q.craftbook.mech.ic.plc.types.Perlstone32_1

/*   
Craftbook

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sk89q.craftbook.mech.ic.plc.types;

import com.sk89q.craftbook.access.WorldInterface;
import com.sk89q.craftbook.mech.ic.LogicChipState;
import com.sk89q.craftbook.mech.ic.plc.LogicPlcLang;
import com.sk89q.craftbook.util.BlockVector;
import com.sk89q.craftbook.util.SignText;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;

/**
* Language for CraftBook's PLC system. Hardcoded for VIVO or 3I3O layouts.
*
* @author olegbl
*/
public final class Perlstone32_1 extends LogicPlcLang {

    /**
     * Debugging switch (verbose mode).
     */
    private final boolean debug;

    public Perlstone32_1() {

        debug = false;
    }

    public Perlstone32_1(boolean debug) {

        this.debug = debug;
    }

    /**
     * Permanent shared storage.
     */
    private Map<String, int[]> publicPersistentStorage = new HashMap<String, int[]>();
    private Map<String, HashMap<BlockVector, int[]>> privatePersistentStorage = new HashMap<String,
            HashMap<BlockVector, int[]>>();

    public final String getName() {

        return "PS32 1.1.3";
    }

    private static int getPersistentStorageType(LogicChipState chip) {

        String type = chip.getText().getLine4().replace(" ", "");
        if (type.equalsIgnoreCase("private")) return 1; // Private
        else if (type.equals("")) return 2; // None
        return 0; // Public
    }

    public final boolean[] tick(String worldName, LogicChipState chip, String program) throws PerlstoneException {
        // checkSyntax(program); // Already done when placing sign.
        // (validateEnvironment)

        String[] staticFunctions = getStaticFunctions(program);

        int persistentStorageType = getPersistentStorageType(chip);

        if (!privatePersistentStorage.containsKey(worldName))
            privatePersistentStorage.put(worldName, new HashMap<BlockVector, int[]>());
        Map<BlockVector, int[]> privatePersistentStorage =
                this.privatePersistentStorage.get(worldName);

        if (persistentStorageType == 0) { // Public
            if (!publicPersistentStorage.containsKey(chip.getText().getLine4())) {
                publicPersistentStorage.put(chip.getText().getLine4(), new int[32]);
            }
        } else if (persistentStorageType == 1) { // Private
            if (!privatePersistentStorage.containsKey(chip.getBlockPosition())) {
                privatePersistentStorage.put(chip.getBlockPosition(), new int[32]);
            }
        }

        int[] gvt = new int[0]; // None
        if (persistentStorageType == 0) // Public
            gvt = publicPersistentStorage.get(chip.getText().getLine4());
        else if (persistentStorageType == 1) // Private
            gvt = privatePersistentStorage.get(chip.getBlockPosition());
        int[] tvt = new int[32];
        boolean[] output = new boolean[3];

        for (int i = 0; i < output.length && i < staticFunctions.length; i++) {
            output[i] = (callFunction(staticFunctions[i], new int[0], chip, gvt, tvt, staticFunctions,
                    new int[] {0}) != 0);
        }

        if (persistentStorageType == 0) { // Public
            publicPersistentStorage.put(chip.getText().getLine4(), gvt);
        } else if (persistentStorageType == 1) { // Private
            privatePersistentStorage.put(chip.getBlockPosition(), gvt);
        }

        return output;
    }

    private final int callFunction(String function, int[] args, LogicChipState chip, int[] pvt, int[] tvt,
                                   String[] staticf, int[] numOpcodes) throws PerlstoneException {

        boolean previousOpcode = false;

        try {
            Stack<Integer> stack = new Stack<Integer>();
            int[] lvt = new int[32];
            int[] jumpTable = buildJumpTable(function);

            if (debug) {
                System.out.println(" - Jump Table: " + Arrays.toString(jumpTable));
//                logger.log(Level.INFO, "[CraftBook] Perlstone32 - Jump Table: " + Arrays.toString(jumpTable));
            }

            for (int b : args) { stack.push(b); }

            String[] tokens = function.split("[ ;]");
            for (int i = 0; i < tokens.length; i++) {
                String token = tokens[i];

                String errorLocation = "§2" + ((i >= 2) ? (tokens[i - 2] + " ") : "") + ((i >= 1) ? (tokens[i - 1] +
                        " ") : "") + "§4" + tokens[i] + "§e" + " " + ((i < tokens.length - 1) ? (tokens[i + 1] + " ")
                                                                                              : "") + (
                        (i < tokens.length - 2) ? (tokens[i + 2]) : "") + "§c";

                numOpcodes[0]++;
                if (numOpcodes[0] == 25000)
                    throw new PerlstoneException(errorLocation + ": Too many opcodes. Check for infinite " +
                            "loop/recursion.");

                if (debug) {
                    if (previousOpcode) {
                        System.out.println(" - PVT: " + Arrays.toString(pvt));
                        System.out.println(" - TVT: " + Arrays.toString(tvt));
                        System.out.println(" - LVT: " + Arrays.toString(lvt));
                        System.out.println(" - Stack: " + stack);
//                        logger.log(Level.INFO, "[CraftBook] Perlstone32 - PVT: " + Arrays.toString(pvt));
//                        logger.log(Level.INFO, "[CraftBook] Perlstone32 - TVT: " + Arrays.toString(tvt));
//                        logger.log(Level.INFO, "[CraftBook] Perlstone32 - LVT: " + Arrays.toString(lvt));
//                        logger.log(Level.INFO, "[CraftBook] Perlstone32 - Stack: " + stack);
                    }
                    System.out.println("Opcode " + numOpcodes[0] + ": token # " + i + ": " + token + " in " + new
                            String(function));
                    previousOpcode = true;
                }

                boolean tokenIsInt = false;
                int tokenInt = 0;
                try {
                    tokenInt = Integer.parseInt(token);
                    tokenIsInt = true;
                } catch (NumberFormatException e) {
                }
                ;

                /* Contingencies */
                if (token.equals("")) continue;
                    /* Variable Functions */
                else if (token.equals("A")) stack.push(chip.getIn(1).is() ? 1 : 0);
                else if (token.equals("B")) stack.push(chip.getIn(2).is() ? 1 : 0);
                else if (token.equals("C")) stack.push(chip.getIn(3).is() ? 1 : 0);
                else if (token.equals("At")) stack.push(chip.getIn(1).isTriggered() ? 1 : 0);
                else if (token.equals("Bt")) stack.push(chip.getIn(2).isTriggered() ? 1 : 0);
                else if (token.equals("Ct")) stack.push(chip.getIn(3).isTriggered() ? 1 : 0);
                else if (token.startsWith("L")) {
                    char tT = (token.length() > 1) ? token.charAt(1) : (char) (stack.pop() + 0);
                    int[] table = null;
                    if (tT == 0 || tT == 'p') {
                        table = pvt;
                        if (getPersistentStorageType(chip) == 2)
                            throw new PerlstoneException(errorLocation + ": Accessing persistent storage when none " +
                                    "was specified.");
                    } else if (tT == 1 || tT == 't') table = tvt;
                    else if (tT == 2 || tT == 'l') table = lvt;
                    else if (tT == 'e') {
                        table = publicPersistentStorage.get(token.substring(2));
                        if (table == null)
                            throw new PerlstoneException(errorLocation + ": External storage specified does not exist" +
                                    ".");
                    } else throw new PerlstoneException(errorLocation + ": Illegal table specified.");
                    int index = (tT != 3 && tT != 'e' && token.length() > 2) ? Integer.parseInt(token.substring(2)) :
                                stack.pop();
                    stack.push(table[index]);
                } else if (token.startsWith("S")) {
                    char tT = (token.length() > 1) ? token.charAt(1) : (char) (stack.pop() + 0);
                    int[] table = null;
                    if (tT == 0 || tT == 'p') {
                        table = pvt;
                        if (getPersistentStorageType(chip) == 2)
                            throw new PerlstoneException(errorLocation + ": Accessing persistent storage when none " +
                                    "was specified.");
                    } else if (tT == 1 || tT == 't') table = tvt;
                    else if (tT == 2 || tT == 'l') table = lvt;
                    else if (tT == 'e') {
                        table = publicPersistentStorage.get(token.substring(2));
                        if (table == null)
                            throw new PerlstoneException(errorLocation + ": External storage specified does not exist" +
                                    ".");
                    } else throw new PerlstoneException(errorLocation + ": Illegal table specified.");
                    int index = (tT != 3 && tT != 'e' && token.length() > 2) ? Integer.parseInt(token.substring(2)) :
                                stack.pop();
                    table[index] = stack.pop();
                    if (tT == 3 || tT == 'e') publicPersistentStorage.put(token.substring(2), table);
                }
                /* Stack Functions */
                else if (tokenIsInt) stack.push(tokenInt);
                else if (token.startsWith("d")) {
                    int num = token.length() > 1 ? Integer.parseInt(token.substring(1)) : stack.pop();
                    for (int k = 0; k < num; k++) { stack.push(stack.peek()); }
                } else if (token.startsWith("p")) {
                    int num = token.length() > 1 ? Integer.parseInt(token.substring(1)) : stack.pop();
                    for (int k = 0; k < num; k++) { stack.pop(); }
                } else if (token.startsWith("v")) {
                    int num = token.length() > 1 ? Integer.parseInt(token.substring(1)) : stack.pop();
                    if (stack.size() < num + 1) throw new PerlstoneException(errorLocation + ": Stack too small.");
                    stack.push(stack.get(stack.size() - 1 - num));
                }
                /* Mutator Functions */
                else if (token.equals("+")) stack.push(stack.pop() + stack.pop());
                else if (token.equals("-")) {
                    int top = stack.pop();
                    stack.push(stack.pop() - top);
                } else if (token.equals("*")) stack.push(stack.pop() * stack.pop());
                else if (token.equals("/")) {
                    int top = stack.pop();
                    stack.push(stack.pop() / top);
                } else if (token.equals("%")) {
                    int top = stack.pop();
                    stack.push(stack.pop() % top);
                } else if (token.equals("^")) {
                    int top = stack.pop();
                    stack.push(stack.pop() ^ top);
                } else if (token.equals(">>")) {
                    int top = stack.pop();
                    stack.push(stack.pop() >> top);
                } else if (token.equals("<<")) {
                    int top = stack.pop();
                    stack.push(stack.pop() << top);
                } else if (token.equals("++")) stack.push(stack.pop() + 1);
                else if (token.equals("--")) stack.push(stack.pop() - 1);
                    /* Comparator Functions */
                else if (token.equals("==")) {
                    int top = stack.pop();
                    stack.push((stack.pop() == top) ? 1 : 0);
                } else if (token.equals("!=")) {
                    int top = stack.pop();
                    stack.push((stack.pop() != top) ? 1 : 0);
                } else if (token.equals(">")) {
                    int top = stack.pop();
                    stack.push((stack.pop() > top) ? 1 : 0);
                } else if (token.equals("<")) {
                    int top = stack.pop();
                    stack.push((stack.pop() < top) ? 1 : 0);
                } else if (token.equals(">=")) {
                    int top = stack.pop();
                    stack.push((stack.pop() >= top) ? 1 : 0);
                } else if (token.equals("<=")) {
                    int top = stack.pop();
                    stack.push((stack.pop() <= top) ? 1 : 0);
                }
                /* Logical Functions */
                else if (token.equals("!")) stack.push(stack.pop() == 0 ? 1 : 0);
                else if (token.equals("&")) {
                    int top = stack.pop();
                    stack.push((stack.pop() != 0 && top != 0) ? 1 : 0);
                } else if (token.equals("|")) {
                    int top = stack.pop();
                    stack.push((stack.pop() != 0 || top != 0) ? 1 : 0);
                } else if (token.equals("x")) {
                    int top = stack.pop();
                    int ptop = stack.pop();
                    stack.push(((ptop != 0 && top == 0) || (ptop == 0 && top != 0)) ? 1 : 0);
                }
                /* Flow Control Functions */
                else if (token.equals("R")) return 0;
                else if (token.equals("r")) return stack.pop();
                else if (token.startsWith("f")) {
                    int functionId = (token.length() > 1) ? Integer.parseInt(token.substring(1, 3)) : stack.pop();
                    int numArgs = (token.length() > 4) ? Integer.parseInt(token.substring(4)) : stack.pop();
                    int[] fArgs = new int[numArgs];
                    for (int j = numArgs - 1; j >= 0; j--) { fArgs[j] = stack.pop(); }
                    stack.push(callFunction(staticf[functionId], fArgs, chip, pvt, tvt, staticf, numOpcodes));
                } else if (token.equals("[")) {
                    if (stack.pop() == 0) i = jumpTable[i];
                } else if (token.equals("]")) {
                    if (stack.pop() != 0) i = jumpTable[i];
                }
                /* Unknown */
                else {
                    if (debug) {
                        System.out.println(i + " " + token + " " + new String(function));
//                        logger.log(Level.INFO, "[CraftBook] Perlstone32 " + i + " " + token + " " + new String
// (function));
                    }
                    throw new PerlstoneException(errorLocation + ": Unknown opcode.");
                }
            }
            return 0;
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new PerlstoneException("Premature end.", e);
        } catch (EmptyStackException e) {
            throw new PerlstoneException("Read empty stack.", e);
        }
    }

    public final String validateEnvironment(String w, Vector v, SignText t, String code) {

        if (privatePersistentStorage.containsKey(w))
            privatePersistentStorage.get(w).remove(v.toBlockVector());

        try {
            checkSyntax(code);
        } catch (PerlstoneException e) {
            return e.getMessage();
        }
        return null;
    }

    public final void checkSyntax(String program) throws PerlstoneException {

        String[] staticFunctions = getStaticFunctions(program);
        for (String f : staticFunctions) {
            checkFunctionSyntax(f);
            buildJumpTable(f);
        }
    }

    private static final void checkFunctionSyntax(String function) throws PerlstoneException {

        try {
            String[] tokens = function.split("[ ;]");
            for (int i = 0; i < tokens.length; i++) {
                String token = tokens[i];

                boolean tokenIsInt = false;
                try {
                    Integer.parseInt(token);
                    tokenIsInt = true;
                } catch (NumberFormatException e) {
                }
                ;

                String errorLocation = "§2" + ((i >= 2) ? (tokens[i - 2] + " ") : "") + ((i >= 1) ? (tokens[i - 1] +
                        " ") : "") + "§4" + tokens[i] + "§e" + " " + ((i < tokens.length - 1) ? (tokens[i + 1] + " ")
                                                                                              : "") + (
                        (i < tokens.length - 2) ? (tokens[i + 2]) : "") + "§c";

                /* Contingencies */
                if (token.equals(""))
                    ;
                    /* Variable Functions */
                else if (token.equals("A"))
                    ;
                else if (token.equals("B"))
                    ;
                else if (token.equals("C"))
                    ;
                else if (token.equals("At"))
                    ;
                else if (token.equals("Bt"))
                    ;
                else if (token.equals("Ct"))
                    ;
                else if (token.startsWith("L")) {
                    if (token.charAt(1) == 'e' && token.length() == 2)
                        throw new PerlstoneException(errorLocation + ": No external storage specified.");
                } else if (token.startsWith("S")) {
                    if (token.charAt(1) == 'e' && token.length() == 2)
                        throw new PerlstoneException(errorLocation + ": No external storage specified.");
                }
                /* Stack Functions */
                else if (tokenIsInt)
                    ;
                else if (token.startsWith("d")) {
                    try {
                        if (token.length() > 1) Integer.parseInt(token.substring(1));
                    } catch (NumberFormatException e) {
                        throw new PerlstoneException(errorLocation + ": Operand must be an integer.");
                    }
                } else if (token.startsWith("p")) {
                    try {
                        if (token.length() > 1) Integer.parseInt(token.substring(1));
                    } catch (NumberFormatException e) {
                        throw new PerlstoneException(errorLocation + ": Operand must be an integer.");
                    }
                } else if (token.startsWith("v")) {
                    try {
                        if (token.length() > 1) Integer.parseInt(token.substring(1));
                    } catch (NumberFormatException e) {
                        throw new PerlstoneException(errorLocation + ": Operand must be an integer.");
                    }
                }
                /* Mutator Functions */
                else if (token.equals("+"))
                    ;
                else if (token.equals("-"))
                    ;
                else if (token.equals("*"))
                    ;
                else if (token.equals("/"))
                    ;
                else if (token.equals("%"))
                    ;
                else if (token.equals("^"))
                    ;
                else if (token.equals(">>"))
                    ;
                else if (token.equals("<<"))
                    ;
                else if (token.equals("++"))
                    ;
                else if (token.equals("--"))
                    ;
                    /* Comparator Functions */
                else if (token.equals("=="))
                    ;
                else if (token.equals("!="))
                    ;
                else if (token.equals(">"))
                    ;
                else if (token.equals("<"))
                    ;
                else if (token.equals(">="))
                    ;
                else if (token.equals("<="))
                    ;
                    /* Logical Functions */
                else if (token.equals("!"))
                    ;
                else if (token.equals("&"))
                    ;
                else if (token.equals("|"))
                    ;
                else if (token.equals("x"))
                    ;
                    /* Flow Control Functions */
                else if (token.equals("R"))
                    ;
                else if (token.equals("r"))
                    ;
                else if (token.startsWith("f")) {
                    try {
                        if (token.length() > 1) Integer.parseInt(token.substring(1, 3));
                    } catch (NumberFormatException e) {
                        throw new PerlstoneException(errorLocation + ": First operand must be a 2-digit integer.");
                    }
                    try {
                        if (token.length() > 3) Integer.parseInt(token.substring(4));
                    } catch (NumberFormatException e) {
                        throw new PerlstoneException(errorLocation + ": Second operand must be an integer.");
                    }
                } else if (token.equals("["))
                    ;
                else if (token.equals("]"))
                    ;
                    /* Unknown */
                else throw new PerlstoneException(errorLocation + ": Unknown opcode.");
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new PerlstoneException("Array index out of bounds.", e);
        } catch (EmptyStackException e) {
            throw new PerlstoneException("Read empty stack.", e);
        }
    }

    private static String[] getStaticFunctions(String program) throws PerlstoneException {

        String[] staticFunctionStrings;
        /* scope */
        {
            staticFunctionStrings = program.replace("\n", "").replaceAll("  *", " ").split(":");
            if (staticFunctionStrings.length > 100) throw new PerlstoneException("Excess functions.");
        }
        return staticFunctionStrings;
    }

    private static int[] buildJumpTable(String function) throws PerlstoneException {

        String[] tokens = function.split("[ ;]");
        int length = tokens.length;
        int[] jumpTable = new int[length];
        Stack<Integer> stack = new Stack<Integer>();
        for (int i = 0; i < length; i++) {
            jumpTable[i] = -1;
            if (tokens[i].equals("[")) {
                stack.push(i);
            }
            if (tokens[i].equals("]")) {
                if (stack.isEmpty()) {
                    String errorLocation = "§2" + ((i >= 2) ? (tokens[i - 2] + " ") : "") + ((i >= 1) ? (tokens[i -
                            1] + " ") : "") + "§4" + tokens[i] + "§e" + " " + ((i < tokens.length - 1) ? (tokens[i +
                            1] + " ") : "") + ((i < tokens.length - 2) ? (tokens[i + 2]) : "") + "§c";
                    throw new PerlstoneException(errorLocation + ": Unmatched ] brace.");
                }
                int location = stack.pop();
                jumpTable[location] = i;
                jumpTable[i] = location;
            }
        }
        if (stack.size() != 0) {
            int i = stack.pop();
            String errorLocation = "§2" + ((i >= 2) ? (tokens[i - 2] + " ") : "") + ((i >= 1) ? (tokens[i - 1] + " ")
                                                                                              : "") + "§4" +
                    tokens[i] + "§e" + " " + (
                    (i < tokens.length - 1) ? (tokens[i + 1] + " ") : "") +
                    ((i < tokens.length - 2) ? (tokens[i + 2]) : "") + "§c";
            throw new PerlstoneException(errorLocation + ": Unmatched [ brace.");
        }
        return jumpTable;
    }

    /*------------------------------------------------------------------------\
    |State code                                                               |
    \------------------------------------------------------------------------*/
    public void writeCommonData(DataOutput out) throws IOException {

        out.write(0);
        out.writeInt(publicPersistentStorage.size());
        for (String key : publicPersistentStorage.keySet()) {
            out.writeUTF(key);
            int[] data = publicPersistentStorage.get(key);
            out.writeInt(data.length);
            for (int i : data) {
                if (i == 0) out.writeBoolean(false);
                else {
                    out.writeBoolean(true);
                    out.writeInt(i);
                }
            }
        }
    }

    public void readCommonData(DataInput in) throws IOException {

        Map<String, int[]> publicPersistentStorage =
                new HashMap<String, int[]>();

        if (in.readByte() != 0) throw new IOException("wrong version");

        int l = in.readInt();
        for (int j = 0; j < l; j++) {
            String name = in.readUTF();
            int[] data = new int[in.readInt()];
            for (int k = 0; k < data.length; k++) { if (in.readBoolean()) data[k] = in.readInt(); }
            publicPersistentStorage.put(name, data);
        }

        this.publicPersistentStorage = publicPersistentStorage;
    }

    public void resetCommonData() {

        publicPersistentStorage = new HashMap<String, int[]>();
    }


    public void writeWorldData(WorldInterface w, DataOutput out) throws IOException {

        String worldName = w.getUniqueIdString();

        HashMap<BlockVector, int[]> worldData =
                privatePersistentStorage.get(worldName);

        out.write(0);
        out.writeUTF(worldName);
        for (BlockVector key : worldData.keySet()) {
            out.writeInt(key.getBlockX());
            out.writeInt(key.getBlockY());
            out.writeInt(key.getBlockZ());
            int[] data = worldData.get(key);
            out.writeInt(data.length);
            for (int i : data) {
                if (i == 0) out.writeBoolean(false);
                else {
                    out.writeBoolean(true);
                    out.writeInt(i);
                }
            }
        }
    }

    public void readWorldData(WorldInterface w, DataInput in) throws IOException {

        String worldName = w.getUniqueIdString();

        HashMap<BlockVector, int[]> worldData =
                new HashMap<BlockVector, int[]>();

        if (in.readByte() != 0) throw new IOException("wrong version");

        int l = in.readInt();
        for (int j = 0; j < l; j++) {
            BlockVector v = new BlockVector(in.readInt(), in.readInt(), in.readInt());
            int[] data = new int[in.readInt()];
            for (int k = 0; k < data.length; k++) { if (in.readBoolean()) data[k] = in.readInt(); }
            worldData.put(v, data);
        }
        privatePersistentStorage.put(worldName, worldData);
    }

    public void resetWorldData(WorldInterface w) {

        privatePersistentStorage.remove(w.getUniqueIdString());
    }
}
TOP

Related Classes of com.sk89q.craftbook.mech.ic.plc.types.Perlstone32_1

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.