Package com.sk89q.craftbook.mechanics.ic.plc.lang

Source Code of com.sk89q.craftbook.mechanics.ic.plc.lang.Perlstone

// $Id$
/*
* Copyright (C) 2012 Lymia Aluysia <lymiahugs@gmail.com>
*
* 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.mechanics.ic.plc.lang;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Stack;

import org.bukkit.ChatColor;

import com.sk89q.craftbook.bukkit.util.BukkitUtil;
import com.sk89q.craftbook.mechanics.ic.ChipState;
import com.sk89q.craftbook.mechanics.ic.ICVerificationException;
import com.sk89q.craftbook.mechanics.ic.plc.PlcException;
import com.sk89q.craftbook.mechanics.ic.plc.PlcLanguage;

public class Perlstone implements PlcLanguage<boolean[], WithLineInfo<String>[]> {

    private static final int MAX_INSTRUCTION_COUNT = 10000;
    private static final int MAX_STACK_SIZE = 64;
    private static final int MAX_RECURSION = 16;
    private static final int PERLSTONE_STORE_VERSION = 0;

    @Override
    public String getName() {

        return "Perlstone-1.1";
    }

    @Override
    public boolean[] initState() {

        return new boolean[32];
    }

    private WithLineInfo<char[]> markLines(String code) {

        char[] chars = code.toCharArray();
        ArrayList<LineInfo> lines = new ArrayList<LineInfo>();
        int line = 1;
        int col = 1;
        for (char aChar : chars) {
            lines.add(new LineInfo(line, col));
            switch (aChar) {
                case '\n':
                    line++;
                    col = 0;
                    break;
                default:
                    break;
            }
            col++;
        }
        return new WithLineInfo<char[]>(lines.toArray(new LineInfo[lines.size()]), chars);
    }

    private char[] fixArray(Character[] c) {

        char[] o = new char[c.length];
        for (int i = 0; i < c.length; i++) {
            o[i] = c[i];
        }
        return o;
    }

    @SuppressWarnings("unchecked")
    private WithLineInfo<String>[] splitFunctions(WithLineInfo<char[]> chars) {

        ArrayList<WithLineInfo<String>> lines = new ArrayList<WithLineInfo<String>>();
        ArrayList<Character> current = new ArrayList<Character>();
        ArrayList<LineInfo> currentLineInfo = new ArrayList<LineInfo>();

        char[] data = chars.code;
        for (int i = 0; i < data.length; i++) {
            switch (data[i]) {
                case ':':
                    lines.add(new WithLineInfo<String>(currentLineInfo.toArray(new LineInfo[currentLineInfo.size()]),
                            new String(fixArray(current
                                    .toArray(new Character[current.size()])))));
                    current.clear();
                    currentLineInfo.clear();
                    break;
                case '\n':
                case ' ':
                    break;
                default:
                    current.add(data[i]);
                    currentLineInfo.add(chars.lineInfo[i]);
            }
        }
        lines.add(new WithLineInfo<String>(currentLineInfo.toArray(new LineInfo[currentLineInfo.size()]),
                new String(fixArray(current
                        .toArray(new Character[current.size()])))));
        return lines.toArray(new WithLineInfo[lines.size()]);
    }

    @Override
    public WithLineInfo<String>[] compile(String code) throws ICVerificationException {

        WithLineInfo<String>[] functions = splitFunctions(markLines(code));
        for (int l = 0; l < functions.length; l++) {
            WithLineInfo<String> line = functions[l];
            char[] chars = line.code.toCharArray();
            LineInfo[] li = line.lineInfo;
            int bracketCount = 0;
            if (chars.length == 0) {
                continue;
            }
            for (int i = 0; i < chars.length; i++) {
                try {
                    char c = chars[i];
                    switch (c) {
                        case '[':
                            bracketCount++;
                            break;
                        case ']':
                            if (bracketCount == 0)
                                throw new ICVerificationException("Too many closing braces " + "on line " + li[i]
                                        .line + " at column " + li[i].col);
                            bracketCount--;
                            break;
                        case '+':
                        case '-':
                        case 'A':
                        case 'B':
                        case 'C':
                        case 'd':
                        case 'p':
                        case 'x':
                        case '!':
                        case '|':
                        case '=':
                        case '&':
                        case '^':
                        case 's':
                        case 'r':
                            break;

                        case '<':
                        case '>':
                        case 'e':
                        case 'S':
                        case 'L':
                            switch (chars[++i]) {
                                case 'p':
                                case 'r':
                                case 'l':
                                case 'P':
                                case 'R':
                                case 'L':
                                    break;
                                default:
                                    throw new ICVerificationException("Unknown modifier " + chars[i] + " to opcode "
                                            + c + " " + "on line "
                                            + li[i].line + " at column " + li[i].col);
                            }
                            if (c == 'S' || c == 'L') {
                                char p = chars[++i];
                                if (!(p >= '0' || p <= '9') && !(p >= 'a' || p <= 'v'))
                                    throw new ICVerificationException("Bad table index " + chars[i] + " for opcode "
                                            + c + " " + "on line "
                                            + li[i].line + " at column " + li[i].col);
                            }
                            break;

                        case 'v': {
                            char n = chars[++i];
                            if (!(n >= '0' || n <= '9'))
                                throw new ICVerificationException("Bad peek depth " + chars[i] + " " + "on line " +
                                        li[i].line + " at column "
                                        + li[i].col);
                        }
                        break;

                        case '.':
                            for (int j = 0; j < 4; j++) {
                                switch (chars[++i]) {
                                    case '+':
                                    case '-':
                                    case '1':
                                    case '0':
                                        break;

                                    default:
                                        throw new ICVerificationException("Bad logic table value " + chars[i] + " " +
                                                "on line " + li[i].line
                                                + " at column " + li[i].col);
                                }
                            }
                            break;

                        case 'c':
                        case 't':
                            for (int j = 0; j < 2; j++) {
                                char n = chars[++i];
                                if (!(n >= '0' || n <= '9'))
                                    throw new ICVerificationException("Invalid character " + chars[i] + " in function" +
                                            " number " + "on line "
                                            + li[i].line + " at column " + li[i].col);
                            }
                            {
                                char n = chars[++i];
                                if (!(n >= '0' || n <= '9'))
                                    throw new ICVerificationException("Invalid character " + chars[i] + " in argument " +
                                            "count " + "on line "
                                            + li[i].line + " at column " + li[i].col);
                            }
                            break;

                        default:
                            throw new ICVerificationException("Unknown opcode " + c + " " + "on line " + li[i].line +
                                    " at column " + li[i].col);
                    }
                } catch (ArrayIndexOutOfBoundsException e) {
                    BukkitUtil.printStacktrace(e);
                    i = li.length - 1;
                    throw new ICVerificationException("Unexpected function end " + "around line " + li[i].line);
                }
            }
            if (bracketCount != 0)
                throw new ICVerificationException("Missing closing braces in function #" + l + " " + "               " +
                        "           starting on line "
                        + li[0].line + " and ending on line " + li[li.length - 1].line);
        }
        return functions;
    }

    @Override
    public void writeState(boolean[] state, DataOutputStream out) throws IOException {

        out.writeInt(PERLSTONE_STORE_VERSION);
        out.writeInt(state.length);
        for (boolean aState : state) {
            out.writeBoolean(aState);
        }
    }

    @Override
    public void loadState(boolean[] state, DataInputStream in) throws IOException {

        if (in.readInt() != PERLSTONE_STORE_VERSION) throw new IOException("incompatible save version");
        if (state.length != in.readInt()) throw new IOException("size mismatch!");
        for (int i = 0; i < state.length; i++) {
            state[i] = in.readBoolean();
        }
    }

    @Override
    public void execute(ChipState chip, boolean[] state, WithLineInfo<String>[] code) throws PlcException {

        boolean[] tt = new boolean[32];
        boolean a = chip.getInputCount() > 0 && chip.getInput(0);
        boolean b = chip.getInputCount() > 1 && chip.getInput(1);
        boolean c = chip.getInputCount() > 2 && chip.getInput(2);

        for (int i = 0; i < chip.getOutputCount(); i++) {
            if (i < code.length) {
                Boolean r = executeFunction(i, state, tt, code, a, b, c, new boolean[0], new int[1], 0);
                if (r == null) {
                    chip.setOutput(i, false);
                } else {
                    chip.setOutput(i, r);
                }
            } else {
                chip.setOutput(i, false);
            }
        }
    }

    @Override
    public boolean supports(String lang) {

        return false;
    }

    private int mod(int a, int b) {

        return (a % b + b) % b;
    }

    private int decodeAddress(char c, int shift) {

        if (c >= '0' && c <= '9') return mod(c - '0' + shift, 32);
        else return mod(c - 'a' + 10 + shift, 32);
    }

    private int parseNumber(char c) {

        return c - '0';
    }

    private boolean parseTableChar(char c) throws PlcException {

        switch (c) {
            case '+':
                return true;
            case '-':
                return false;
            case '1':
                return true;
            case '0':
                return false;

                // Shouldn't happen because of validation.
            default:
                throw new PlcException("invalid table", "Invalid character in logic table.");
        }
    }

    private String errmsg(String err, int fno, char opcode, LineInfo li, boolean[] pt, boolean[] tt, boolean[] lt,
            int pshift, int tshift,
            int lshift, Stack<Boolean> stack, int tc) {

        String errm = "";
        if (!err.startsWith(ChatColor.RED + "Detailed Error Message: ")) {
            errm += ChatColor.RED + "Detailed Error Message: " + ChatColor.RESET + err + "\n";
            errm += ChatColor.RED + "Persistent Variable Table: \n " + ChatColor.RESET + dumpStateText(pt) + "\n";
            errm += ChatColor.RED + " - Shift: " + ChatColor.RESET + pshift + "\n";
            errm += ChatColor.RED + "Temp Variable Table: \n " + ChatColor.RESET + dumpStateText(tt) + "\n";
            errm += ChatColor.RED + " - Shift: " + ChatColor.RESET + tshift + "\n";
        } else {
            errm += err + "\n";
        }
        errm += ChatColor.RED + "====\n";
        if (tc > 0) {
            errm += "(" + tc + " tail call" + (tc > 1 ? "s" : "") + " omitted)\n====\n";
        }
        errm += ChatColor.RED + "Location: " + ChatColor.RESET + "Opcode " + opcode + " at line " + li.line + ", " +
                "column " + li.col + " in function #"
                + fno + "\n";
        errm += ChatColor.RED + "Local Variable Table: \n " + ChatColor.RESET + dumpStateText(lt) + "\n";
        errm += ChatColor.RED + " - Shift: " + ChatColor.RESET + lshift + "\n";
        errm += ChatColor.RED + "Function Stack: " + ChatColor.RESET + dumpStateText(stack.toArray(new Boolean[stack
                                                                                                               .size()]));
        return errm;
    }

    // Use wrapper type to be able to express "no return" as null
    private Boolean executeFunction(int fno, boolean[] pt, boolean[] tt, WithLineInfo<String>[] funs, boolean a,
            boolean b, boolean c,
            boolean[] args, int[] opc, int rec) throws PlcException {

        int tailcalls = 0;

        // Wrap in an while(true) to allow the t opcode to be a tail call.
        outer:
            while (true) {
                String fn = funs[fno].code;
                LineInfo[] lis = funs[fno].lineInfo;
                char[] code = fn.toCharArray();
                int[] jt = new int[code.length]; // Jump table so that [ and ] aren't that messy
                /* scope */
                {
                    Stack<Integer> bracketStack = new Stack<Integer>();
                    for (int i = 0; i < code.length; i++) {
                        char ch = code[i];
                        if (ch == '[') {
                            bracketStack.push(i);
                        } else if (ch == ']') {
                            int j = bracketStack.pop();
                            jt[i] = j;
                            jt[j] = i;
                        }
                    }
                }

                int ip = 0;
                Stack<Boolean> executionStack = new Stack<Boolean>();
                for (boolean arg1 : args) {
                    executionStack.push(arg1);
                }
                boolean[] lt = new boolean[32];
                int pshift = 0;
                int tshift = 0;
                int lshift = 0;
                char op = '?';

                LineInfo li = new LineInfo(0, 0);
                try {
                    if (rec > MAX_RECURSION)
                        throw new PlcException("stack overflow", "Aborted due to too many recursive non-tail calls.");
                    try {
                        while (ip < code.length) {
                            opc[0]++;
                            if (opc[0] == MAX_INSTRUCTION_COUNT)
                                throw new PlcException("ran too long", "Aborted due to running too many instructions in " +
                                        "one update");
                            if (executionStack.size() > MAX_STACK_SIZE)
                                throw new PlcException("stack too big", "Aborted due to too many values pushed onto stack" +
                                        ".");
                            op = code[ip];
                            li = lis[ip];
                            switch (op) {
                                case '+':
                                    executionStack.push(true);
                                    break;
                                case '-':
                                    executionStack.push(false);
                                    break;

                                case 'A':
                                    executionStack.push(a);
                                    break;
                                case 'B':
                                    executionStack.push(b);
                                    break;
                                case 'C':
                                    executionStack.push(c);
                                    break;

                                case '<':
                                case '>':
                                case 'e': {
                                    int mul = 1;
                                    int add = 0;
                                    switch (code[ip]) {
                                        case '<':
                                            add = -1;
                                            break;
                                        case '>':
                                            add = +1;
                                            break;
                                        case 'e':
                                            mul = 0;
                                            break;
                                        default:
                                            break;
                                    }

                                    switch (code[++ip]) {
                                        case 'p':
                                        case 'P':
                                            pshift = mul * pshift + add;
                                            break;
                                        case 't':
                                        case 'T':
                                            tshift = mul * tshift + add;
                                            break;
                                        case 'l':
                                        case 'L':
                                            lshift = mul * lshift + add;
                                            break;
                                        default:
                                            break;
                                    }
                                }
                                break;

                                case 'S':
                                case 'L': {
                                    boolean[] table = null;
                                    int shift = 0;

                                    switch (code[++ip]) {
                                        case 'p':
                                            shift = pshift;
                                        case 'P':
                                            table = pt;
                                            break;
                                        case 't':
                                            shift = tshift;
                                        case 'T':
                                            table = tt;
                                            break;
                                        case 'l':
                                            shift = lshift;
                                        case 'L':
                                            table = lt;
                                            break;
                                        default:
                                            break;
                                    }
                                    if(table == null)
                                        break;

                                    int add = decodeAddress(code[++ip], shift);
                                    if (op == 'S')
                                        table[add] = executionStack.pop();
                                    else
                                        executionStack.push(table[add]);
                                }
                                break;

                                case 'd':
                                    executionStack.push(executionStack.peek());
                                    break;
                                case 'p':
                                    executionStack.pop();
                                    break;
                                case 'v':
                                    try {
                                        int level = parseNumber(code[++ip]);
                                        executionStack.push(executionStack.get(executionStack.size() - 1 - level));
                                    } catch (ArrayIndexOutOfBoundsException e) {
                                        throw new PlcException("bad stack pos", "Attempted to call peek on too small a " +
                                                "stack.");
                                    }
                                    break;
                                case 'x':
                                    boolean x = executionStack.pop();
                                    boolean y = executionStack.pop();
                                    executionStack.push(x);
                                    executionStack.push(y);
                                    break;
                                default:
                                    break;

                                case '!':
                                    executionStack.push(!executionStack.pop());
                                    break;

                                    // Using the short-circuiting versions would cause it to sometimes pop one less
                                    // value than it should.
                                case '^':
                                    executionStack.push(executionStack.pop() ^ executionStack.pop());
                                    break;
                                case '&':
                                    executionStack.push(executionStack.pop() & executionStack.pop());
                                    break;
                                case '|':
                                    executionStack.push(executionStack.pop() | executionStack.pop());
                                    break;
                                case '=':
                                    executionStack.push(executionStack.pop() == executionStack.pop());
                                    break;

                                case '.':
                                    boolean ta = parseTableChar(code[++ip]);
                                    boolean tb = parseTableChar(code[++ip]);
                                    boolean tc = parseTableChar(code[++ip]);
                                    boolean td = parseTableChar(code[++ip]);

                                    boolean e = executionStack.pop();
                                    boolean f = executionStack.pop();

                                    if (!e && !f) {
                                        executionStack.push(ta);
                                    } else if (!e && f) {
                                        executionStack.push(tb);
                                    } else if (e && !f) {
                                        executionStack.push(tc);
                                    } else {
                                        executionStack.push(td);
                                    }
                                    break;

                                case 'c':
                                case 't':
                                    int n = parseNumber(code[++ip]) * 10 + parseNumber(code[++ip]);
                                    int nArgs = parseNumber(code[++ip]);
                                    boolean[] arg = new boolean[nArgs];

                                    if (n < 0 || n >= funs.length)
                                        throw new PlcException("func not found", "Attempted to call nonexistent function " +
                                                "#" + n);

                                    if (op == 'c') {
                                        for (int i = nArgs - 1; i >= 0; i--) {
                                            arg[i] = executionStack.pop();
                                        }
                                        Boolean v = executeFunction(n, pt, tt, funs, a, b, c, arg, opc, rec + 1);
                                        if (v != null) {
                                            executionStack.push(v);
                                        }
                                        break;
                                    } else {
                                        fno = n;
                                        args = arg;
                                        tailcalls++;
                                        continue outer;
                                    }

                                case '[':
                                    if (!executionStack.pop()) {
                                        ip = jt[ip];
                                    }
                                    break;
                                case ']':
                                    if (executionStack.pop()) {
                                        ip = jt[ip];
                                    }
                                    break;

                                case 's':
                                    return null;
                                case 'r':
                                    return executionStack.pop();
                            }
                            ip++;
                        }
                    } catch (EmptyStackException e) {
                        throw new PlcException("empty stack", "Popped while stack was empty.");
                    } catch (StackOverflowError e) {
                        throw new PlcException("stack overflow", "Java stack overflow.");
                    }
                } catch (PlcException e) {
                    throw new PlcException(e.getMessage(), errmsg(e.detailedMessage, fno, op, li, pt, tt, lt, pshift,
                            tshift, lshift, executionStack,
                            tailcalls));
                }
                return null;
            }
    }

    private String dumpStateText(boolean[] state) {

        char[] c = new char[state.length];
        for (int i = 0; i < state.length; i++) {
            c[i] = state[i] ? '1' : '0';
        }
        return new String(c);
    }

    private String dumpStateText(Boolean[] state) {

        char[] c = new char[state.length];
        for (int i = 0; i < state.length; i++) {
            c[i] = state[i] ? '1' : '0';
        }
        return new String(c);
    }

    @Override
    public String dumpState(boolean[] state) {

        return ChatColor.RED + "Persistent Variable Table: \n " + ChatColor.RESET + dumpStateText(state);
    }
}
TOP

Related Classes of com.sk89q.craftbook.mechanics.ic.plc.lang.Perlstone

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.