Created
June 20, 2012 13:08
-
-
Save myzsyweb/2959816 to your computer and use it in GitHub Desktop.
a simple interpreter for BASIC written in AWK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/gawk -f | |
# a simple interpreter for GWBASIC(subset) written in AWK | |
# following 'The AWK Programming Language' Chapter 6 | |
# @date June 20,2012 | |
# @link basic.sh https://gist.github.com/2959816 | |
# @link test.sh https://gist.github.com/2959877 | |
# TODO: | |
# gosub/return...ok | |
# if/else/elseif/endif...ok | |
# while/wend...ok | |
# more unit test | |
# error warning | |
# builtin functon | |
# document | |
# clean code | |
# more features in gwbasic | |
# @ref 'The AWK Programming Language' | |
# @ref 'man mawk' | |
# @ref http://en.wikipedia.org/wiki/GW-BASIC | |
BEGIN{ | |
DEBUG_MODE = 0; | |
printf("BASIC\n"); | |
code = "\n" | |
#top = "top" | |
ifstack[0] = 1; | |
#ifstack[top] = 0; | |
ifstack["top"] = 0; | |
callstack[0] = 0; | |
callstack["top"] = 0; | |
#whilestack[0] = 0; | |
#whilestack["top"] = 0; | |
} | |
!/^( |\t)*\#/{ | |
if($0 ~ /^ *[A-Za-z_0-9]+ *\:$/){ | |
gsub(/ |\:/,""); | |
$0 = "@" $0; | |
} | |
code = code $0 "\n"; | |
} | |
END{ | |
debug("<CODE>"); | |
debug(code,length(code)); | |
debug("<RUN>\n\n\n"); | |
line = code; | |
codelen = length(code); | |
advance(); | |
stat(); | |
} | |
function push(Stack,x){ | |
Stack[++Stack["top"]] = x; | |
} | |
function pop(Stack){ | |
return Stack[Stack["top"]--]; | |
} | |
function advance() { | |
#if (tok == "(eof)") return "(eof)" | |
#while (length(line) == 0)if (getline line == 0)return tok = "(eof)"; | |
if (line == "") | |
return tok = "(eof)"; | |
if (match(line, /^[A-Za-z_][A-Za-z_0-9]*/) || | |
match(line, /^-?([0-9]+\.?[0-9]*|\.[0-9]+)/) || | |
#match(line, /^\"(\\.|[^"])*\"/) || | |
match(line, /^\"[^\"]*\"/) || | |
match(line, /^(<|<=|=|<>|>=|>)/) || | |
#match(line, /^\*\/\+\-]/) || | |
match(line, /^./)) { | |
tok = substr(line, 1, RLENGTH); | |
line = substr(line, RLENGTH+1); | |
if(tok ~ /^ +/) | |
return advance(); | |
return tok; | |
} | |
error("advance>line " substr(line,1,10) " incomprehensible at " line); | |
} | |
function eat(s) { # read next token if s == tok | |
if (tok != s) error("line " substr(line,1,20) ": saw " tok ", expected " s) | |
advance(); | |
} | |
function nl() { # absorb newlines and semicolons | |
while(tok ~ /^(\n|\:)/) | |
advance(); | |
#while (tok == "\n" || tok == ":") | |
#advance(); | |
} | |
function error(s) { print("Error: " s)| "cat 1>&2"; exit 1 } | |
function jump(label){ | |
if(label ~ /^[0-9]+/){ | |
if(!match(code,"\n *" tok " *")) | |
error("goto line"); | |
line = substr(code, RSTART+RLENGTH) | |
advance(); | |
}else if(label ~ /^[A-Za-z_][A-Za-z_0-9]*/){ | |
if(!match(code,"\n *@" tok " *(\n|$)")) | |
error("goto label"); | |
line = substr(code, RSTART+RLENGTH) | |
debug(RSTART","RLENGTH "goto>" "@" label " " line) | |
advance(); | |
}else{error("goto");} | |
} | |
function ifskip(){ | |
#debug("ifskipline> " ifstack["top"]) | |
#print_array(ifstack) | |
return (ifstack[ifstack["top"]]!=1) | |
} | |
function print_array(A, i,len,s){ | |
len=length(A); | |
s = A[0] "|" | |
for(i=1;i<=len;i++) | |
s = s " " A[i]; | |
debug(s); | |
} | |
function stat( lhs,op,e) { | |
if(tok=="(eof)") | |
return; | |
nl(); | |
if(tok ~ /^[0-9]+/) | |
advance(); #linenum | |
if(tok=="(eof)") | |
return; | |
if(tok == "@"){ | |
eat("@") | |
if(1)#use F_if_block | |
if(tok=="if"){ | |
debug(">if"); | |
eat("if"); | |
if(!ifskip()){ | |
e = (conj()!=0); | |
eat("\n"); | |
debug("if> " e); | |
ifstack[++ifstack["top"]] = e; | |
}else{ | |
ifstack[++ifstack["top"]] = -1; | |
} | |
}else if(tok=="else"){ | |
debug(">else"); | |
eat("else"); | |
eat("\n"); | |
if(ifstack[ifstack["top"]]==1){ | |
ifstack[ifstack["top"]] = 0; | |
}else if(ifstack[ifstack["top"]]==0) | |
ifstack[ifstack["top"]] = 1; | |
else | |
; | |
}else if(tok=="elseif"){ | |
debug(">elseif"); | |
eat("elseif"); | |
e = (conj()!=0) | |
eat("\n"); | |
if(ifstack[ifstack["top"]]==1){ | |
ifstack[ifstack["top"]] = 0; | |
}else if(ifstack[ifstack["top"]]==0){ | |
ifstack[ifstack["top"]] = e; | |
}else | |
; | |
}else if(tok=="endif"){ | |
debug(">endif"); | |
eat("endif"); | |
eat("\n"); | |
debug(ifstack["top"],ifstack[ifstack["top"]]); | |
delete ifstack[ifstack["top"]]; | |
#debug(ifstack["top"]--) | |
ifstack["top"]--; | |
debug(ifstack["top"],ifstack[ifstack["top"]]); | |
}else{ | |
#while(tok !~ /^\n/) | |
while(tok!="\n") | |
advance(); | |
} | |
else | |
while(tok !~ /^\n/) | |
advance(); | |
}else if(ifskip()){ | |
debug("skiptok>" tok); | |
while(tok !="\n" && tok!="(eof)"){ | |
advance(); | |
debug("skiptok->" tok); | |
} | |
}else{ | |
#debug("eval ing...") | |
lhs = lvalue(); | |
if (lhs == "print"){ | |
#eat("print") | |
#debug(conj()) | |
#return "" | |
if(tok !~ /\n|\:/){ | |
printf("%s",exprlist()); | |
if(tok==";"){ | |
eat(";"); | |
}else | |
printf("\n") | |
}else | |
printf("\n"); | |
#debug(e) | |
}else if(lhs == "let"){ | |
#eat("let") | |
lhs = lvalue(); | |
eat("="); | |
env[lhs] = conj(); | |
#return "" | |
}else if(lhs == "label"){ | |
advance(); | |
}else if(lhs == "input"){ | |
if(tok ~ /^\".*\"$/){ | |
gsub(/\"/,"",tok); | |
printf("%s",tok); | |
advance(); | |
eat(","); | |
} | |
e = lvalue(); | |
getline env[e]<"-" | |
#advance(); | |
}else if(lhs == "rem"){ | |
while(tok !~ /^\n/) | |
advance(); | |
}else if(lhs == "cls"){ | |
system("clear || cls"); | |
}else if(lhs == "if"){ | |
e = conj(); | |
debug("if " e ":"); | |
if(tok=="goto"){ | |
eat("goto"); | |
#debug("label>" tok "!") | |
if(e) | |
jump(tok); | |
else{ | |
advance(); | |
} | |
}else if(tok == "then"){ | |
eat("then") | |
if(tok ~ /[0-9]+/){ | |
if(e) | |
jump(tok); | |
else | |
advance(); | |
}else{ | |
if(e) | |
stat() | |
else | |
while(tok !~ /^\n/) | |
advance(); | |
} | |
}else{error("if")} | |
}else if(lhs == "goto"){ | |
jump(tok); | |
}else if(lhs == "end"){ | |
exit 0; | |
}else if(lhs == "gosub"){ | |
callstack[++callstack["top"]] = length(code)-length(line)+1 | |
debug("here is |" substr(code,callstack[callstack["top"]])); | |
jump(tok); | |
}else if(lhs == "return"){ | |
#jump(tok); | |
line = substr(code,callstack[callstack["top"]--]); | |
advance(); | |
}else if(lhs == "while"){ | |
e = conj(); | |
debug("while " e ":"); | |
if(e)#e!=0 | |
; | |
else{ | |
debug("skip while"); | |
e = 0; | |
#!!!!!!!!!!!!!!!!!!!!!!!!!! | |
while(e>=0){ | |
debug("while level> " e); | |
if(tok == "wend") | |
e--; | |
else if(tok=="while") | |
e++; | |
advance(); | |
} | |
} | |
#jump(tok) | |
#callstack[++callstack["top"]] = 0 | |
}else if(lhs == "wend"){ | |
debug("wend"); | |
goto_while(length(code)-length(line)+1); | |
debug(substr(code,1,length(code)-length(line)) "{end}"); | |
#jump(tok) | |
#line = substr(code,callstack[callstack["top"]--]); | |
#advance(); | |
}else{ | |
eat("="); | |
env[lhs] = conj(); | |
} | |
} | |
if(tok=="(eof)") | |
return; | |
else | |
stat() # use while later | |
} | |
function goto_while(len, i,l,offset,A){ | |
A["top"] = 0; | |
offset = 1; | |
line = substr(code,1,len); | |
advance(); | |
while(tok != "(eof)"){ | |
if(tok=="while"){ | |
debug("see>while{" substr(code,offset,20) "...}"); | |
A[++A["top"]] = offset;} | |
else if(tok=="wend"){ | |
A["top"]--; | |
} | |
offset = len-length(line); | |
advance(); | |
} | |
if(A["top"]>=0){ | |
line = substr(code,A[A["top"]+1]); | |
advance(); | |
}else | |
error(); | |
#exit 0; | |
} | |
function expr(e) { | |
e = rel(); | |
while (tok ~ /<|<=|=|<>|>=|>|&/) { | |
op = tok; | |
advance(); | |
if(op=="=") e = 0+e == 0+rel(); | |
else if(op=="<>") e = 0+e != 0+rel(); | |
else if(op=="<") e = 0+e < 0+rel(); | |
else if(op=="<=") e = 0+e <= 0+rel(); | |
else if(op==">") e = 0+e > 0+rel(); | |
else if(op==">=") e = 0+e >= 0+rel(); | |
else if(op=="&") e = e "" rel(); | |
} | |
return e | |
} | |
function conj( op,e){ | |
e = expr(); | |
while (tok == "and" || tok == "or") { | |
op = tok; | |
advance(); | |
if(op=="and") | |
e = e && expr(); | |
else if(op=="or") | |
e = e || expr(); | |
} | |
return e | |
} | |
function rel( op, e) { | |
e = term(); | |
while (tok == "+" || tok == "-") { | |
op = tok; | |
advance(); | |
if(op=="+") | |
e = e + term(); | |
else if(op=="-") | |
e = e - term(); | |
} | |
return e | |
} | |
function term( op, e) { # fact | fact [*/%] fact | |
e = fact(); | |
while (tok == "*" || tok == "/") { | |
op = tok; | |
advance(); | |
if(op=="*") | |
e = e * fact(); | |
else if(op=="/") | |
e = e / fact(); | |
} | |
return e; | |
} | |
function fact( e) { | |
debug("fact>" tok); | |
if (tok == "(") { | |
eat("("); e = expr(); eat(")"); | |
return e | |
} else if (tok ~ /^[A-Za-z][A-Za-z0-9]*/) { | |
return ident(); | |
} else if (tok ~ /^-?([0-9]+\.?[0-9]*|\.[0-9]+)/) { | |
e = tok | |
advance(); | |
return e | |
}else if(match(tok, /^\"[^\"]*\"/)){ | |
e = substr(tok,2,length(tok)-2); | |
debug("string>" e); | |
advance(); | |
return e | |
}else | |
error("fact>unexpected " tok " at line " substr(line,1,10)); | |
} | |
function ident( id, e) { | |
if (!match(tok, /^[A-Za-z_][A-Za-z_0-9]*/)) | |
error("ident>unexpected " tok " at line " substr(line,1,10)); | |
id = tok; | |
advance(); | |
if (tok == "[") { # array | |
eat("["); e = expr(); eat("]"); | |
return env[id,e] | |
} else if (tok == "(") { # function | |
eat("("); | |
e = ""; | |
if (tok != ")") { | |
e = exprlist(); | |
eat(")"); | |
} else eat(")"); | |
return builtin(id,e); | |
} else | |
return env[id]; | |
} | |
function lvalue( id,e){ | |
if (!match(tok, /^[A-Za-z_][A-Za-z_0-9]*/)) | |
error("lvalue>unexpected " tok " at line " substr(line,1,10)); | |
id = tok; | |
advance(); | |
if (tok == "[") { | |
eat("["); e = expr(); eat("]"); | |
return id SUBSEP e; | |
}else | |
return id; | |
} | |
function exprlist( n, e) { | |
e = conj() # has to be at least one | |
for (n = 1; tok == ","; n++) { | |
advance(); | |
if(tok ~ /\n|:/) | |
e = e SUBSEP " "; | |
else | |
e = e SUBSEP " " conj(); | |
} | |
return e | |
} | |
function builtin(fun_name,arg_list, n,A){ | |
n = split(arg_list,A,SUBSEP " "); | |
if(fun_name=="len"){ | |
return length(A[1]); | |
}else if(fun_name=="mod"){ | |
return A[1] % A[2]; | |
}else error(); | |
return; | |
} | |
function debug(x,y,z){ | |
#if(x) | |
if(DEBUG_MODE) | |
printf("%s\n",x y z); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment