Last active
May 26, 2018 03:37
-
-
Save king1600/bc36baa82ba0fe2d5a45f4a817aa9690 to your computer and use it in GitHub Desktop.
Python kasm v5
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
import string | |
from collections import deque | |
class Token: | |
__slots__ = ('op','args','lineno','text','next') | |
def __init__(self, **kwargs): | |
self.op = self.args = self.lineno = self.text = self.next = None | |
self.set(**kwargs) | |
def set(self, **kwargs): | |
for attr in self.__slots__: | |
if attr in kwargs: | |
setattr(self, attr, kwargs[attr]) | |
class Scope: | |
__slots__ = ('name','last','symbols') | |
def __init__(self, last=None, name=None): | |
self.name = name | |
self.last = last | |
self.symbols = {} | |
def set(self, symbol, value): | |
self.symbols[symbol] = value | |
def get(self, symbol): | |
if symbol in self.symbols: | |
return self.symbols[symbol]; | |
if self.last is not None: | |
return self.last.get(symbol) | |
return None | |
VARS = string.ascii_letters + '_' | |
INSTRUCTIONS = ('set','mov','exit', | |
'add','sub','div','mul','out', | |
'func','ret','call','push','pop', | |
'cmp','ne','lt','gt','lte','gte', | |
'jmp','jcp','ccal','name') | |
class KasmContext: | |
def __init__(self, stdout=print, max_cmd=-1): | |
self.stdout = stdout | |
self.cmd_inc = 0 | |
self.depth = 0 | |
self.stack = deque() | |
self.calls = deque() | |
self.max_cmd = max_cmd | |
self.g_scope = Scope(name='global') | |
self.scope = self.g_scope | |
self.max_recursion = 4096 | |
self.reg = self.rxx = self.carry = False | |
def eval(self, code): | |
token = self.lex(code) | |
while token is not None: | |
token = self.interpret(token) | |
def set(self, var, item): | |
if var == 'reg': | |
self.reg = item | |
elif var == 'rxx': | |
self.rxx = item | |
elif var == 'clr': | |
self.carry = var | |
elif self.scope.get(var) is not None: | |
self.scope.set(var, item) | |
else: | |
raise Exception("Cannot set {0} to {1}".format(var, item)) | |
def get(self, var): | |
if var == 'reg': | |
return self.reg | |
elif var == 'rxx': | |
return self.rxx | |
elif var == 'null': | |
return 0 | |
elif var == 'clr': | |
return self.carry | |
elif var == 'pop': | |
return self.stack.pop() | |
if var[0] in VARS: | |
value = self.scope.get(var) | |
if value == None: | |
raise Exception('"{0}" does not exist in scope'.format(var)) | |
return value | |
else: | |
return eval(var, {}) | |
def lex(self, code): | |
token, first, line, lines = None, None, '', code.split('\n') | |
for i in range(len(lines)): | |
try: | |
line = lines[i].lstrip().rstrip() | |
line = line.split(';')[0] | |
if line.isspace() or len(line) < 1: | |
continue | |
parts = line.split(' ') | |
if token is not None: | |
token.next = Token(lineno=i + 1, text=line, next=None) | |
token = token.next | |
else: | |
token = Token(lineno=i + 1, text=line, next=None) | |
first = token | |
token.op = parts[0].lower() | |
token.args = (l.rstrip().lstrip() for l in ' '.join(parts[1:]).split(',')) | |
token.args = tuple(filter(lambda x: len(x) > 0, token.args)) | |
if token.op not in INSTRUCTIONS: | |
raise Exception("Invalid instruction") | |
except Exception as err: | |
raise Exception('Error on line:{0} "{1}"> {2}'.format(i, lines[i], err)) | |
return first | |
def interpret(self, t): | |
if t == None: | |
return None | |
if self.depth > 0: | |
if t.op == 'ret': | |
self.depth -= 1 | |
return t.next | |
next_token = t.next | |
old_carry = self.carry | |
self.cmd_inc += 1 | |
if self.max_cmd > 0 and self.cmd_inc > self.max_cmd: | |
raise Exception("Maximum instructions issued") | |
if t.op == 'set': | |
self.scope.set(t.args[0], self.get(t.args[1])) | |
elif t.op == 'name': | |
self.scope.set(t.args[0], t) | |
elif t.op == 'mov': | |
self.set(t.args[0], self.get(t.args[1])) | |
elif t.op == 'exit': | |
return None | |
elif t.op == 'add': | |
self.set(t.args[0], self.get(t.args[0]) + self.get(t.args[1])) | |
elif t.op == 'sub': | |
self.set(t.args[0], self.get(t.args[0]) - self.get(t.args[1])) | |
elif t.op == 'mul': | |
self.set(t.args[0], self.get(t.args[0]) * self.get(t.args[1])) | |
elif t.op == 'div': | |
self.set(t.args[0], self.get(t.args[0]) / self.get(t.args[1])) | |
elif t.op == 'out': | |
self.stdout(' '.join([str(self.get(arg)) for arg in t.args])) | |
elif t.op == 'func': | |
self.scope.set(t.args[0], t) | |
self.depth += 1 | |
elif t.op == 'ret': | |
self.rxx = self.get(t.args[0]) | |
if len(self.calls) > 0: | |
if self.scope.last is not None: | |
self.scope = self.scope.last | |
next_token = self.calls.pop().next | |
elif t.op == 'call': | |
self.calls.append(t) | |
func = self.scope.get(t.args[0]) | |
self.scope = Scope(last=self.scope, name=func.args[0]) | |
next_token = func.next | |
elif t.op == 'push': | |
for arg in t.args: | |
self.stack.append(self.get(arg)) | |
elif t.op == 'pop': | |
self.set(t.args[0], self.stack.pop()) | |
elif t.op == 'cmp': | |
self.carry = 1 if self.get(t.args[0]) == self.get(t.args[1]) else 0 | |
elif t.op == 'ne': | |
self.carry = 1 if self.get(t.args[0]) != self.get(t.args[1]) else 0 | |
elif t.op == 'lt': | |
self.carry = 1 if self.get(t.args[0]) < self.get(t.args[1]) else 0 | |
elif t.op == 'gt': | |
self.carry = 1 if self.get(t.args[0]) > self.get(t.args[1]) else 0 | |
elif t.op == 'lte': | |
self.carry = 1 if self.get(t.args[0]) <= self.get(t.args[1]) else 0 | |
elif t.op == 'gte': | |
self.carry = 1 if self.get(t.args[0]) >= self.get(t.args[1]) else 0 | |
elif t.op == 'jmp': | |
return self.interpret(self.scope.get(t.args[0]).next) | |
elif t.op == 'jcp': | |
if self.carry: | |
next_token = self.scope.get(t.args[0]).next | |
elif t.op == 'ccal': | |
if self.carry: | |
self.calls.append(t) | |
func = self.scope.get(t.args[0]) | |
self.scope = Scope(last=self.scope, name=func.args[0]) | |
next_token = func.next | |
if self.carry and old_carry: | |
self.carry = 0 | |
return next_token | |
################# Example: #######################3 | |
kasm = KasmContext() | |
kasm.eval(""" | |
set i, 0 ; create variable i | |
name for_loop ; define a point for looping | |
out i ; output the variable i | |
add i, 1 ; i += 1 | |
lt i, 10 ; check if i < 10 (less than) | |
jcp for_loop ; if check success, jump back up to for_loop | |
exit ; if not success, exit program (not needed tho) | |
""") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice