/* BASIC Interpreter 
 *      for ttps://www.shroudoftheavatar.com/?p=39149#more-39149
 *          ttps://d2sx9mrt4zumaq.cloudfront.net/wp-content/uploads/2014/04/DND1_Complete.pdf
 *
 * Version 0.5a (未完成公開)
 *
 * 実装履歴
 *   2014-04-30 v0.1a (作業時間:約6時間)
 *      ・コマンドライン入力文のトークン分解
 *      ・プログラムリストの入力
 *      ・プログラムリストの出力(LIST)
 *      ・プログラムリストの開始(RUN)
 *      ・ジャンプ系の仮実装(GOTO,GOSUB,RETURN)
 *      ・配列添字の最小値設定の仮実装(BASE)
 *   2014-05-01 v0.2a (作業時間:約0.5時間)
 *      ・プログラムリストの出力の修正(LIST)
 *   2014-05-04 v0.3a (作業時間:約8時間)
 *      ・式の計算(演算子等,但し配列変数を除く)
 *      ・値の出力(PRINT)
 *      ・変数への代入(LET,但し配列変数を除く)
 *      ・関数(INT,ABS,RND,CLK,(VAL))
 *   2014-05-05 v0.4a (作業時間:約5時間)
 *      ・配列変数の操作(DIM,LET,式の計算)
 *   2014-05-06 v0.5a (作業時間:約3.5時間)
 *      ・enumの比較をequals()から==に変更した(意味あるのこれ？)
 *		・一部メソッドの使い方を変更
 *      ・変数の管理を分離した
 */
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.regex.*;

class Basic
{
	static final int major = 0;
	static final int minor = 5;
	static final char release = 'a';
	
	public static void main (String[] args) throws java.lang.Exception
	{
		System.out.printf("BASIC Interpreter - Version %d.%d%c", major, minor, release);
		System.out.println();
		System.out.println("Your code goes here!");
		System.out.println();
		try {
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			Excutor excutor = new Excutor();
			String line;
			while ((line = in.readLine()) != null) {
				try {
					List<Token> tokens = Tokenizer.tokenize(line);
					if (excutor.doEditorCommand(tokens) < 0) {
						break;
					}
				} catch (SyntaxError er) {
					System.out.println(er);
				}
			}
//			System.out.println(excutor);
		} catch (Exception ex) {
			System.out.println(ex);
		}
		
	}
}


enum TokenType
{
	INT, DBL, STR, NAME, SYMBL, SHARP, SEMICOLON,
	OP_BRKT, CL_BRKT, COMMA, COLON, UNKNOWN
}

final class Token
{
	final TokenType type;
	final String token;
	
	public Token(TokenType type, String token)
	{
		this.type = type;
		this.token = token;
	}
	
	public TokenType getType() { return type; }
	public String getToken() { return token; }
	
	@Override
	public String toString()
	{
		return "[" + type + " : " + token + "]";
	}
	
	public boolean tokenIs(String value)
	{
		return token.equals(value);
	}
	
	public boolean isInt()       { return TokenType.INT       == type; }
	public boolean isStr()       { return TokenType.STR       == type; }
	public boolean isDbl()       { return TokenType.DBL       == type; }
	public boolean isName()      { return TokenType.NAME      == type; }
	public boolean isSymbl()     { return TokenType.SYMBL     == type; }
	public boolean isOpBrkt()    { return TokenType.OP_BRKT   == type; }
	public boolean isClBrkt()    { return TokenType.CL_BRKT   == type; }
	public boolean isColon()     { return TokenType.COLON     == type; }
	public boolean isSemiColon() { return TokenType.SEMICOLON == type; }
	public boolean isComma()     { return TokenType.COMMA     == type; }
	public boolean isSharp()     { return TokenType.SHARP     == type; }
}

class SyntaxError extends java.lang.Exception
{
	public SyntaxError(String msg) { super(msg); }
	public SyntaxError(Throwable ex) { super(ex); }
	public SyntaxError(List<Token> tokens, int index)
	{
		super("token index(" + index + "): " + tokens.toString());
	}
}

final class TokenError extends SyntaxError
{
	public TokenError(String line) { super(line); }
}

final class Tokenizer
{
	private static Pattern p_space = Pattern.compile("^\\s+");
	private static Pattern p_int = Pattern.compile("^\\d+");
	private static Pattern p_dbl = Pattern.compile("^\\d*\\.\\d+");
	private static Pattern p_str = Pattern.compile("^\"(\"\"|[^\"])*\"");
	private static Pattern p_name = Pattern.compile("^[A-Za-z][A-Za-z0-9]*\\$?");
	private static Pattern p_symbl = Pattern.compile("^(>=|<=|<>|[=<>+\\*/-])");
	
	public static List<Token> tokenize(String line) throws TokenError
	{
		List<Token> list = new ArrayList<Token>();
		Matcher m = null;
		TokenType type = null;
		String token = null;
		int offset = 0;
		
		while (line.length() > 0) {
			if ((m = p_space.matcher(line)).lookingAt()) {
				line = line.substring(m.end());
				continue;
			} else if ((m = p_dbl.matcher(line)).lookingAt()) {
				type = TokenType.DBL;
			} else if ((m = p_int.matcher(line)).lookingAt()) {
				type = TokenType.INT;
			} else if ((m = p_str.matcher(line)).lookingAt()) {
				type = TokenType.STR;
			} else if ((m = p_name.matcher(line)).lookingAt()) {
				type = TokenType.NAME;
			} else if ((m = p_symbl.matcher(line)).lookingAt()) {
				type = TokenType.SYMBL;
			} else {
				m = null;
				offset = 1;
				if (line.startsWith(token = "(")) {
					type = TokenType.OP_BRKT;
				} else if (line.startsWith(token = ")")) {
					type = TokenType.CL_BRKT;
				} else if (line.startsWith(token = ":")) {
					type = TokenType.COLON;
				} else if (line.startsWith(token = ",")) {
					type = TokenType.COMMA;
				} else if (line.startsWith(token = "#")) {
					type = TokenType.SHARP;
				} else if (line.startsWith(token = ";")) {
					type = TokenType.SEMICOLON;
				} else {
					throw new TokenError(line);
				}
			}
			if (m != null) {
				token = m.group();
				if (TokenType.NAME == type) {
					token = token.toUpperCase();
				}
				offset = m.end();
			}
			list.add(new Token(type, token));
			line = line.substring(offset);
		}
		return list;
	}
}

final class ExcuteError extends SyntaxError
{
	public ExcuteError(Throwable ex) { super(ex); } 
	public ExcuteError(String msg) { super(msg); } 
}

