com.github.mjvesa.herd.Interpreter.java Source code

Java tutorial

Introduction

Here is the source code for com.github.mjvesa.herd.Interpreter.java

Source

/*
 *  Copyright 2012 Matti Vesa
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you
 *  may not use this file except in compliance with the License. You may
 *  obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  permissions and limitations under the License.
 */
package com.github.mjvesa.herd;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Set;
import java.util.Stack;

import com.github.mjvesa.herd.wordset.ArithmeticWordSet;
import com.github.mjvesa.herd.wordset.FlowControlWordSet;
import com.github.mjvesa.herd.wordset.HeapWordSet;
import com.github.mjvesa.herd.wordset.InterpreterWordSet;
import com.github.mjvesa.herd.wordset.MiscWordSet;
import com.github.mjvesa.herd.wordset.SQLWordSet;
import com.github.mjvesa.herd.wordset.StackWordSet;
import com.github.mjvesa.herd.wordset.StringWordSet;
import com.github.mjvesa.herd.wordset.VaadinWordSet;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;

/**
 * Core interpreter. The outer interpreter reads words from the input stream
 * (source file) and parses them into executable words. The inner interpreter
 * simply executes defined words by executing their constituent words in
 * sequence.
 * 
 * Words execute themselves, which allows for WordSets that can be installed
 * dynamically. Defined words are executed in the interpreter, which is called
 * by the defined words execute() method.
 * 
 * Dictionary and heap space are separate.
 * 
 * @author mjvesa@vaadin.com
 * 
 */
public class Interpreter implements Presenter, Serializable {

    private static final long serialVersionUID = 3847825662484445132L;
    private HashMap<String, Word> dictionary;
    private HashMap<String, String> source;
    private Stack<Object> dataStack;
    private Stack<Integer> returnStack;
    private Stack<CompiledWord[]> codeStack;
    private Object[] heap;
    private DefinedWord currentDefinition; // These two are used to define new
                                           // words
    private ArrayList<CompiledWord> currentDefinitionWords;
    private boolean isCompiling;

    // ip and code represent the defined word currently being executed
    private int ip;
    private CompiledWord[] code;

    private Stack<Parser> parsers;
    private Parser parser;
    private Files files;
    private boolean logNewWords;
    private boolean logExecutedWords;

    private View view;

    /**
     * Default constructor
     */
    public Interpreter() {
        logNewWords = false;
        logExecutedWords = false;
    }

    /**
     * Does actual setting up of the interpreters.
     */
    public void setup() {
        setUpStorage();
        installWordSets();
        loadFiles();
    }

    private void loadFiles() {
        files = new Files();
        source = files.loadFiles();
    }

    public Set<String> getWordList() {
        return dictionary.keySet();
    }

    public Set<String> getFileList() {
        return source.keySet();
    }

    /**
     * Initializes stacks, dictionary and heap
     */
    private void setUpStorage() {
        dataStack = new Stack<Object>();
        returnStack = new Stack<Integer>();
        codeStack = new Stack<CompiledWord[]>();
        heap = new Object[2000];
        dictionary = new HashMap<String, Word>();
        parsers = new Stack<Parser>();
    }

    /**
     * Installs all the built-in wordsets.
     */
    private void installWordSets() {

        new ArithmeticWordSet().install(this);
        new FlowControlWordSet().install(this);
        new HeapWordSet().install(this);
        new InterpreterWordSet().install(this);
        new MiscWordSet().install(this);
        new SQLWordSet().install(this);
        new StackWordSet().install(this);
        new StringWordSet().install(this);
        new VaadinWordSet().install(this);
    }

    public void runFile(String command) {
        try {
            dataStack.clear();
            returnStack.clear();
            interpret(command);
            print("OK");
        } catch (Exception e) {
            print("ERROR: " + e.getClass().toString() + " -- Current word: " + code[ip].getName()
                    + " Location of error: line: " + code[ip].getLine() + " column: " + code[ip].getCol());
            e.printStackTrace();
        }
    }

    /**
     * Outer interpreter.
     * 
     * @param str
     */
    public void interpret(String str) {

        parsers.push(parser);
        isCompiling = false;
        parser = new Parser();
        parser.setString(str);

        String word = parser.getNextWord();

        while (!word.isEmpty()) {

            if (isCompiling) {
                compileWord(word);
            } else {

                if (dictionary.containsKey(word)) {
                    Word w = dictionary.get(word);
                    logExecutedWord(w);
                    w.execute(this);
                } else {
                    if (word.charAt(0) == '"') {
                        dataStack.push(word.substring(1, word.length() - 1));
                    } else if (Character.isDigit(word.charAt(0))) {
                        dataStack.push(Integer.valueOf(word));
                    } else {
                        print("INTERPRETER ERROR: Could not resolve: " + word + " " + parser.getPosition());
                    }
                }
            }
            word = parser.getNextWord();
        }

        parser = parsers.pop();
    }

    /**
     * Takes a word and finds its definition from the dictionary and compiles a
     * reference to it into the current definition.
     * 
     * @param word
     */
    private void compileWord(String word) {

        if (dictionary.containsKey(word)) {
            Word w = dictionary.get(word);
            if (w.isImmediate()) {
                w.execute(this);
            } else {
                currentDefinitionWords.add(new CompiledWord(w, parser.getLine(), parser.getCol()));
            }
        } else {
            if (word.charAt(0) == '"') {
                generateLiteral(word.substring(1, word.length() - 1));
            } else if (Character.isDigit(word.charAt(0))) {
                generateLiteral(Integer.valueOf(word));
            } else {
                print("COMPILER ERROR: Could not resolve: \"" + word + "\" " + parser.getPosition());
            }
        }
    }

