Skip to content

Instantly share code, notes, and snippets.

@myzsyweb myzsyweb/basic.sh
Created Jun 20, 2012

Embed
What would you like to do?
a simple interpreter for BASIC written in AWK
#!/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
You can’t perform that action at this time.