enum VarType
{
	INT, DBL, STR, UNKNOWN
}

class VarArray
{
	int[] intarr = null;
	double[] dblarr = null;
	String[] strarr = null;
	VarType type = VarType.UNKNOWN;
	List<Integer> dim;
	int maxdim = 1;
	
	VarArray(List<Integer> dim) throws SyntaxError
	{
		if (dim == null || dim.size() < 1) {
			throw new NullPointerException();
		}
		this.dim = new ArrayList<Integer>(dim);
		for (Integer i : dim) {
			maxdim *= 1 + i.intValue();
		}
		if (maxdim < 1) {
			throw new SyntaxError("Invalid define array dimention");
		}
	}
	
	public VarType getType()
	{
		return type;
	}
	
	public void setType(VarType type) throws SyntaxError
	{
		if (isUnknown() == false) {
			throw new SyntaxError("already exist array");
		}
		this.type = type;
		switch (type) {
		case INT:
			intarr = new int[maxdim + 1];
			break;
		case DBL:
			dblarr = new double[maxdim + 1];
			break;
		case STR:
			strarr = new String[maxdim + 1];
			break;
		default:
			throw new SyntaxError("Invalid array type");
		}
	}
	
	private int getIndex(List<Integer> indexes, int base) throws SyntaxError
	{
		if (indexes == null || indexes.size() != dim.size()) {
			throw new SyntaxError("Invalid array indexes " + indexes);
		}
		int index = 0;
		int tmp = 1;
		for (int i = 0; i < dim.size(); i++) {
			int p = indexes.get(i).intValue();
			int max = dim.get(i).intValue();
			if (p < base || p > max) {
				throw new SyntaxError("array index out of bounds");
			}
			index += p * tmp;
			tmp *= max;
		}
		return index;
	}
	
	public void setInt(List<Integer> indexes, int base, int value) throws SyntaxError
	{
		if (isInt() == false) {
			throw new SyntaxError("");
		}
		intarr[getIndex(indexes, base)] = value;
	}

	public void setDbl(List<Integer> indexes, int base, double value) throws SyntaxError {
		if (isDbl() == false) {
			throw new SyntaxError("");
		}
		dblarr[getIndex(indexes, base)] = value;
	}

	public void setStr(List<Integer> indexes, int base, String value) throws SyntaxError
	{
		if (isStr() == false) {
			throw new SyntaxError("");
		}
		strarr[getIndex(indexes, base)] = value;
	}
	
	public int getInt(List<Integer> indexes, int base) throws SyntaxError
	{
		if (isInt() == false) {
			throw new SyntaxError("");
		}
		return intarr[getIndex(indexes, base)];
	}

	public double getDbl(List<Integer> indexes, int base) throws SyntaxError
	{
		if (isDbl() == false) {
			throw new SyntaxError("");
		}
		return dblarr[getIndex(indexes, base)];
	}

	public String getStr(List<Integer> indexes, int base) throws SyntaxError
	{
		if (isStr() == false) {
			throw new SyntaxError("");
		}
		String tmp = strarr[getIndex(indexes, base)];
		return tmp == null ? "" : tmp;
	}
	
	public Result getResult(List<Integer> indexes, int base) throws SyntaxError
	{
		switch (type) {
		case INT:
			return new Result(intarr[getIndex(indexes, base)]);
		case DBL:
			return new Result(dblarr[getIndex(indexes, base)]);
		case STR:
			String tmp = strarr[getIndex(indexes, base)];
			return new Result(tmp == null ? "" : tmp);
		default:
			return new Result();
		}
	}
	
	public boolean isInt()     { return VarType.INT     == type; }
	public boolean isDbl()     { return VarType.DBL     == type; }
	public boolean isStr()     { return VarType.STR     == type; }
	public boolean isUnknown() { return VarType.UNKNOWN == type; }

}

enum ResultType
{
	INT, DBL, STR, EOL, UNKNOWN
}

class Result
{
	public static final Result getTrue()
	{
		return new Result((int)-1);
	}
	public static final Result getFalse()
	{
		return new Result((int)0);
	}
	public static final Result getBool(boolean bool)
	{
		return bool ? getTrue() : getFalse();
	}
	
	private int offset;
	private int intvalue = 0;
	private double dblvalue = 0;
	private String strvalue = null;
	private ResultType type;
	
	Result()
	{
		type = ResultType.EOL;
	}
	
	static Result getEOL(int offset)
	{
		Result temp = new Result();
		temp.offset = offset;
		return temp;
	}
	
	Result(int value)
	{
		intvalue = value;
		type = ResultType.INT;
	}


	Result(double value)
	{
		dblvalue = value;
		type = ResultType.DBL;
	}

	
	Result(Number value)
	{
		if (Integer.class.equals(value.getClass())) {
			intvalue = value.intValue();
			type = ResultType.INT;
		} else if (Double.class.equals(value.getClass())) {
			dblvalue = value.doubleValue();
			type = ResultType.DBL;
		} else {
			type = ResultType.UNKNOWN;
		}
	}
	
	Result(String value)
	{
		strvalue = value;
		type = ResultType.STR;
	}

	Result(int value, int offset)
	{
		this(value);
		this.offset = offset;
	}

	Result(double value, int offset)
	{
		this(value);
		this.offset = offset;
	}

	Result(Number value, int offset)
	{
		this(value);
		this.offset = offset;
	}

	Result(String value, int offset)
	{
		this(value);
		this.offset = offset;
	}

	
	public ResultType getType()
	{
		return type;
	}	
	
	public int getOffset()
	{
		return offset;
	}
	
	public void setOffset(int offset)
	{
		this.offset = offset;
	}
	
	public int getIntValue()
	{
		return intvalue;
	}
	
	public double getDblValue()
	{
		return dblvalue;
	}

	public Number getNumValue()
	{
		if (ResultType.INT.equals(type)) {
			return Integer.valueOf(intvalue);
		} else if (ResultType.DBL.equals(type)) {
			return Double.valueOf(dblvalue);
		} else {
			return null;
		}
	}
	public String getStrValue()
	{
		return strvalue;
	}
	
	public boolean isNumber()
	{
		return ResultType.INT == type || ResultType.DBL == type;
	}

	public boolean isInt()
	{
		return ResultType.INT == type;
	}

	public boolean isDbl()
	{
		return ResultType.DBL == type;
	}
	
	public boolean isStr()
	{
		return ResultType.STR == type;
	}
}

final class Variable
{

	private Map<String, VarArray> arrvars = new HashMap<String, VarArray>();
	private Map<String, String> strvars = new HashMap<String, String>();
	private Map<String, Number> numvars = new HashMap<String, Number>();
	
