/* 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.4a (未完成公開)
*
* 実装履歴
* 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,式の計算)
*/
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.regex.*;
class Basic
{
static final int major = 0;
static final int minor = 4;
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);
}
}
}
final class Debug
{
public static boolean isIDEONE() { return true; }
}
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 isInt() { return TokenType.INT.equals(type); }
public boolean isStr() { return TokenType.STR.equals(type); }
public boolean isDbl() { return TokenType.DBL.equals(type); }
public boolean isName() { return TokenType.NAME.equals(type); }
public boolean isSymbl() { return TokenType.SYMBL.equals(type); }
public boolean isOpBrkt() { return TokenType.OP_BRKT.equals(type); }
public boolean isClBrkt() { return TokenType.CL_BRKT.equals(type); }
public boolean isColon() { return TokenType.COLON.equals(type); }
public boolean isSemiColon() { return TokenType.SEMICOLON.equals(type); }
public boolean isComma() { return TokenType.COMMA.equals(type); }
public boolean isSharp() { return TokenType.SHARP.equals(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.equals(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 VarArrayType
{
}
class VarArray
{
int[] intarr = null;
double[] dblarr = null;
VarArrayType type
= VarArrayType.
UNKNOWN; 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 VarArrayType getType()
{
return type;
}
public void setType(VarArrayType type) throws SyntaxError
{
if (VarArrayType.
UNKNOWN.
equals(this.
type) == 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 (VarArrayType.INT.equals(type) == false) {
throw new SyntaxError("");
}
intarr[getIndex(indexes, base)] = value;
}
public void setDbl(List<Integer> indexes, int base, double value) throws SyntaxError {
if (VarArrayType.DBL.equals(type) == false) {
throw new SyntaxError("");
}
dblarr[getIndex(indexes, base)] = value;
}
public void setStr
(List
<Integer
> indexes,
int base,
String value
) throws SyntaxError
{
if (VarArrayType.STR.equals(type) == false) {
throw new SyntaxError("");
}
strarr[getIndex(indexes, base)] = value;
}
public int getInt(List<Integer> indexes, int base) throws SyntaxError
{
if (VarArrayType.INT.equals(type) == false) {
throw new SyntaxError("");
}
return intarr[getIndex(indexes, base)];
}
public double getDbl(List<Integer> indexes, int base) throws SyntaxError
{
if (VarArrayType.DBL.equals(type) == false) {
throw new SyntaxError("");
}
return dblarr[getIndex(indexes, base)];
}
public String getStr
(List
<Integer
> indexes,
int base
) throws SyntaxError
{
if (VarArrayType.STR.equals(type) == false) {
throw new SyntaxError("");
}
String tmp
= strarr
[getIndex
(indexes, base
)]; return tmp == null ? "" : tmp;
}
}
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.equals(type) || ResultType.DBL.equals(type);
}
public boolean isInt()
{
return ResultType.INT.equals(type);
}
public boolean isDbl()
{
return ResultType.DBL.equals(type);
}
public boolean isString()
{
return ResultType.STR.equals(type);
}
}
final class Excutor
{
private NavigableMap
<Integer, List
<Token
>> program
= new TreeMap
<Integer, List
<Token
>>();
private Map
<String, VarArray
> strarrs
= new HashMap
<String, VarArray
>(); private Map
<String, VarArray
> numarrs
= new HashMap
<String, VarArray
>(); private Map
<String, String
> strvars
= new HashMap
<String, String
>(); private Map
<String, Number
> numvars
= new HashMap
<String, Number
>();
private Integer programcounter
= null; private Deque<Integer> programcounterstack = new ArrayDeque<Integer>();
private int arraybase = 0;
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;
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.equals(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().equals(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 (ResultType.INT.equals(temp1.getType())
&& ResultType.DBL.equals(temp2.getType())) {
return calc2(temp1.getNumValue().doubleValue(), temp2.getDblValue(), op);
} else if (ResultType.DBL.equals(temp1.getType())
&& ResultType.INT.equals(temp2.getType())) {
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 (TokenType.CL_BRKT.equals(tokens.get(index).getType()) == 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 (strarrs.containsKey(token)) {
temp = get_strarr(tokens, index);
index = temp.getOffset();
r_stack.push(checksign(temp, sign));
} else if (strvars.containsKey(token)) {
r_stack.push(checksign(new Result(strvars.get(token), index), sign));
} else if (numarrs.containsKey(token)) {
temp = get_numarr(tokens, index);
index = temp.getOffset();
r_stack.push(checksign(temp, sign));
} else if (numvars.containsKey(token)) {
r_stack.push(checksign(new Result(numvars.get(token), index), sign));
} else if (checkKeyword(token)) {
throw new SyntaxError("Invalid name");
} else if (token.lastIndexOf("$") > 0) {
// 未定義 文字列 変数
if (index + 1 < tokens.size() && "(".equals(tokens.get(index + 1).getToken())) {
// 配列
List<Integer> dims = new ArrayList<Integer>();
VarArray arr = new VarArray(dims);
arr.setType(VarArrayType.STR);
strarrs.put(tokens.get(index).getToken(), arr);
temp = get_strarr(tokens, index);
index = temp.getOffset();
r_stack.push(checksign(temp, sign));
} else {
strvars.put(token, "");
r_stack.push(checksign(new Result("", index), sign));
}
} else {
// 未定義 数値 変数
if (index + 1 < tokens.size() && "(".equals(tokens.get(index + 1).getToken())) {
// 配列
List<Integer> dims = new ArrayList<Integer>();
VarArray arr = new VarArray(dims);
arr.setType(VarArrayType.INT);
numarrs.put(tokens.get(index).getToken(), arr);
temp = get_numarr(tokens, index);
index = temp.getOffset();
r_stack.push(checksign(temp, sign));
} else {
numvars.
put(token,
Integer.
valueOf(0)); r_stack.push(checksign(new Result((int)0, 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 (ResultType.STR.equals(temp.getType())) {
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 (ResultType.STR.equals(temp.getType())) {
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_strarr(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 (TokenType.OP_BRKT.equals(tokens.get(index).getType()) == 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:
Result rs = new Result(strarrs.get(name).getStr(indexes, arraybase));
rs.setOffset(index);
return rs;
default:
throw new SyntaxError(tokens, index);
}
}
throw new SyntaxError(tokens, index);
}
public Result get_numarr(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 (TokenType.OP_BRKT.equals(tokens.get(index).getType()) == 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:
VarArray arr = numarrs.get(name);
Result rs = null;
switch (arr.getType()) {
arr.setType(VarArrayType.INT);
case INT:
rs = new Result(arr.getInt(indexes, arraybase));
break;
case DBL:
rs = new Result(arr.getDbl(indexes, arraybase));
break;
default:
throw new ExcuteError("Unknown error");
}
rs.setOffset(index);
return rs;
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 (TokenType.OP_BRKT.equals(tokens.get(index + 1).getType()) == 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 (TokenType.CL_BRKT.equals(tokens.get(index).getType())) {
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 (TokenType.CL_BRKT.equals(tokens.get(index).getType())) {
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 (TokenType.CL_BRKT.equals(tokens.get(index).getType())) {
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 (TokenType.CL_BRKT.equals(tokens.get(index).getType())) {
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 (TokenType.CL_BRKT.equals(tokens.get(index).getType())) {
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 (TokenType.INT.equals(type)) {
if (tmpint.intValue() < 1) {
throw new SyntaxError(token);
}
if (tokens.size() > 1) {
program.put(tmpint, tokens);
} else {
program.remove(tmpint);
}
return 0;
} else if (TokenType.NAME.equals(type) == 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 (TokenType.INT.equals(first.getType()) == 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 ("-".equals(first.getToken())) {
if (TokenType.INT.equals(second.getType()) == 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 (TokenType.INT.equals(first.getType())) {
start
= Integer.
valueOf(first.
getToken()); if (start.intValue() < 1) {
throw new SyntaxError(tokens , 1);
}
if ("-".equals(second.getToken()) == false) {
throw new SyntaxError(tokens ,2);
}
list = program.tailMap(start);
} else {
throw new SyntaxError(tokens ,1);
}
break;
case 4:
first = tokens.get(1);
if (TokenType.INT.equals(first.getType()) == false) {
throw new SyntaxError(tokens ,1);
}
start
= Integer.
valueOf(first.
getToken()); if (start.intValue() < 1) {
throw new SyntaxError(tokens ,1);
}
if ("-".equals(tokens.get(2).getToken()) == false) {
throw new SyntaxError(tokens ,2);
}
third = tokens.get(3);
if (TokenType.INT.equals(third.getType()) == 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 (TokenType.INT.equals(first.getType()) == 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 (TokenType.COLON.equals(type)) {
index++;
if (index == tokens.size()) {
return index;
}
first = tokens.get(index);
type = first.getType();
}
if (TokenType.NAME.equals(type) == 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 ("TO".equals(tokens.get(index + 1).getToken()) == 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 (TokenType.NAME.equals(cur.getType()) == 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 (TokenType.OP_BRKT.equals(cur.getType()) == false) {
throw new SyntaxError(tokens, index);
}
dims.clear();
flag = 2;
break;
case 2:
if (TokenType.INT.equals(cur.getType()) == 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:
VarArray temp = new VarArray(dims);
if (name.lastIndexOf("$") > 0) {
temp.setType(VarArrayType.STR);
strarrs.put(name, temp);
} else {
numarrs.put(name, temp);
}
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 (TokenType.COLON.equals(tokens.get(index + 1).getType()) == false) {
throw new SyntaxError(tokens, index + 1);
}
}
// TODO 未テスト コード
Token first = tokens.get(index);
if (TokenType.INT.equals(first.getType()) == 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 (TokenType.COLON.equals(tokens.get(index + 1).getType()) == false) {
throw new SyntaxError(tokens, index + 1);
}
}
Token first = tokens.get(index);
if (TokenType.INT.equals(first.getType()) == 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 (TokenType.COLON.equals(tokens.get(index).getType()) == 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 (TokenType.INT.equals(first.getType()) == false) {
throw new SyntaxError(tokens, index);
}
int tmpint
= Integer.
parseInt(first.
getToken()); 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 (name.lastIndexOf("$") > 0) {
if (strarrs.containsKey(name)) {
if (TokenType.OP_BRKT.equals(tokens.get(index).getType()) == false) {
throw new SyntaxError(tokens, index);
}
index++;
List<Integer> indexes = new ArrayList<Integer>();
index = getIndexes(tokens, index, indexes);
index++;
if ("=".equals(tokens.get(index).getToken()) == false) {
throw new SyntaxError(tokens, index);
}
index++;
temp = calc(tokens, index);
if (ResultType.STR.equals(temp.getType()) == false) {
throw new ExcuteError("Invalid type value");
}
index = temp.getOffset();
strarrs.get(name).setStr(indexes, arraybase, temp.getStrValue());
} else {
if ("=".equals(tokens.get(index).getToken()) == false) {
throw new SyntaxError(tokens, index);
}
index++;
temp = calc(tokens, index);
if (ResultType.STR.equals(temp.getType()) == false) {
throw new ExcuteError("Invalid type value");
}
index = temp.getOffset();
strvars.put(name, temp.getStrValue());
}
} else {
if (numarrs.containsKey(name)) {
if (TokenType.OP_BRKT.equals(tokens.get(index).getType()) == false) {
throw new SyntaxError(tokens, index);
}
index++;
List<Integer> indexes = new ArrayList<Integer>();
index = getIndexes(tokens, index, indexes);
index++;
if ("=".equals(tokens.get(index).getToken()) == false) {
throw new SyntaxError(tokens ,index);
}
index++;
temp = calc(tokens, index);
index = temp.getOffset();
VarArray arr = numarrs.get(name);
switch (temp.getType()) {
case INT:
switch (arr.getType()) {
arr.setType(VarArrayType.INT);
case INT:
arr.setInt(indexes, arraybase, temp.getIntValue());
break;
case DBL:
arr.
setDbl(indexes, arraybase,
Double.
valueOf(temp.
getNumValue().
doubleValue())); break;
default:
throw new ExcuteError("Invalid type value");
}
break;
case DBL:
switch (arr.getType()) {
arr.setType(VarArrayType.DBL);
case DBL:
arr.setDbl(indexes, arraybase, temp.getDblValue());
break;
case INT:
arr.
setInt(indexes, arraybase,
Integer.
valueOf(temp.
getNumValue().
intValue())); break;
default:
throw new ExcuteError("Invalid type value");
} break;
default:
throw new ExcuteError("Invalid type value");
}
} else if (numvars.containsKey(name)) {
if ("=".equals(tokens.get(index).getToken()) == false) {
throw new SyntaxError(tokens ,index);
}
index++;
temp = calc(tokens, index);
index = temp.getOffset();
Number num
= numvars.
get(name
); switch (temp.getType()) {
case INT:
if (Integer.
class.
equals(num.
getClass())) { numvars.put(name, temp.getNumValue());
} else if (Double.
class.
equals(num.
getClass())) { numvars.
put(name,
Double.
valueOf(temp.
getNumValue().
doubleValue())); } else {
throw new ExcuteError("Invalid type value");
}
break;
case DBL:
if (Integer.
class.
equals(num.
getClass())) { numvars.
put(name,
Integer.
valueOf(temp.
getNumValue().
intValue())); } else if (Double.
class.
equals(num.
getClass())) { numvars.put(name, temp.getNumValue());
} else {
throw new ExcuteError("Invalid type value");
}
break;
default:
throw new ExcuteError("Invalid type value");
}
} else {
if ("=".equals(tokens.get(index).getToken()) == false) {
throw new SyntaxError(tokens, index);
}
index++;
temp = calc(tokens, index);
index = temp.getOffset();
switch (temp.getType()) {
case INT:
case DBL:
numvars.put(name, temp.getNumValue());
break;
default:
throw new ExcuteError("Invalid type value");
}
}
}
return excute(tokens, index + 1);
} catch (SyntaxError er) {
throw er;
// ex.printStackTrace();
throw new ExcuteError(tokens.toString());
}
}
}