505 lines
20 KiB
Python
Raw Normal View History

2025-11-25 16:06:53 +01:00
#!/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 <filename>")