	private int arraybase = 0;
	
	Variable() { }
	
	public void setArrayBase(int base) throws SyntaxError
	{
		if (base != 0 && base != 1) {
			throw new SyntaxError("");
		}
		arraybase = base;
	}
	
	public boolean exist(String name)
	{
		return isStr(name) || isNum(name);
	}
	
	public static boolean isStrName(String name)
	{
		return name.lastIndexOf("$") > 0;
	}
	
	public boolean isStr(String name)
	{
		return strvars.containsKey(name)
			|| (isStrName(name) && isArray(name));
	}
	
	public boolean isNum(String name)
	{
		return numvars.containsKey(name)
			|| (isStrName(name) == false && isArray(name));
	}
	
	public boolean isArray(String name)
	{
		return arrvars.containsKey(name);
	}
	
	public boolean isInt(String name)
	{
		if (isNum(name)) {
			if (isArray(name)) {
				return arrvars.get(name).isInt();
			} else {
				return Integer.class.equals(numvars.get(name).getClass());
			}
		}
		return false;
	}

	
	public boolean isDbl(String name)
	{
		if (isNum(name)) {
			if (isArray(name)) {
				return arrvars.get(name).isDbl();
			} else {
				return Double.class.equals(numvars.get(name).getClass());
			}
		}
		return false;
	}
	
	public VarType getType(String name)
	{
		if (isStr(name)) {
			return VarType.STR;
		} else if (isInt(name)) {
			return VarType.INT;
		} else if (isDbl(name)) {
			return VarType.DBL;
		} else {
			return VarType.UNKNOWN;
		}
	}
	public Result getResult(String name, int index)
	{
		Result temp = getResult(name);
		temp.setOffset(index);
		return temp;
	}
	
	public Result getResult(String name)
	{
		if (isArray(name)) {
			return new Result();
		}
		if (isStr(name)) {
			return new Result(strvars.get(name));
		}
		if (isInt(name)) {
			return new Result(numvars.get(name).intValue());
		}
		if (isDbl(name)) {
			return new Result(numvars.get(name).doubleValue());
		}
		return new Result();
	}

	public Result getResult(String name, List<Integer> indexes, int index) throws SyntaxError
	{
		Result temp = getResult(name, indexes);
		temp.setOffset(index);
		return temp;
	}

	public Result getResult(String name, List<Integer> indexes) throws SyntaxError
	{
		if (isArray(name) == false) {
			createSimpleArray(name, 10);
		}
		VarArray arr = arrvars.get(name);
		if (arr.isUnknown()) {
			arr.setType(VarType.INT);
		}
		return arr.getResult(indexes, arraybase);
	}
	
	public void putValue(String name, String value) throws SyntaxError
	{
		if (isStrName(name) == false) {
			throw new SyntaxError("");
		}
		strvars.put(name, value);
	}

	public void putValue(String name, int value) throws SyntaxError
	{
		if (isStrName(name)) {
			throw new SyntaxError("");
		}
		if (isDbl(name)) {
			numvars.put(name, Double.valueOf(Integer.valueOf(value).doubleValue()));
		} else {
			numvars.put(name, Integer.valueOf(value));
		}
	}

	public void putValue(String name, double value) throws SyntaxError
	{
		if (isStrName(name)) {
			throw new SyntaxError("");
		}
		if (isInt(name)) {
			numvars.put(name, Integer.valueOf(Double.valueOf(value).intValue()));
		} else {
			numvars.put(name, Double.valueOf(value));
		}
	}

	public void putValue(String name, Result value) throws SyntaxError
	{
		switch (value.getType()) {
		case INT: putValue(name, value.getIntValue()); break;
		case DBL: putValue(name, value.getDblValue()); break;
		case STR: putValue(name, value.getStrValue()); break;
		default:
			throw new SyntaxError("");
		}
	}

	public void createArray(String name, List<Integer> dims) throws SyntaxError
	{
		VarArray arr = new VarArray(dims);
		if (isStrName(name)) {
			arr.setType(VarType.STR);
		}
		arrvars.put(name, arr);
	}

	public void createSimpleArray(String name, int dim) throws SyntaxError
	{
		List<Integer> dims = new ArrayList<Integer>();
		dims.add(Integer.valueOf(dim));
		VarArray arr = new VarArray(dims);
		if (isStrName(name)) {
			arr.setType(VarType.STR);
		}
		arrvars.put(name, arr);
	}

	public void putValue(String name, List<Integer> indexes, String value) throws SyntaxError
	{
		if (isStrName(name) == false) {
			throw new SyntaxError("");
		}
		if (isArray(name) == false) {
			createSimpleArray(name, 10);
		}
		arrvars.get(name).setStr(indexes, arraybase, value);
	}

	public void putValue(String name, List<Integer> indexes, int value) throws SyntaxError
	{
		if (isStrName(name)) {
			throw new SyntaxError("");
		}
		if (isArray(name) == false) {
			createSimpleArray(name, 10);
		}
		VarArray arr = arrvars.get(name);
		if (arr.isUnknown()) {
			arr.setType(VarType.INT);
		}
		if (arr.isInt()) {
			arr.setInt(indexes, arraybase, value);
		} else {
			arr.setDbl(indexes, arraybase, Integer.valueOf(value).doubleValue());
		}
	}

	public void putValue(String name, List<Integer> indexes, double value) throws SyntaxError
	{
		if (isStrName(name)) {
			throw new SyntaxError("");
		}
		if (isArray(name) == false) {
			createSimpleArray(name, 10);
		}
		VarArray arr = arrvars.get(name);
		if (arr.isUnknown()) {
			arr.setType(VarType.DBL);
		}
		if (arr.isDbl()) {
			arr.setDbl(indexes, arraybase, value);
		} else {
			arr.setInt(indexes, arraybase, Double.valueOf(value).intValue());
		}
	}

	public void putValue(String name, List<Integer> indexes, Result value) throws SyntaxError
	{
		switch (value.getType()) {
		case INT: putValue(name, indexes, value.getIntValue()); break;
		case DBL: putValue(name, indexes, value.getDblValue()); break;
		case STR: putValue(name, indexes, value.getStrValue()); break;
		default:
			throw new SyntaxError("");
		}
	}

}

final class Excutor
{
	private NavigableMap<Integer, List<Token>> program = new TreeMap<Integer, List<Token>>();

	private Integer programcounter = null;
	private Deque<Integer> programcounterstack = new ArrayDeque<Integer>();
	
	private boolean jumpflag = false;
	private boolean stopflag = false;
	private boolean endflag = false;
	
