Skip to content

Instantly share code, notes, and snippets.

@forestbelton
Created April 6, 2013 04:46
Show Gist options
  • Save forestbelton/5324847 to your computer and use it in GitHub Desktop.
Save forestbelton/5324847 to your computer and use it in GitHub Desktop.
VVTB interpreter
intro = [
'=======================================',
'VVTB (Very Very Tiny Basic) interpreter',
'(c) 2012 Forest Belton (case)',
'======================================='
]
prgm = {}
vars = {}
add_msg = (msg, error) ->
classes = if error is true then 'error' else ''
$('#msgs').append "<div class=\"#{classes}\">#{msg}</div>"
getlines = () ->
lines = []
for line of prgm
lines.push parseInt(line)
return lines.sort (a,b) -> a - b
print_prgm = () ->
keys = getlines()
for key in keys
add_msg prgm[key].raw, false
run_prgm = () ->
lines = getlines()
pc = lines[0]
while prgm[pc] isnt undefined
console.log "PC: #{pc}"
pc = eval_prgm pc, prgm[pc].stmt, lines
eval_prgm = (pc, stmt, lines) ->
switch stmt.type
when 'goto' then return stmt.line
when 'let' then vars[stmt.dest] = eval_expr stmt.val
when 'print' then add_msg (stmt.items.map(eval_expr).join ''), false
when 'if' then if stmt.cond.op eval_expr(stmt.cond.left), eval_expr(stmt.cond.right) then return stmt.line
for line in lines
if line > pc
return line
return undefined
eval_expr = (stmt) ->
if typeof stmt is 'object'
return stmt.op eval_expr(stmt.left), eval_expr(stmt.right)
else if typeof stmt is 'string'
return vars[stmt]
else
return stmt
readline = () ->
add_msg '> ' + $('#input').val(), false
try
line = vvtb.parse $('#input').val() + '\n'
catch error
add_msg error.message, true
return
switch line.stmt.type
when 'new' then prgm = {}
when 'list' then print_prgm()
when 'run' then run_prgm()
when 'delete' then delete prgm[line.line]
else prgm[line.line] = { 'raw' : $('#input').val(), 'stmt' : line.stmt }
$('document').ready () ->
for line in intro
add_msg line, false
$('#input').focus()
$(document).click () ->
$('#input').focus()
$('#input').keydown (e) ->
if e.which is 13
readline()
$('#input').val ''
{
function make_stmt(s, args) {
var stmt = { 'type' : s };
switch(s) {
case 'let':
stmt['dest'] = args[0];
stmt['val'] = args[1];
break;
case 'if':
stmt['cond'] = args[0];
stmt['line'] = args[1];
break;
case 'print':
stmt['items'] = args[0];
break;
case 'rem':
stmt['comment'] = args[0];
break;
case 'goto':
stmt['line'] = args[0];
break;
}
return stmt;
}
ops = {
'+' : function(x,y) { return x + y },
'-' : function(x,y) { return x - y },
'*' : function(x,y) { return x * y },
'/' : function(x,y) { return x / y },
'<' : function(x,y) { return x < y },
'>' : function(x,y) { return x > y },
'=' : function(x,y) { return x == y },
'<>' : function(x,y) { return x != y },
'<=' : function(x,y) { return x <= y },
'>=' : function(x,y) { return x >= y },
}
}
line
= line:cmd_line ws '\n' { return line }
/ line:prog_line ws '\n' { return line }
cmd_line
= 'RUN' { return { 'stmt' : make_stmt('run') } }
/ 'LIST' { return { 'stmt' : make_stmt('list') } }
/ 'NEW' { return { 'stmt' : make_stmt('new') } }
/ 'QUIT' { return { 'stmt' : make_stmt('quit') } }
prog_line
= line:number ws s:stmt? { if(s=='') s = make_stmt('delete',[]); return { 'line' : line, 'stmt' : s} }
stmt
= 'LET' ws v:variable ws '=' ws e:expr { return make_stmt('let', [v, e]) }
/ 'IF' ws cond:test ws 'THEN' ws line:number { return make_stmt('if', [cond, line]) }
/ 'PRINT' ws p:print_items { return make_stmt('print', [p]) }
/ 'REM' ws line:[^\n]+ { return make_stmt('rem', [line.join('')]) }
/ 'GOTO' ws line:number { return make_stmt('goto', [line]) }
print_items
= p:print_item ws ',' ws ps:print_items { ps.unshift(p); return ps }
/ p:print_item ws ';' ws ps:print_items { ps.unshift(p); return ps }
/ p:print_item { return [p] }
separator
= ','
/ ';'
print_item
= expr
/ string
test
= left:expr ws comp:comparison ws right:expr { return { 'left' : left, 'right' : right, 'op' : ops[comp] } }
comparison
= '='
/ '<>'
/ '<'
/ '<='
/ '>'
/ '>='
expr
= left:secondary ws op:[+-] ws right:expr { return { 'op' : ops[op], 'left' : left, 'right' : right } }
/ secondary
secondary
= left:primary ws op:[*/] ws right:secondary { return { 'op' : ops[op], 'left' : left, 'right' : right } }
/ p:primary { return p }
primary
= '(' e:expr ')' { return e }
/ variable
/ number
string
= ["] data:[^"]+ ["] { return data.join('') }
variable
= [A-Z]
number
= digits:[0-9]+ { return parseInt(digits.join('')) }
ws
= [ \t\v\f]*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment