Add defun (custom functions).

Rename defvar to setq, add local environment and let.
This commit is contained in:
redxef 2022-11-27 01:43:27 +01:00
parent 8b7e6ae7ba
commit ddabe282a4
Signed by: redxef
GPG key ID: 7DAC3AA211CBD921

View file

@ -23,27 +23,37 @@ except ImportError:
from yaml import SafeLoader from yaml import SafeLoader
def lazy_fc_if(env, a, b, c): def lazy_fc_if(env, local, a, b, c):
a.reduce(env) a.reduce(env, local)
if a.reduced: if a.reduced:
b.reduce(env) b.reduce(env, local)
return b return b.reduced
c.reduce(env) c.reduce(env, local)
return c.reduced
def lazy_fc_nif(env, a, b, c): def lazy_fc_nif(env, local, a, b, c):
a.reduce(env) a.reduce(env, local)
if not a.reduced: if not a.reduced:
b.reduce(env) b.reduce(env, local)
return b return b.reduced
c.reduce(env) c.reduce(env, local)
c.reduced
def fc_load(env, path): def lazy_fc_defun(env, local, name, variables, func):
_ = local
# need ugly hack, because variables are actually a function with n-1 args
varnames = [variables._fc] + [v._value for v in variables._args]
env.set_lisp_function(name._value, varnames, func)
def fc_load(env, local, path):
_ = local
ipc_value = env.input ipc_value = env.input
for k in path.strip('.').split('.'): for k in path.strip('.').split('.'):
ipc_value = ipc_value[k] ipc_value = ipc_value[k]
return ipc_value return ipc_value
def fc_has_key(env, path): def fc_has_key(env, local, path):
_ = local
ipc_value = env.input ipc_value = env.input
for k in path.strip('.').split('.'): for k in path.strip('.').split('.'):
try: try:
@ -58,28 +68,31 @@ class Environment:
self._input = input self._input = input
self._variables = {} self._variables = {}
self._functions = { self._functions = {
'__last__': lambda *a: a[-1], # special function, if multiple expressions, execute all and return result of last one '__last__': lambda _env, _local, *a: a[-1], # special function, if multiple expressions, execute all and return result of last one
'defvar': lambda env, n, v: env.set_variable(n, v), 'setq': lambda env, _, n, v: env.set_variable(n, v),
'write': lambda _, a: print(a), 'let': lambda _, local, n, v: local.set_variable(n, v),
'write': lambda _env, _local, a: print(a),
'load': fc_load, 'load': fc_load,
'has-key': fc_has_key, 'has-key': fc_has_key,
'=': lambda _, a, b: a == b, '=': lambda _, _l, a, b: a == b,
'!=': lambda _, a, b: a != b, '!=': lambda _, _l, a, b: a != b,
'>': lambda _, a, b: a > b, '>': lambda _, _l, a, b: a > b,
'<': lambda _, a, b: a < b, '<': lambda _, _l, a, b: a < b,
'>=': lambda _, a, b: a >= b, '>=': lambda _, _l, a, b: a >= b,
'<=': lambda _, a, b: a <= b, '<=': lambda _, _l, a, b: a <= b,
'+': lambda _, *a: sum(a), '+': lambda _, _l, *a: sum(a),
'-': lambda _, a, b: a - b, '-': lambda _, _l, a, b: a - b,
'*': lambda _, *a: functools.reduce(lambda a, b: a * b, a), '*': lambda _, _l, *a: functools.reduce(lambda a, b: a * b, a),
'/': lambda _, a, b: a // b, '/': lambda _, _l, a, b: a // b,
'|': lambda _, *a: functools.reduce(lambda a, b: a or b, a), '|': lambda _, _l, *a: functools.reduce(lambda a, b: a or b, a),
'&': lambda _, *a: functools.reduce(lambda a, b: a and b, a), '&': lambda _, _l, *a: functools.reduce(lambda a, b: a and b, a),
} }
self._lazy_functions = { self._lazy_functions = {
'?': lazy_fc_if, '?': lazy_fc_if,
'!?': lazy_fc_nif, '!?': lazy_fc_nif,
'defun': lazy_fc_defun,
} }
self._lisp_functions = {}
@property @property
def input(self): def input(self):
@ -97,6 +110,28 @@ class Environment:
def get_lazy_function(self, name: str): def get_lazy_function(self, name: str):
return self._lazy_functions[name] return self._lazy_functions[name]
def set_lisp_function(self, name: str, vars: list[object], e: object):
self._lisp_functions[name] = vars, e
def get_lisp_function(self, name: str) -> tuple[list[str], object]:
return self._lisp_functions[name]
class LocalEnvironment:
def __init__(self):
self._variables = {}
def copy(self) -> 'LocalEnvironment':
n = LocalEnvironment()
n._variables = self._variables.copy()
return n
def set_variable(self, name: str, value: object):
self._variables[name] = value
def get_variable(self, name: str):
return self._variables[name]
class Expression: class Expression:
STATE_CONSTRUCTED = 0 STATE_CONSTRUCTED = 0
@ -106,14 +141,14 @@ class Expression:
self._state = Expression.STATE_CONSTRUCTED self._state = Expression.STATE_CONSTRUCTED
self._reduced = None self._reduced = None
def _reduce(self, env: Environment, args: list[object]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[object]):
_ = env, args _ = env, local, args
raise NotImplementedError('Implement in subclass') raise NotImplementedError('Implement in subclass')
def reduce(self, env: Environment): def reduce(self, env: Environment, local: LocalEnvironment):
if self._state == Expression.STATE_REDUCED: if self._state == Expression.STATE_REDUCED:
return return
self._reduced = self._reduce(env, []) self._reduced = self._reduce(env, local, [])
self._state = Expression.STATE_REDUCED self._state = Expression.STATE_REDUCED
@property @property
@ -131,8 +166,8 @@ class Constant(Expression):
def __repr__(self): def __repr__(self):
return repr(self._value) return repr(self._value)
def _reduce(self, env: Environment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
_ = env, args _ = env, local, args
return self._value return self._value
class VariableSet(Constant): class VariableSet(Constant):
@ -140,9 +175,12 @@ class VariableSet(Constant):
class VariableGet(Constant): class VariableGet(Constant):
def _reduce(self, env: Environment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
_ = args _ = args
return env.get_variable(self._value) try:
return local.get_variable(self._value)
except KeyError:
return env.get_variable(self._value)
class Function(Expression): class Function(Expression):
@ -154,20 +192,30 @@ class Function(Expression):
def __repr__(self): def __repr__(self):
return f'({self._fc} {self._args})' return f'({self._fc} {self._args})'
def _reduce(self, env: Environment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
try: try:
fc = env.get_function(self._fc) argnames, fc = env.get_lisp_function(self._fc)
[a.reduce(env) for a in args] assert isinstance(fc, Expression)
r = fc(env, *[a.reduced for a in args]) l = local.copy()
for an, av in zip(argnames, args):
av.reduce(env, l)
l.set_variable(an, av.reduced)
fc.reduce(env, l)
r = fc.reduced
except KeyError as e: except KeyError as e:
fc = env.get_lazy_function(self._fc) try:
r = fc(env, *args) fc = env.get_function(self._fc)
[a.reduce(env, local) for a in args]
r = fc(env, local, *[a.reduced for a in args])
except KeyError:
fc = env.get_lazy_function(self._fc)
r = fc(env, local, *args)
return r return r
def reduce(self, env: Environment): def reduce(self, env: Environment, local: LocalEnvironment):
if self._state == Expression.STATE_REDUCED: if self._state == Expression.STATE_REDUCED:
return return
self._reduced = self._reduce(env, self._args) self._reduced = self._reduce(env, local, self._args)
self._state = Expression.STATE_REDUCED self._state = Expression.STATE_REDUCED
class Token: class Token:
@ -530,7 +578,7 @@ def window_new(runtime_data: RuntimeData, *, debug):
print(json.dumps(e.ipc_data)) print(json.dumps(e.ipc_data))
async with runtime_data.lock: async with runtime_data.lock:
for i, cfg in enumerate(runtime_data.programs): for i, cfg in enumerate(runtime_data.programs):
cfg.match.reduce(Environment(e.ipc_data)) cfg.match.reduce(Environment(e.ipc_data), LocalEnvironment())
if cfg.match.reduced: if cfg.match.reduced:
container_id = e.ipc_data['container']['id'] container_id = e.ipc_data['container']['id']
await ipc.command(f'for_window [con_id="{container_id}"] focus') await ipc.command(f'for_window [con_id="{container_id}"] focus')