	private Set<String> editorcommandnames = new HashSet<String>();
	private Set<String> commandnames = new HashSet<String>();
	private Set<String> functionnames = new HashSet<String>();
	private Set<String> keywordnames = new HashSet<String>();
	
	private Map<String, Integer> opepriority = new HashMap<String, Integer>();

	private Random rand = new Random();
	private double lastrand = 0;
	
	private int tabcount = 0;
	
	private Variable vars = new Variable();

	public Excutor()
	{
		String[] edcmd = { "RUN", "LOAD", "SAVE", "LIST", "RENUM", "CONT", "NEW" };
		String[] cmd = { "PRINT", "INPUT", "LET", "REM", "STOP", "END", "GOTO",
			"GO", "TO", "GOSUB", "RETURN", "BASE", "DIM", "IF", "THEN", "ELSE", "ERASE",
			"FOR", "NEXT", "STEP", "BREAK", "RESTORE", "FILE", "READ", "DATA", "WRITE"};
		String[] fnc = { "INT", "STR$", "RND", "CLK", "ABS", "VAL", "SIN", "COS", "TAN",
			"SGN", "SQRT", "EXP", "LOG", "LEFT$", "RIGHT$", "MID$", "LEN"};
		String[] key = { "AND", "OR" };
		String[] ope = { "OR", "AND", "=", ">=", "<=", "<", ">", "+", "-", "*", "/" };
		
		
		for (String s : edcmd) editorcommandnames.add(s);
		for (String s : cmd) commandnames.add(s);
		for (String s : fnc) functionnames.add(s);
		for (String s : key) keywordnames.add(s);
		
		for (int i = 0; i < ope.length; i++) {
			opepriority.put(ope[i], Integer.valueOf(i));
		}
	}
	
	@Override
	public String toString()
	{
		return program.toString().replaceAll("(\\d+=\\[\\[)", "\n$1");
	}
		
	private boolean checkKeyword(String name)
	{
		return editorcommandnames.contains(name)
			|| commandnames.contains(name)
			|| functionnames.contains(name)
			|| keywordnames.contains(name);
	}
	
	private void printLine(List<Token> tokens)
	{
		printLine(Integer.valueOf(tokens.get(0).getToken()), tokens);
	}
	
	private void printLine(Integer linenum, List<Token> tokens)
	{
		int s = 0;
		if (linenum != null) {
			System.out.printf("%05d ", linenum.intValue());
			s = 1;
		}
		TokenType before = null, type;
		for (int i = s; i < tokens.size(); i++) {
			Token token = tokens.get(i);
			type = token.getType();
			if (TokenType.NAME == before) {
				switch (type) {
					case NAME:
					case INT:
					case DBL:
					case SHARP:
					case STR:
						System.out.print(' ');
						break;
					default:
						break;
				}
			}
			System.out.print(tokens.get(i).getToken());
			before = type;
		}
		System.out.println();
	}
	
	private Result checksign(Result temp, int sign) throws SyntaxError
	{
		switch (temp.getType()) {
		case INT:
			if (sign < 0) {
				return new Result( - temp.getNumValue().intValue(), temp.getOffset());
			}
			break;
		case DBL:
			if (sign < 0) {
				return new Result( - temp.getNumValue().doubleValue(), temp.getOffset());
			}
			break;
		case STR:
			if (sign == 0) {
				break;
			}
		default:
			throw new SyntaxError("invalid sign character");
		}
		return temp;
	}
	
	private Result calc2(int temp1, int temp2, String op) throws SyntaxError
	{
		int res = 0;
		switch (op) {
		case "+": 	res = temp1 + temp2; break; // TODO OverFlow どした
		case "-": 	res = temp1 - temp2; break;
		case "*": 	res = temp1 * temp2; break;
		case "/": 	if (temp2 == 0) {
						throw new ExcuteError("Zero divided");
					}
				  	res = temp1 / temp2; break;
		case "=": 	res = temp1 == temp2 ? -1 : 0; break;
		case ">":	res = temp1 > temp2 ? -1 : 0; break;
		case "<":	res = temp1 < temp2 ? -1 : 0; break;
		case ">=":	res = temp1 >= temp2 ? -1 : 0; break;
		case "<=":	res = temp1 <= temp2 ? -1 : 0; break;
		case "AND":	res = (temp1 & temp2) != 0 ? -1 : 0; break;
		case "OR":	res = (temp1 | temp2) != 0 ? -1 : 0; break;
		default:
			throw new SyntaxError("Invalid operator: " + op);
		}
		return new Result(res);
	}
	
	private Result calc2(double temp1, double temp2, String op) throws SyntaxError
	{
		double res = 0;
		switch (op) {
		case "+": 	res = temp1 + temp2; break; // TODO OverFlow どした
		case "-": 	res = temp1 - temp2; break;
		case "*": 	res = temp1 * temp2; break;
		case "/": 	if (temp2 == 0) {
						throw new ExcuteError("Zero divided");
					}
				  	res = temp1 / temp2; break;
		case "=": 	return Result.getBool(temp1 == temp2);
		case ">": 	return Result.getBool(temp1 > temp2);
		case "<": 	return Result.getBool(temp1 < temp2);
		case ">=": 	return Result.getBool(temp1 >= temp2);
		case "<=": 	return Result.getBool(temp1 <= temp2);
		case "AND": return Result.getBool(temp1 * temp2 != 0.0);
		case "OR":	return Result.getBool(temp1 != 0.0 || temp2 != 0.0);
		default:
			throw new SyntaxError("Invalid operator: " + op);
		}
		return new Result(res);
	}
	
	private Result calc2(String temp1, String temp2, String op) throws SyntaxError
	{
		String res;
		switch (op) {
		case "-":
		case "*":
		case "/":
		case "AND":
		case "OR":	throw new SyntaxError("Invalid str operetor: " + op);
		case "+":	return new Result(temp1 + temp2);
		case "=":	return Result.getBool(temp1.equals(temp2));
		case ">":	return Result.getBool(temp1.compareTo(temp2) > 0);
		case "<":	return Result.getBool(temp1.compareTo(temp2) < 0);
		case ">=":	return Result.getBool(temp1.compareTo(temp2) >= 0);
		case "<=":	return Result.getBool(temp1.compareTo(temp2) <= 0);
		default:
			throw new SyntaxError("Invalid operator: " + op);
		}
	}
	
	private Result calc2(Result temp1, Result temp2, String op) throws SyntaxError
	{
		if (temp1.getType() == temp2.getType()) {
			switch (temp1.getType()) {
			case INT:
				return calc2(temp1.getIntValue(), temp2.getIntValue(), op);
			case DBL:
				return calc2(temp1.getDblValue(), temp2.getDblValue(), op);
			case STR:
				return calc2(temp1.getStrValue(), temp2.getStrValue(), op);
			default:
				throw new ExcuteError("Invalid type value");
			}
		} else if (temp1.isInt() && temp2.isDbl()) {
			return calc2(temp1.getNumValue().doubleValue(), temp2.getDblValue(), op);
		} else if (temp1.isDbl() && temp2.isInt()) {
			return calc2(temp1.getDblValue(), temp2.getNumValue().doubleValue(), op);
		} else {
			throw new ExcuteError("Invalid type value");
		}
		
	}
	
	private boolean calccheck(String op1, String op2) throws SyntaxError
	{
		Integer p1 = opepriority.get(op1);
		Integer p2 = opepriority.get(op2);
		if (p1 == null || p2 == null) {
			throw new SyntaxError("Invalid expression symbol");
		}
		return p2.compareTo(p1) < 0;
	}
	
	private Result calc(List<Token> tokens, int index) throws SyntaxError
	{
		if (index >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		
		Deque<Result> r_stack = new ArrayDeque<Result>();
		Deque<String> s_stack = new ArrayDeque<String>();
		int sign = 0;
		int flag = 0;
		Result temp, temp2;

		for (; index < tokens.size(); index++) {
			Token cur = tokens.get(index);
			TokenType type = cur.getType();
			String token = cur.getToken();
		
			if (flag == 0) {
				flag = 1;
				switch (type) {
				case SYMBL:
					if (sign != 0) {
						throw new SyntaxError(tokens, index);
					}
					if ("+".equals(token)) {
						sign = 1;
					} else if ("-".equals(token)) {
						sign = -1;
					} else {
						throw new SyntaxError(tokens, index);
					}
					flag = 0;
					break;
				case INT:
					r_stack.push(checksign(new Result(Integer.parseInt(token), index), sign));
					break;
				case DBL:
					r_stack.push(checksign(new Result(Double.parseDouble(token), index), sign));
					break;
				case STR:
					r_stack.push(checksign(new Result(token.substring(1, token.length() - 1), index), sign));
					break;
				case OP_BRKT:
					temp = calc(tokens, index + 1);
					index = temp.getOffset();
					if (tokens.get(index).isClBrkt() == false) {
						throw new SyntaxError(tokens, index);
					}
					r_stack.push(checksign(temp, sign));
					break;
				case NAME:
					if (functionnames.contains(token)) {
						temp = doFunction(tokens, index);
						index = temp.getOffset();
						r_stack.push(checksign(temp, sign));
					} else if (vars.isArray(token)) {
						temp = get_arrvar(tokens, index);
						index = temp.getOffset();
						r_stack.push(checksign(temp, sign));
					} else if (vars.isStr(token)) {
						r_stack.push(checksign(vars.getResult(token, index), sign));
					} else if (vars.isNum(token)) {
						r_stack.push(checksign(vars.getResult(token, index), sign));
					} else if (checkKeyword(token)) {
						throw new SyntaxError("Invalid name");
					} else {
						// 未定義 変数
						if (index + 1 < tokens.size() && tokens.get(index + 1).isOpBrkt()) {
							// 配列
							temp = get_arrvar(tokens, index);
							index = temp.getOffset();
							r_stack.push(checksign(temp, sign));
						} else if (vars.isStrName(token)) {
							vars.putValue(token, "");
							r_stack.push(checksign(vars.getResult(token, index), sign));
						} else {
							vars.putValue(token, (int)0);
							r_stack.push(checksign(vars.getResult(token, index), sign));
						}
					}
					break;
				default:
					throw new SyntaxError(tokens, index);
				}
			} else if (flag == 1) {
				flag = 0;
				sign = 0;
				switch (type) {
				case NAME:
					switch (token) {
					case "OR":
					case "AND":
						temp = r_stack.peek();
						if (temp.isStr()) {
							throw new ExcuteError("Invalid expression symbol");
						}
						break;
					default:
						throw new SyntaxError(tokens, index);
					}
				case SYMBL:
					switch (token) {
					case "-":
					case "*":
					case "/":
						temp = r_stack.peek();
						if (temp.isStr()) {
							throw new ExcuteError("Invalid expression symbol");
						}
					default:
						break;
					}
					while (s_stack.size() > 0 && calccheck(s_stack.peek(), token)) {
						if (r_stack.size() < 2) {
							throw new ExcuteError("Unknown error");
						}
						temp2 = r_stack.pop();
						temp = r_stack.pop();
						temp = calc2(temp, temp2, s_stack.pop());
						r_stack.push(temp);
					}
					s_stack.push(token);
					break;
				case COLON:
				case SEMICOLON:
				case COMMA:
				case CL_BRKT:
					while (s_stack.size() > 0) {
						if (r_stack.size() < 2) {
							throw new ExcuteError("Unknown error");
						}
						temp2 = r_stack.pop();
						temp = r_stack.pop();
						temp = calc2(temp, temp2, s_stack.pop());
						r_stack.push(temp);
					}
					if (r_stack.size() != 1) {
						throw new ExcuteError("Unknown error");
					}
					temp = r_stack.pop();
					temp.setOffset(index);
					return temp;
				default:
					throw new SyntaxError(tokens, index);
				}
			}
		}
		

		if (flag == 0) {
			if (r_stack.size() > 0 || s_stack.size() > 0) {
				throw new SyntaxError(tokens, index);
			}
			return Result.getEOL(index);
		}
		
		while (s_stack.size() > 0) {
			if (r_stack.size() < 2) {
				throw new ExcuteError("Unknown error");
			}
			temp2 = r_stack.pop();
			temp = r_stack.pop();
			temp = calc2(temp, temp2, s_stack.pop());
			r_stack.push(temp);
		}
		if (r_stack.size() != 1) {
			throw new ExcuteError("Unknown error");
		}
		temp = r_stack.pop();
		temp.setOffset(index);
		return temp;
	}

	
	public Result get_arrvar(List<Token> tokens, int index) throws SyntaxError
	{
		if (index + 3 >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		String name = tokens.get(index).getToken();
		index++;
		if (tokens.get(index).isOpBrkt() == false) {
			throw new SyntaxError(tokens, index);
		}
		index++;
		List<Integer> indexes = new ArrayList<Integer>();
		for (; index < tokens.size(); index++) {
			Result temp = calc(tokens, index);
			switch (temp.getType()) {
			case INT: indexes.add(Integer.valueOf(temp.getIntValue())); break;
			case DBL: indexes.add(Integer.valueOf(temp.getNumValue().intValue())); break;
			default:  throw new SyntaxError(tokens, index);
			}
			index = temp.getOffset();
			if (index >= tokens.size()) {
				throw new SyntaxError(tokens, index);
			}
			switch (tokens.get(index).getType()) {
			case COMMA:
				break;
			case CL_BRKT:
				return vars.getResult(name, indexes, index);
			default:
				throw new SyntaxError(tokens, index);
			}
		}
		throw new SyntaxError(tokens, index);
	}

	public Result doFunction(List<Token> tokens, int index) throws SyntaxError
	{
		if (index + 1 >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		if (tokens.get(index + 1).isOpBrkt() == false) {
			throw new SyntaxError(tokens, index);
		}
		String name = tokens.get(index).getToken();
		Result temp;
		switch (name) {
		case "INT":
			temp = calc(tokens, index + 2);
			index = temp.getOffset();
			if (tokens.get(index).isClBrkt()) {
				switch (temp.getType()) {
				case INT:
					return temp;
				case DBL:
					return new Result(temp.getNumValue().intValue(), index);
				default:
					break;
				}
			}
			throw new SyntaxError(tokens, index);
		case "RND":
			temp = calc(tokens, index + 2);
			index = temp.getOffset();
			if (tokens.get(index).isClBrkt()) {
				switch (temp.getType()) {
				case INT:
				case DBL:
					int s = temp.getNumValue().intValue();
					if (s == 0) {
						return new Result(lastrand = rand.nextDouble(), index);
					} else if (s > 0) {
						rand.setSeed((long)s);
						lastrand = rand.nextDouble();
					}
					return new Result(lastrand, index);
				default:
					break;
				}
			}
			throw new SyntaxError(tokens, index);
		case "CLK":
			temp = calc(tokens, index + 2);
			index = temp.getOffset();
			if (tokens.get(index).isClBrkt()) {
				switch (temp.getType()) {
				case INT:
				case DBL:
					double t = temp.getNumValue().doubleValue();
					t -= Math.floor(t);
					t *= 24;
					return new Result((int)t, index);
				default:
					break;
				}
			}
			throw new SyntaxError(tokens, index);
		case "ABS":
			temp = calc(tokens, index + 2);
			index = temp.getOffset();
			if (tokens.get(index).isClBrkt()) {
				switch (temp.getType()) {
				case INT:
					return new Result(Math.abs(temp.getIntValue()), index);
				case DBL:
					return new Result(Math.abs(temp.getDblValue()), index);
				default:
					break;
				}
			}
			throw new SyntaxError(tokens, index);
		case "VAL":
			temp = calc(tokens, index + 2);
			index = temp.getOffset();
			if (tokens.get(index).isClBrkt()) {
				switch (temp.getType()) {
				case STR:
					Matcher mt;
					if ((mt = Pattern.compile("^(\\d*\\.\\d+)").matcher(temp.getStrValue())).lookingAt()) {
						return new Result(Double.parseDouble(mt.group(1)), index);
					} else if ((mt = Pattern.compile("^(\\d+)").matcher(temp.getStrValue())).lookingAt()) {
						return new Result(Integer.parseInt(mt.group(1)), index);
					} else {
						return new Result(0.0, index);
					}
				default:
					break;
				}
			}
			throw new SyntaxError(tokens, index);
		default:
			break;
		}
		
		return Result.getEOL(index);
	}
	
	public int doEditorCommand(List<Token> tokens) throws SyntaxError
	{
		if (tokens == null || tokens.isEmpty()) {
			return 0;
		}
		
		Token first = tokens.get(0);
		String token = first.getToken();
		TokenType type = first.getType();
		
		if (first.isInt()) {
			Integer tmpint = Integer.valueOf(token);
			if (tmpint.intValue() < 1) {
				throw new SyntaxError(token);
			}
			if (tokens.size() > 1) {
				program.put(tmpint, tokens);
			} else {
				program.remove(tmpint);
			}
			return 0;
		} else if (first.isName() == false) {
			throw new ExcuteError(token);
		}
		
		/* */
		if (Debug.isIDEONE()) {
			printLine(null, tokens); // IDEONE上じゃ入力が見えないので
		}
		/* */
		
		switch (token) {
			case "CONT": // プログラムの再開
				// TODO 処理
				break;
			case "EXIT": // インタプリタの終了
				return -1;
			case "LIST": // プログラムの表示
				do_list(tokens);
				break;
			case "LOAD": // プログラムのロード
				// TODO 処理
				break;
			case "NEW":  // プログラムと変数の全消去
				// TODO 処理
				break;
			case "RENUM": // プログラムの行番号再割り当て
				// TODO 処理
				break;
			case "RUN":  // プログラムの実行
				do_run(tokens);
				break;
			case "SAVE": // プログラムの保存
				// TODO 処理
				break;
			default:
				excute(tokens, 0);
				break;
		}
		if (stopflag) {
			System.out.println("Stop");
			stopflag = false;
			return 0;
		}
		System.out.println("Ok");
		return 0;
	}
	
	private void do_list(List<Token> tokens) throws SyntaxError
	{
		Integer start = null;
		Integer end = null;
		Token first, second, third;
		Map<Integer, List<Token>> list = null;
		try {
			switch (tokens.size()) {
				case 1:
					list = program;
					break;
				case 2:
					first = tokens.get(1);
					if (first.isInt() == false) {
						throw new SyntaxError(tokens ,1);
					}
					end = start = Integer.valueOf(first.getToken());
					if (start.intValue() < 1) {
						throw new SyntaxError(tokens ,1);
					}
					list = program.subMap(start, true, end, true);
					break;
				case 3:
					first = tokens.get(1);
					second = tokens.get(2);
					if (first.tokenIs("-")) {
						if (second.isInt() == false) {
							throw new SyntaxError(tokens ,2);
						}
						end = Integer.valueOf(second.getToken());
						if (end.intValue() < 1) {
							throw new SyntaxError(tokens ,2);
						}
						list = program.headMap(end, true);
					} else if (first.isInt()) {
						start = Integer.valueOf(first.getToken());
						if (start.intValue() < 1) {
							throw new SyntaxError(tokens , 1);
						}
						if (second.tokenIs("-") == false) {
							throw new SyntaxError(tokens ,2);
						}
						list = program.tailMap(start);
					} else {
						throw new SyntaxError(tokens ,1);
					}
					break;
				case 4:
					first = tokens.get(1);
					if (first.isInt() == false) {
						throw new SyntaxError(tokens ,1);
					}
					start = Integer.valueOf(first.getToken());
					if (start.intValue() < 1) {
						throw new SyntaxError(tokens ,1);
					}
					if (tokens.get(2).tokenIs("-") == false) {
						throw new SyntaxError(tokens ,2);
					}
					third = tokens.get(3);
					if (third.isInt() == false) {
						throw new SyntaxError(tokens ,3);
					}
					end = Integer.valueOf(third.getToken());
					if (end.intValue() < 1) {
						throw new SyntaxError(tokens ,3);
					}
					list = program.subMap(start, true, end, true);
					break;
				default:
					throw new SyntaxError(tokens, 0);
			}
		} catch (SyntaxError er) {
			throw er;
		} catch (Exception ex) {
			throw new ExcuteError(ex);
		}
		
		if (list == null) {
			return;
		}
		
		for (Integer key: list.keySet()) {
			printLine(key, list.get(key));
		}
		
	}
	
	private void do_run(List<Token> tokens) throws SyntaxError
	{
		switch (tokens.size()) {
			case 1:
				programcounter = program.ceilingKey(Integer.valueOf(1));
				if (programcounter == null) {
					throw new SyntaxError("Program is not found");
				}
				break;
			case 2:
				Token first = tokens.get(1);
				if (first.isInt() == false) {
					throw new SyntaxError("Illegal Argument");
				}
				programcounter = Integer.valueOf(first.getToken());
				if (programcounter.intValue() < 1) {
					throw new SyntaxError("Wrong Line Number");
				}
				if (program.containsKey(programcounter) == false) {
					throw new SyntaxError("Not found Line Number");
				}
				break;
			default:
				throw new SyntaxError("Illegal Arguments");
		}
		
		stopflag = endflag = false;
		List<Token> line = null;
		try {
			while (programcounter != null) {
				line = program.get(programcounter);
				excute(line, 1);
				if (jumpflag == false) {
					programcounter = program.higherKey(programcounter);
					if (stopflag) {
						break;
					} else if (endflag) {
						break;
					}
				} else {
					jumpflag = false;
				}
			}
		} catch (SyntaxError er) {
			printLine(line);
			throw er;
		}
		
	}
	
	private int excute(List<Token> tokens, int index) throws SyntaxError
	{
		if (index < 0 || index >= tokens.size()) {
			return tokens.size();
		}
		Token first = tokens.get(index);
		TokenType type = first.getType();
		
		while (first.isColon()) {
			index++;
			if (index == tokens.size()) {
				return index;
			}
			first = tokens.get(index);
			type = first.getType();
		}
		
		if (first.isName() == false) {
			throw new SyntaxError(first.toString());
		}
		String cmd = first.getToken();
		int offset = index;
		switch (cmd) {
			case "BASE": // 配列の下限(0/1)の設定
				offset = cmd_base(tokens, index + 1);
				break;
			case "BREAK": // FORループの脱出
				// TODO 処理
				break;
			case "DATA": // データの列挙
				// TODO 処理
				break;
			case "DIM":  // 配列変数の宣言
				offset = cmd_dim(tokens, index + 1);
				break;
			case "ELSE": // IF文のELSE節の開始
				// TODO 処理
				break;
			case "END":  // プログラムの終了
				endflag = true;
				return tokens.size();
			case "FILE": // ファイルの割り当て
				// TODO 処理
				break;
			case "FOR":  // 繰り返し
				// TODO 処理
				break;
			case "GO":   // GOTOの分割トークン
				if (index + 2 >= tokens.size()) {
					throw new SyntaxError(tokens, index);
				}
				if (tokens.get(index + 1).tokenIs("TO") == false) {
					throw new SyntaxError(tokens, index);
				}
				offset = cmd_goto(tokens, index + 2);
				break;
			case "GOTO": // ジャンプ
				offset = cmd_goto(tokens, index + 1);
				break;
			case "GOSUB": // サブルーチンジャンプ
				offset = cmd_gosub(tokens, index + 1);
				break;
			case "IF":    // 条件節
				// TODO 処理
				break;
			case "INPUT": // キーボードからの値の入力
				// TODO 処理
				break;
			case "LET":   // 変数への代入
				offset = cmd_let(tokens, index + 1);
				break;
			case "NEXT":  // FOR文の末端
				// TODO 処理
				break;
			case "PRINT": // 画面へ値を出力
				offset = cmd_print(tokens, index + 1);
				break;
			case "REM":   // コメント行
				offset = tokens.size();
				break;
			case "RESTORE": // データ・ファイルの読み込み位置のリセット
				// TODO 処理
				break;
			case "RETURN": // サブルーチンからの脱出
				offset = cmd_return(tokens, index);
				break;
			case "STOP":   // プログラムの中断
				stopflag = true;
				return tokens.size();
			default:
				offset = cmd_let(tokens, index);
				break;
		}
		return offset;
	}
	
	private int cmd_dim(List<Token> tokens, int index) throws SyntaxError
	{
		if (index + 3 >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		List<Integer> dims = new ArrayList<Integer>();
		int flag = 0;
		Token cur = null;
		String name = null;
		for (; index < tokens.size(); index++) {
			cur = tokens.get(index);
			switch (flag) {
			case 0:
				if (cur.isName() == false) {
					throw new SyntaxError(tokens, index);
				}
				name = cur.getToken();
				if (checkKeyword(name)) {
					throw new SyntaxError("Invalid variable name:" + name);
				}
				flag = 1;
				break;
			case 1:
				if (cur.isOpBrkt() == false) {
					throw new SyntaxError(tokens, index);
				}
				dims.clear();
				flag = 2;
				break;
			case 2:
				if (cur.isInt() == false) {
					throw new SyntaxError(tokens, index);
				}
				Integer n = Integer.valueOf(cur.getToken());
				if (n.intValue() < 1) {
					throw new SyntaxError("Invalid dimention size");
				}
				dims.add(n);
				flag = 3;
				break;
			case 3:
				switch (cur.getType()) {
				case COMMA:
					flag = 2;
					break;
				case CL_BRKT:
					vars.createArray(name, dims);
					flag = 4;
					break;
				default:
					throw new SyntaxError(tokens, index);
				}
				break;
			case 4:
				switch (cur.getType()) {
				case COMMA:
					flag = 0;
					break;
				case COLON:
					return excute(tokens, index);
				default:
					throw new SyntaxError(tokens, index);
				}
				break;
			default:
				throw new SyntaxError(tokens, index);
			}
		}
		if (flag != 4) {
			throw new SyntaxError(tokens, index);
		}
		
		return excute(tokens, index);
	}

	private int cmd_goto(List<Token> tokens, int index) throws SyntaxError
	{
		if (index >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		if (index + 1 < tokens.size()) {
			if (tokens.get(index + 1).isColon() == false) {
				throw new SyntaxError(tokens, index + 1);
			}
		}
		// TODO 未テスト コード
		Token first = tokens.get(index);
		if (first.isInt() == false) {
			throw new SyntaxError(tokens, index);
		}
		Integer next = Integer.valueOf(first.getToken());
		if (program.containsKey(next) == false) {
			throw new SyntaxError(tokens, index);
		}
		programcounter = next;
		jumpflag = true;
		return tokens.size();
	}

	private int cmd_gosub(List<Token> tokens, int index) throws SyntaxError
	{
		if (index >= tokens.size()) {
			throw new SyntaxError(tokens, index);
		}
		if (index + 1 < tokens.size()) {
			if (tokens.get(index + 1).isColon() == false) {
				throw new SyntaxError(tokens, index + 1);
			}
		}
		Token first = tokens.get(index);
		if (first.isInt() == false) {
			throw new SyntaxError(tokens, index);
		}
		Integer next = Integer.valueOf(first.getToken());
		if (program.containsKey(next) == false) {
			throw new SyntaxError(tokens, index);
		}
		// TODO 未テスト コード
		// WRONG CODE : RETURNはGOSUBの次の行だけじゃなく次の命令文(コロンがある)から始まることも
		try {
			programcounterstack.push(programcounter);
			programcounter = next;
			jumpflag = true;
			return tokens.size();
		} catch (Exception ex) {
			throw new ExcuteError(ex);
		}
	}
	
	private int cmd_return(List<Token> tokens, int index) throws SyntaxError
	{
		if (index < tokens.size()) {
			if (tokens.get(index).isColon() == false) {
				throw new SyntaxError(tokens, index);
			}
		}
		// TODO 未テスト コード
		// WRONG CODE : RETURNはGOSUBの次の行だけじゃなく次の命令文(コロンがある)から始まることも
		try {
			Integer back = programcounterstack.pop();
			programcounter = program.higherKey(back);
			jumpflag = true;
			return tokens.size();
		} catch (Exception ex) {
			throw new ExcuteError(ex);
		}
	}

	
	private int cmd_base(List<Token> tokens, int index) throws SyntaxError
	{
		// TODO 未テスト コード
		// N-88 BASIC の OPTION BASE の場合は
		// どのDIMよりも先に使う必要があり、プログラム中１回しか使えないらしい
		try {
			Token first = tokens.get(index);
			if (first.isInt() == false) {
				throw new SyntaxError(tokens, index);
			}
			int tmpint = Integer.parseInt(first.getToken());
			vars.setArrayBase(tmpint);
			/*
			if (tmpint != 0 && tmpint != 1) {
				throw new SyntaxError(tokens, index + 1);
			}
			arraybase = tmpint;
			*/
			return excute(tokens, index + 1);
		} catch (SyntaxError er) {
			throw er;
		} catch (Exception ex) {
			throw new ExcuteError(ex);
		}
	}
	
	private int cmd_print(List<Token> tokens, int index) throws SyntaxError
	{
		if (index == tokens.size()) {
			tabcount = 0;
			System.out.println();
			return tokens.size();
		} else if (index > tokens.size()) {
			throw new ExcuteError("Unknown error");
		}
		StringBuilder output = new StringBuilder();
		boolean nl = true;
		for (; index < tokens.size(); index++) {
			Token cur = tokens.get(index);
			switch (cur.getType()) {
			case COLON:
				if (nl) {
					tabcount = 0;
					System.out.println(output);
				} else {
					tabcount += output.length();
					System.out.print(output);
				}
				return excute(tokens, index + 1);
			case COMMA:
				output.append('\t');
				nl = false;
				break;
			case SEMICOLON:
				nl = false;
				break;
			case INT:
			case DBL:
			case STR:
			case NAME:
				Result temp = calc(tokens, index);
				index = temp.getOffset() - 1;
				switch (temp.getType()) {
				case INT: output.append(temp.getIntValue()); break;
				case DBL: output.append(temp.getDblValue()); break;
				case STR: output.append(temp.getStrValue()); break;
				default:
					throw new ExcuteError("Unknown error");
				}
				nl = true;
				break;
			}
		}
		if (nl) {
			tabcount = 0;
			System.out.println(output);
		} else {
			tabcount += output.length();
			System.out.print(output);
		}
		return excute(tokens, index);	
	}
	
	private int getIndexes(List<Token> tokens, int index, List<Integer> indexes) throws SyntaxError
	{
		for (; index < tokens.size(); index++) {
			Result temp = calc(tokens, index);
			switch (temp.getType()) {
			case INT: indexes.add(Integer.valueOf(temp.getIntValue())); break;
			case DBL: indexes.add(Integer.valueOf(temp.getNumValue().intValue())); break;
			default: throw new SyntaxError(tokens, index);
			}
			index = temp.getOffset();
			switch (tokens.get(index).getType()) {
			case COMMA:
				break;
			case CL_BRKT:
				return index;
			default:
				throw new SyntaxError(tokens, index);
			}
		}
		throw new SyntaxError(tokens, index);
	}

	private int cmd_let(List<Token> tokens, int index) throws SyntaxError
	{
		try {
			if (index + 2 >= tokens.size()) {
				throw new SyntaxError(tokens, index);
			}
			Token first = tokens.get(index);
			String name = first.getToken();
			if (checkKeyword(name)) { // 予約語チェック
			     throw new SyntaxError("Wrong variable name: " + name); 
			}
			index++;
			Result temp;

			if (tokens.get(index).isOpBrkt()) {
				index++;
				List<Integer> indexes = new ArrayList<Integer>();
				index = getIndexes(tokens, index, indexes);
				index++;
				if (tokens.get(index).tokenIs("=") == false) {
					throw new SyntaxError(tokens, index);
				}
				index++;
				temp = calc(tokens, index);
				index = temp.getOffset();
				vars.putValue(name, indexes, temp);
			} else if (tokens.get(index).tokenIs("=")) {
				index++;
				temp = calc(tokens, index);
				index = temp.getOffset();
				vars.putValue(name, temp);
			} else {
				throw new SyntaxError(tokens, index);
			}

			return excute(tokens, index + 1);
		} catch (SyntaxError er) {
			throw er;
		} catch (Exception ex) {
			// ex.printStackTrace();
			throw new ExcuteError(tokens.toString());
		}
		
	}
}

final class Debug
{
	public static boolean isIDEONE() { return true; }
	public static void print(String msg) { System.out.print(msg); }
	public static void println(String msg) { System.out.println(msg); }
}