/* 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';
{
System.
out.
printf("BASIC Interpreter - Version %d.%d%c", major, minor, release
); System.
out.
println("Your code goes here!"); try {
Excutor excutor = new Excutor();
while ((line = in.readLine()) != null) {
try {
List<Token> tokens = Tokenizer.tokenize(line);
if (excutor.doEditorCommand(tokens) < 0) {
break;
}
} catch (SyntaxError er) {
}
}
// System.out.println(excutor);
}
}
}
enum TokenType
{
INT, DBL, STR, NAME, SYMBL, SHARP, SEMICOLON,
OP_BRKT, CL_BRKT, COMMA, COLON,
UNKNOWN}
final class Token
{
final TokenType type;
public Token
(TokenType type,
String token
) {
this.type = type;
this.token = token;
}
public TokenType getType() { return type; }
public String getToken
() { return token
; }
@Override
{
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;
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
{
}
class VarArray
{
int[] intarr = null;
double[] dblarr = null;
List<Integer> dim;
int maxdim = 1;
VarArray(List<Integer> dim) throws SyntaxError
{
if (dim == null || dim.size() < 1) {
}
this.dim = new ArrayList<Integer>(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
{
}
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;
}
{
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 {
}
}
{
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;
}
{
if (ResultType.INT.equals(type)) {
} else if (ResultType.DBL.equals(type)) {
return Double.
valueOf(dblvalue
); } else {
return null;
}
}
{
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 {
}
}
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>();
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 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
{
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:
break;
default:
break;
}
}
System.
out.
print(tokens.
get(i
).
getToken()); before = type;
}
}
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);
}
{
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
{
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 *= 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()) {
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) {
stopflag = false;
return 0;
}
return 0;
}
private void do_list(List<Token> tokens) throws SyntaxError
{
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;
throw new ExcuteError(ex);
}
if (list == null) {
return;
}
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;
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);
}
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);
}
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);
}
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();
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();
throw new ExcuteError(ex);
}
}
private int cmd_base(List<Token> tokens, int index) throws SyntaxError
{
// TODO 未テスト コード
// N-88 BASIC の OPTION BASE の場合は
// どのDIMよりも先に使う必要があり、プログラム中1回しか使えないらしい
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;
throw new ExcuteError(ex);
}
}
private int cmd_print(List<Token> tokens, int index) throws SyntaxError
{
if (index == tokens.size()) {
tabcount = 0;
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;
} else {
tabcount += output.length();
}
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;
} else {
tabcount += output.length();
}
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;
// 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
); } }