    /**
     * 
     * Inner interpreter
     * 
     * Execution:
     * 
     * Base words are atomic, so just fetch and execute. Defined words consist
     * of other words. Obtain list of words and execute in series. Store
     * previous point in return stack so there will be no problems with nested
     * calls.
     */
    public void execute(Word word) {

        if (word == null) {
            Notification.show("Attempted to excute undefined word!", Type.ERROR_MESSAGE);
            return;
            // TODO return to main loop or stop interpreting when this happens.
        }
        logExecutedWord(word);
        word.execute(this);
    }

    /**
     * Executes a defined word. This is done in the Interpreter so that the
     * innards of the defined word are exposed.
     * 
     * @param word
     */
    public void executeDefinedWord(DefinedWord definedWord) {
        returnStack.push(ip);
        codeStack.push(code);
        code = definedWord.getCode();
        ip = 0;
        while (ip < code.length) {
            logExecutedWord(code[ip]);
            code[ip].execute(this);
            ip++;
        }
        ip = returnStack.pop();
        code = codeStack.pop();
    }

    private void logExecutedWord(Word word) {
        if (logExecutedWords) {
            print("Executing: " + word.getName());
        }
    }

    public Word getNextWord() {

        String s = getParser().getNextWord();
        Word word = getDictionary().get(s);
        if (word == null) {
            print("The word \"" + s + "\" has not been defined!");
            return null;
        }
        return word;
    }

    /* This can be used to skip filling words */
    public String getNextNonNopWord() {

        String word = parser.getNextWord();
        while (!word.isEmpty()) {
            Word w = dictionary.get(word);
            if (w instanceof DefinedWord && ((DefinedWord) w).getCode().length > 0) {
                return word;
            }
            word = parser.getNextWord();
        }
        return null;
    }

    /* Generates a word which pushes a literal onto the stack */
    public void generateLiteral(Object value) {
        Word w = dictionary.get("literal");
        CompiledWord cw = new CompiledWord(w, value);
        currentDefinitionWords.add(cw);
    }

    public void create() {
        createNewWord(parser.getNextWord());
    }

    public void createFromStack() {
        createNewWord((String) dataStack.pop());
    }

    public void anonCreate() {
        createNewWord("_anonymous_");
        dataStack.push(currentDefinition);
    }

    private void createNewWord(String name) {
        if (isCompiling) {
            // TODO is this the right thing to do? "Specs" seem kinda vague
            finishCompilation();
        }
        currentDefinition = new DefinedWord();
        view.addNewWord(name);
        currentDefinition.setName(name);
        dictionary.put(name, currentDefinition);
        currentDefinitionWords = new ArrayList<CompiledWord>();
        isCompiling = true;
    }

    /*
     * Used to finish compilation of either colon definitions or stuff made with
     * CREATE. Or anonymous colon definition.
     */
    public void finishCompilation() {
        String name = currentDefinition.getName();
        currentDefinition.setCode(currentDefinitionWords.toArray(new CompiledWord[0]));
        if (logNewWords) {
            print("ADDED: " + name);
        }
        isCompiling = false;
    }

    public void printStack() {
        ListIterator<Object> iterator = dataStack.listIterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            print("Class: " + o.getClass() + " -- [" + o.toString() + "]");
        }
    }

    public void print(String msg) {
        view.print(msg);
    }

    public void addSource(String name, String code) {

        files.saveFile(name, code);
        source.put(name, code);

    }

    public String getSource(String value) {
        return source.get(value);
    }

    public void setLogNewWords(boolean b) {
        logNewWords = b;
    }

    public void setLogExecutedWords(boolean b) {
        logExecutedWords = b;
    }

    public HashMap<String, Word> getDictionary() {
        return dictionary;
    }

    public Object popData() {
        return dataStack.pop();
    }

    public void pushData(Object obj) {
        dataStack.push(obj);
    }

    public Object peekData() {
        return dataStack.peek();
    }

    public Integer peekReturn() {
        return returnStack.peek();
    }

    public Integer popReturn() {
        return returnStack.pop();
    }

    public void pushReturn(Integer value) {
        returnStack.push(value);
    }

    public int getIp() {
        return ip;
    }

    public void setIp(int ip) {
        this.ip = ip;
    }

    public Word getExecutedWord() {
        return code[ip];
    }

    public int getCurrentDefinitionSize() {
        return currentDefinitionWords.size();
    }

    public CompiledWord getFromCurrentDefinition(int index) {
        return currentDefinitionWords.get(index);
    }

    public void addToCurrentDefinition(CompiledWord word) {
        currentDefinitionWords.add(word);

    }

    public CompiledWord peekCode(int i) {
        return code[i];
    }

    public int getCodeLength() {
        return code.length;
    }

    public Object peek(int index) {
        return heap[index];
    }

    public void poke(int index, Object value) {
        heap[index] = value;
    }

    public void setCompiling(boolean b) {
        isCompiling = b;
    }

    public Parser getParser() {
        return parser;
    }

    public DefinedWord getCurrentDefinition() {
        return currentDefinition;
    }

    public Object getCurrentParam() {
        return ((CompiledWord) getExecutedWord()).getParameter();
    }

    public ComponentContainer getMainPanel() {
        return view.getMainComponentContainer();
    }

    public Object peekHeap(int i) {
        return heap[i];
    }

    public void setView(View view) {
        this.view = view;
    }

    public View getView() {
        return this.view;
    }

}