#!/usr/bin/env python3 import re, copy class Pointer: def __init__(s, v=None): s.val = v class NanoClass: def __init__(s, n, p, m, c, d): s.name, s.props, s.methods, s.constructor, s.destructor = n, p, m, c, d class NanoObject: def __init__(s, c, p): s._class, s._props = c, p class Runtime: def __init__(s): s.classes, s.globals, s.locals_stack = {}, {}, [{}] s.return_value, s.returning = None, False @property def locals(s): return s.locals_stack[-1] def push_scope(s): s.locals_stack.append({}) def pop_scope(s): len(s.locals_stack) > 1 and s.locals_stack.pop() def get_var(s, n): if n in s.locals: return s.locals[n] return s.globals.get(n) def set_var(s, n, v): for sc in reversed(s.locals_stack): if n in sc: sc[n] = v; return if n in s.globals: s.globals[n] = v else: s.locals[n] = v def set_local(s, n, v): s.locals[n] = v def tokenize(s, c): c = re.sub(r'//[^\n]*', '', re.sub(r'/\*.*?\*/', '', c, flags=re.DOTALL)) toks, pats = [], [ (r'\"[^\"]*\"', 'S'), (r"\'[^\']*\'", 'S'), (r'\d+\.?\d*', 'N'), (r'class\b', 'CL'), (r'new\b', 'NW'), (r'if\b', 'IF'), (r'else\b', 'EL'), (r'while\b', 'WH'), (r'for\b', 'FR'), (r'return\b', 'RT'), (r'null\b', 'NU'), (r'true\b', 'TR'), (r'false\b', 'FA'), (r'\*\*', 'DS'), (r'\|\|', 'OR'), (r'&&', 'AN'), (r'==', 'EQ'), (r'!=', 'NE'), (r'<=', 'LE'), (r'>=', 'GE'), (r'\+=', 'PE'), (r'-=', 'ME'), (r'\*=', 'UE'), (r'/=', 'DE'), (r'\+\+', 'IC'), (r'--', 'DC'), (r'[a-zA-Z_][a-zA-Z0-9_]*', 'I'), (r'[{}()\[\];,.<>+\-*/%=!&|:~]', 'Y'), (r'\s+', None)] i = 0 while i < len(c): for p, t in pats: m = re.match(p, c[i:]) if m: t and toks.append((t, m.group())) i += len(m.group()); break else: i += 1 return toks def parse_block(s, t, st): if st >= len(t) or t[st] != ('Y', '{'): return [], st d, i, b = 1, st + 1, [] while i < len(t) and d > 0: if t[i] == ('Y', '{'): d += 1 elif t[i] == ('Y', '}'): d -= 1 d > 0 and b.append(t[i]); i += 1 return b, i def parse_expr(s, t, st, es=None): es = es or [';', ',', ')', '}', ']'] e, d, i = [], 0, st while i < len(t): x = t[i] if d == 0 and x[0] == 'Y' and x[1] in es: break if x[0] == 'Y' and x[1] in '([{': d += 1 elif x[0] == 'Y' and x[1] in ')]}': d -= 1 e.append(x); i += 1 return e, i def to_num(s, v): if v is None: return 0 if isinstance(v, (int, float)): return v try: return int(v) except: try: return float(v) except: return 0 def is_truthy(s, v): if v is None: return False if isinstance(v, (int, float)): return v != 0 if isinstance(v, (str, list)): return len(v) > 0 return True def eval_expr(s, t): if not t: return None if len(t) == 1: x = t[0] if x[0] == 'N': return float(x[1]) if '.' in x[1] else int(x[1]) if x[0] == 'S': return x[1][1:-1] if x[0] == 'NU': return None if x[0] == 'TR': return 1 if x[0] == 'FA': return 0 if x[0] == 'I': return s.get_var(x[1]) if t[0] == ('NW', 'new'): return s.eval_new(t[1:]) if t[0] == ('Y', '*') and len(t) > 1: v = s.eval_expr(t[1:]) return v.val if isinstance(v, Pointer) else v if t[0] == ('Y', '&') and len(t) > 1 and t[1][0] == 'I': return Pointer(s.get_var(t[1][1])) if t[0] == ('Y', '{'): return s.eval_array(t) for i, x in enumerate(t): if x == ('OR', '||'): l = s.eval_expr(t[:i]) return l if s.is_truthy(l) else s.eval_expr(t[i+1:]) for i, x in enumerate(t): if x == ('AN', '&&'): l = s.eval_expr(t[:i]) return 0 if not s.is_truthy(l) else (1 if s.is_truthy(s.eval_expr(t[i+1:])) else 0) for op in [('EQ', '=='), ('NE', '!=')]: d = 0 for i, x in enumerate(t): if x[0] == 'Y' and x[1] in '([{': d += 1 elif x[0] == 'Y' and x[1] in ')]}': d -= 1 if d == 0 and x == op: l, r = s.eval_expr(t[:i]), s.eval_expr(t[i+1:]) return 1 if (l == r if op[1] == '==' else l != r) else 0 for op in [('LE', '<='), ('GE', '>='), ('Y', '<'), ('Y', '>')]: d = 0 for i, x in enumerate(t): if x[0] == 'Y' and x[1] in '([{': d += 1 elif x[0] == 'Y' and x[1] in ')]}': d -= 1 if d == 0 and x == op: l, r = s.to_num(s.eval_expr(t[:i])), s.to_num(s.eval_expr(t[i+1:])) if op[1] == '<': return 1 if l < r else 0 if op[1] == '>': return 1 if l > r else 0 if op[1] == '<=': return 1 if l <= r else 0 if op[1] == '>=': return 1 if l >= r else 0 d = 0 for i in range(len(t) - 1, -1, -1): x = t[i] if x[0] == 'Y' and x[1] in ')]}': d += 1 elif x[0] == 'Y' and x[1] in '([{': d -= 1 if d == 0: if x == ('Y', '+') and i > 0: l, r = s.eval_expr(t[:i]), s.eval_expr(t[i+1:]) if isinstance(l, str) or isinstance(r, str): return str(l if l is not None else '') + str(r if r is not None else '') return s.to_num(l) + s.to_num(r) if x == ('Y', '-') and i > 0: return s.to_num(s.eval_expr(t[:i])) - s.to_num(s.eval_expr(t[i+1:])) d = 0 for i in range(len(t) - 1, -1, -1): x = t[i] if x[0] == 'Y' and x[1] in ')]}': d += 1 elif x[0] == 'Y' and x[1] in '([{': d -= 1 if d == 0: if x == ('Y', '*') and i > 0: return s.to_num(s.eval_expr(t[:i])) * s.to_num(s.eval_expr(t[i+1:])) if x == ('Y', '/') and i > 0: r = s.to_num(s.eval_expr(t[i+1:])) return s.to_num(s.eval_expr(t[:i])) / r if r else 0 if x == ('Y', '%') and i > 0: r = s.to_num(s.eval_expr(t[i+1:])) return s.to_num(s.eval_expr(t[:i])) % r if r else 0 if t[0] == ('Y', '('): d, i = 1, 1 while i < len(t) and d > 0: if t[i] == ('Y', '('): d += 1 elif t[i] == ('Y', ')'): d -= 1 i += 1 return s.eval_expr(t[1:i-1]) if len(t) >= 2 and t[0][0] == 'I': if t[1] == ('Y', '('): return s.eval_call(t) if t[1] == ('Y', '.'): return s.eval_member(t) if t[1] == ('Y', '['): return s.eval_index(t) return None def eval_array(s, t): if not t or t[0] != ('Y', '{'): return [] items, i = [], 1 while i < len(t) and t[i] != ('Y', '}'): e, i = s.parse_expr(t, i, [',', '}']) e and items.append(s.eval_expr(e)) i < len(t) and t[i] == ('Y', ',') and (i := i + 1) return items def eval_new(s, t): if not t or t[0][0] != 'I': return None c = s.classes.get(t[0][1]) if not c: return None o = NanoObject(c, copy.deepcopy(c.props)) a = s.parse_args(t, 1) if len(t) > 1 and t[1] == ('Y', '(') else [] c.constructor and s.call_method(o, c.constructor, a) return o def parse_args(s, t, st): if st >= len(t) or t[st] != ('Y', '('): return [] a, i, d = [], st + 1, 1 while i < len(t) and d > 0: if t[i] == ('Y', ')'): d -= 1 if d == 0: break i += 1; continue if t[i] == ('Y', '('): d += 1 e, i = s.parse_expr(t, i, [',', ')']) e and a.append(s.eval_expr(e)) i < len(t) and t[i] == ('Y', ',') and (i := i + 1) return a def eval_call(s, t): n, a = t[0][1], s.parse_args(t, 1) if n == 'print': print(*a); return if n == 'len': return len(a[0]) if a and a[0] else 0 if n == 'str': return str(a[0]) if a and a[0] is not None else '' if n == 'int': if not a or a[0] is None: return 0 try: return int(float(a[0])) except: return 0 if n == 'bool': return 1 if a and s.is_truthy(a[0]) else 0 if n == 'typeof': if not a: return 'null' v = a[0] if v is None: return 'null' if isinstance(v, int): return 'int' if isinstance(v, float): return 'float' if isinstance(v, str): return 'str' if isinstance(v, list): return 'array' if isinstance(v, NanoObject): return v._class.name return 'unknown' f = s.get_var(n) if f and isinstance(f, tuple) and len(f) == 2: return s.call_func(f[0], f[1], a) return None def call_func(s, ps, bd, ar): s.push_scope() rp, va, vk = [], None, None for p in ps: if p.startswith('**'): vk = p[2:] elif p.startswith('*'): va = p[1:] else: rp.append(p) for i, p in enumerate(rp): if '=' in p: pn, df = p.split('=', 1) s.set_local(pn.strip(), ar[i] if i < len(ar) else s.eval_expr(s.tokenize(df.strip()))) else: s.set_local(p.strip(), ar[i] if i < len(ar) else None) va and s.set_local(va, list(ar[len(rp):])) vk and s.set_local(vk, {}) s.returning, s.return_value = False, None s.execute_block(bd) r = s.return_value s.returning, s.return_value = False, None s.pop_scope() return r def eval_member(s, t): o, i = s.get_var(t[0][1]), 2 while i < len(t): if t[i] == ('Y', '['): e, j = s.parse_expr(t, i + 1, [']']) idx = s.eval_expr(e) if isinstance(o, (list, str)): idx = int(idx) if idx is not None else 0 o = o[idx] if 0 <= idx < len(o) else None elif isinstance(o, dict): o = o.get(idx) i = j + 1 continue if t[i][0] != 'I': break m = t[i][1]; i += 1 if i < len(t) and t[i] == ('Y', '('): a = s.parse_args(t, i) o = s.call_obj_method(o, m, a) d = 1; i += 1 while i < len(t) and d > 0: if t[i] == ('Y', '('): d += 1 elif t[i] == ('Y', ')'): d -= 1 i += 1 else: if isinstance(o, NanoObject): o = o._props.get(m) elif isinstance(o, dict): o = o.get(m) elif isinstance(o, str) and m == 'length': o = len(o) elif isinstance(o, list) and m == 'length': o = len(o) if i < len(t) and t[i] == ('Y', '.'): i += 1 elif i < len(t) and t[i] == ('Y', '['): pass else: break return o def call_obj_method(s, o, m, a): if isinstance(o, str): if m == 'substr': return o[int(a[0]) if a else 0:(int(a[0]) if a else 0)+(int(a[1]) if len(a)>1 else len(o))] if m == 'split': return o.split(a[0] if a else ' ') if m == 'count': return o.count(a[0] if a else '') if m == 'indexOf': return o.find(a[0] if a else '') if m == 'toUpper': return o.upper() if m == 'toLower': return o.lower() if m == 'trim': return o.strip() if m == 'replace': return o.replace(a[0] if a else '', a[1] if len(a)>1 else '') if isinstance(o, list): if m == 'push': o.append(a[0] if a else None); return len(o) if m == 'pop': return o.pop() if o else None if m == 'join': return (str(a[0]) if a else '').join(str(x) for x in o) if m == 'indexOf': try: return o.index(a[0] if a else None) except: return -1 if m == 'slice': return o[int(a[0]) if a else 0:int(a[1]) if len(a)>1 else len(o)] if isinstance(o, NanoObject) and m in o._class.methods: return s.call_method(o, o._class.methods[m], a) return None def call_method(s, o, md, ar): ps, bd = md s.push_scope() rp, va, vk = [], None, None for p in ps: if p.startswith('**'): vk = p[2:] elif p.startswith('*'): va = p[1:] else: rp.append(p) if rp and rp[0].strip() == 'this': s.set_local('this', o); rp = rp[1:] for i, p in enumerate(rp): if '=' in p: pn, df = p.split('=', 1) s.set_local(pn.strip(), ar[i] if i < len(ar) else s.eval_expr(s.tokenize(df.strip()))) else: s.set_local(p.strip(), ar[i] if i < len(ar) else None) va and s.set_local(va, list(ar[len(rp):])) vk and s.set_local(vk, {}) s.returning, s.return_value = False, None s.execute_block(bd) r = s.return_value s.returning, s.return_value = False, None s.pop_scope() return r def eval_index(s, t): o = s.get_var(t[0][1]) i = 1 while i < len(t) and t[i] == ('Y', '['): e, j = s.parse_expr(t, i + 1, [']']) idx = s.eval_expr(e) if isinstance(o, (list, str)): idx = int(idx) if idx is not None else 0 o = o[idx] if 0 <= idx < len(o) else None elif isinstance(o, dict): o = o.get(idx) else: return None i = j + 1 return o def execute(s, c): s.execute_tokens(s.tokenize(c)) def execute_tokens(s, t): i = 0 while i < len(t): if s.returning: return s.return_value if t[i] == ('CL', 'class'): i = s.parse_class(t, i) elif t[i] == ('IF', 'if'): i = s.execute_if(t, i) elif t[i] == ('WH', 'while'): i = s.execute_while(t, i) elif t[i] == ('FR', 'for'): i = s.execute_for(t, i) elif t[i] == ('RT', 'return'): e, i = s.parse_expr(t, i + 1, [';']) s.return_value, s.returning = s.eval_expr(e), True return s.return_value else: st, i = s.parse_expr(t, i, [';']) st and s.execute_stmt(st) i < len(t) and t[i] == ('Y', ';') and (i := i + 1) return None def execute_block(s, t): return s.execute_tokens(t) def parse_class(s, t, st): n = t[st + 1][1] bd, i = s.parse_block(t, st + 2) ps, ms, ct, dt, j = {}, {}, None, None, 0 while j < len(bd): if bd[j] == ('Y', '~') and j + 1 < len(bd): j += 1 if bd[j][0] == 'I' and bd[j][1] == n: j += 1 pm, j = s.parse_params(bd, j) mb, j = s.parse_block(bd, j) dt = (pm, mb) elif bd[j][0] == 'I': pn = bd[j][1]; j += 1 if j < len(bd) and bd[j] == ('Y', '('): pm, j = s.parse_params(bd, j) mb, j = s.parse_block(bd, j) if pn == n: ct = (pm, mb) else: ms[pn] = (pm, mb) elif j < len(bd) and bd[j] == ('Y', '='): j += 1 e, j = s.parse_expr(bd, j, [';']) ps[pn] = s.eval_expr(e) j < len(bd) and bd[j] == ('Y', ';') and (j := j + 1) else: ps[pn] = None j < len(bd) and bd[j] == ('Y', ';') and (j := j + 1) else: j += 1 s.classes[n] = NanoClass(n, ps, ms, ct, dt) return i def parse_params(s, t, st): if st >= len(t) or t[st] != ('Y', '('): return [], st ps, i, c, d = [], st + 1, '', 0 while i < len(t): x = t[i] if x == ('Y', '('): d += 1; c += x[1] elif x == ('Y', ')'): if d == 0: c.strip() and ps.append(c.strip()); i += 1; break d -= 1; c += x[1] elif x == ('Y', ',') and d == 0: c.strip() and ps.append(c.strip()); c = '' else: c += x[1] i += 1 return ps, i def execute_if(s, t, st): i = st + 1 if i >= len(t) or t[i] != ('Y', '('): return i cd, i = s.parse_expr(t, i + 1, [')']); i += 1 bd, i = s.parse_block(t, i) eb = [] if i < len(t) and t[i] == ('EL', 'else'): i += 1 if i < len(t) and t[i] == ('IF', 'if'): return s.execute_if(t, i) if not s.is_truthy(s.eval_expr(cd)) else i eb, i = s.parse_block(t, i) if s.is_truthy(s.eval_expr(cd)): s.execute_block(bd) elif eb: s.execute_block(eb) return i def execute_while(s, t, st): i = st + 1 if i >= len(t) or t[i] != ('Y', '('): return i cd, ce = s.parse_expr(t, i + 1, [')']) bd, i = s.parse_block(t, ce + 1) while s.is_truthy(s.eval_expr(cd)): s.execute_block(bd) if s.returning: break return i def execute_for(s, t, st): i = st + 1 if i >= len(t) or t[i] != ('Y', '('): return i it, i = s.parse_expr(t, i + 1, [';']) it and s.execute_stmt(it); i += 1 cd, i = s.parse_expr(t, i, [';']); i += 1 up, i = s.parse_expr(t, i, [')']); i += 1 bd, i = s.parse_block(t, i) while s.is_truthy(s.eval_expr(cd)): s.execute_block(bd) if s.returning: break up and s.execute_stmt(up) return i def execute_stmt(s, t): if not t: return for i, x in enumerate(t): if x == ('PE', '+='): tg, vl = t[:i], s.eval_expr(t[i+1:]) cr = s.eval_expr(tg) nv = str(cr if cr is not None else '') + str(vl if vl is not None else '') if isinstance(cr, str) or isinstance(vl, str) else s.to_num(cr) + s.to_num(vl) s.assign(tg, nv); return if x == ('ME', '-='): s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) - s.to_num(s.eval_expr(t[i+1:]))); return if x == ('UE', '*='): s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) * s.to_num(s.eval_expr(t[i+1:]))); return if x == ('DE', '/='): r = s.to_num(s.eval_expr(t[i+1:])) s.assign(t[:i], s.to_num(s.eval_expr(t[:i])) / r if r else 0); return if x == ('Y', '='): s.assign(t[:i], s.eval_expr(t[i+1:])); return for i, x in enumerate(t): if x == ('IC', '++'): tg = t[:i] if i > 0 else t[i+1:]; s.assign(tg, s.to_num(s.eval_expr(tg)) + 1); return if x == ('DC', '--'): tg = t[:i] if i > 0 else t[i+1:]; s.assign(tg, s.to_num(s.eval_expr(tg)) - 1); return s.eval_expr(t) def assign(s, tg, v): if not tg: return if len(tg) == 1 and tg[0][0] == 'I': s.set_var(tg[0][1], v); return if len(tg) >= 3 and tg[1] == ('Y', '.'): o, i = s.get_var(tg[0][1]), 2 while i < len(tg) - 2: m = tg[i][1] if isinstance(o, NanoObject): o = o._props.get(m) elif isinstance(o, dict): o = o.get(m) i += 2 m = tg[i][1] if isinstance(o, NanoObject): o._props[m] = v elif isinstance(o, dict): o[m] = v return if len(tg) >= 3 and tg[1] == ('Y', '['): o = s.get_var(tg[0][1]) e, _ = s.parse_expr(tg, 2, [']']) idx = s.eval_expr(e) if isinstance(o, list): idx = int(idx) if idx is not None else 0 while len(o) <= idx: o.append(None) o[idx] = v elif isinstance(o, dict): o[idx] = v return if tg[0] == ('Y', '*'): p = s.eval_expr(tg[1:]) isinstance(p, Pointer) and setattr(p, 'val', v) def run_file(fn): with open(fn) as f: Runtime().execute(f.read()) if __name__ == '__main__': import sys len(sys.argv) > 1 and run_file(sys.argv[1]) or print("Usage: python nano.py ")