More fixes for defun and more elaborate example in readme.

This commit is contained in:
redxef 2022-11-27 02:45:57 +01:00
parent 74e98f7e3b
commit 8763d8a382
Signed by: redxef
GPG key ID: 7DAC3AA211CBD921
2 changed files with 35 additions and 8 deletions

View file

@ -19,6 +19,7 @@ Run multiple programs by specifying a yaml configuration file:
--- ---
signal: signal number or name, optional. Should program entries which have signal: true wait for this signal before continuing to the next one. signal: signal number or name, optional. Should program entries which have signal: true wait for this signal before continuing to the next one.
timeout: timeout in milliseconds timeout: timeout in milliseconds
init: a lisp program, optional. Used to initialize the environment, useful to define custom functions which should be available everywhere.
programs: programs:
- match: a filter with which to match the window - match: a filter with which to match the window
workspace: string or null, the workspace to move windows to workspace: string or null, the workspace to move windows to
@ -87,6 +88,12 @@ This could be combined with waybar to enforce an ordering of tray applications:
```yaml ```yaml
signal: SIGUSR1 signal: SIGUSR1
timeout: 2000 timeout: 2000
init: |
(
(setq i3_path ".container.window_properties.class")
(setq sway_path ".container.app_id")
(defun "idmatch" (name) (= (? (has-key sway_path) (load sway_path) (load i3_path)) name))
)
programs: programs:
- cmd: 'nm-applet --indicator' - cmd: 'nm-applet --indicator'
match: '(False)' match: '(False)'

View file

@ -164,6 +164,8 @@ class Constant(Expression):
self._value = value self._value = value
def __repr__(self): def __repr__(self):
if isinstance(self._value, str):
return f'"{self._value}"'
return repr(self._value) return repr(self._value)
def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
@ -171,10 +173,15 @@ class Constant(Expression):
return self._value return self._value
class VariableSet(Constant): class VariableSet(Constant):
pass
def __repr__(self):
return self._value
class VariableGet(Constant): class VariableGet(Constant):
def __repr__(self):
return self._value
def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
_ = args _ = args
try: try:
@ -190,7 +197,8 @@ class Function(Expression):
self._args = args self._args = args
def __repr__(self): def __repr__(self):
return f'({self._fc} {self._args})' a = ' '.join([repr(a) for a in self._args])
return f'({self._fc} {a})'
def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]): def _reduce(self, env: Environment, local: LocalEnvironment, args: list[Expression]):
try: try:
@ -296,7 +304,7 @@ def token_extract_keyword(stream: str) -> tuple[Token, str]:
i += 1 i += 1
else: else:
raise ValueError('No keyword in stream') raise ValueError('No keyword in stream')
while stream[i] in string.ascii_letters + '_-><=!+-*/?&|': while stream[i] in string.ascii_letters + string.digits + '_-><=!+-*/?&|':
i += 1 i += 1
return Token(Token.KEYWORD, stream[:i]), stream[i:] return Token(Token.KEYWORD, stream[:i]), stream[i:]
@ -312,8 +320,11 @@ def token_extract_grouping_close(stream: str) -> tuple[Token, str]:
def token_extract_space(stream: str) -> tuple[Token, str]: def token_extract_space(stream: str) -> tuple[Token, str]:
i = 0 i = 0
try:
while stream[i] in string.whitespace: while stream[i] in string.whitespace:
i += 1 i += 1
except IndexError:
pass
return Token(Token.WHITESPACE, stream[:i]), stream[i:] return Token(Token.WHITESPACE, stream[:i]), stream[i:]
def tokenize(program: str) -> list[Token]: def tokenize(program: str) -> list[Token]:
@ -351,7 +362,7 @@ def tokenize_sanitize_function(token_before: Token | None, token: Token, token_a
def tokenize_sanitize_setvar(token_before: Token | None, token: Token, token_after: Token | None) -> Token | None: def tokenize_sanitize_setvar(token_before: Token | None, token: Token, token_after: Token | None) -> Token | None:
if token_before is None: if token_before is None:
return return
if (token_before.t == Token.FUNCTION and token_before.v == 'defvar') and token.t == Token.KEYWORD: if (token_before.t == Token.FUNCTION and token_before.v in ('setq', 'let')) and token.t == Token.KEYWORD:
return Token(Token.VARIABLE_SET, token.v) return Token(Token.VARIABLE_SET, token.v)
def tokenize_sanitize_getvar(token_before: Token | None, token: Token, token_after: Token | None) -> Token | None: def tokenize_sanitize_getvar(token_before: Token | None, token: Token, token_after: Token | None) -> Token | None:
@ -359,7 +370,7 @@ def tokenize_sanitize_getvar(token_before: Token | None, token: Token, token_aft
if token.t == Token.KEYWORD: if token.t == Token.KEYWORD:
return Token(Token.VARIABLE_GET, token.v) return Token(Token.VARIABLE_GET, token.v)
return return
if (token_before.t != Token.FUNCTION or token_before.v != 'defvar') and token.t == Token.KEYWORD: if (token_before.t != Token.FUNCTION or token_before.v not in ('setq', 'let')) and token.t == Token.KEYWORD:
return Token(Token.VARIABLE_GET, token.v) return Token(Token.VARIABLE_GET, token.v)
def _tokenize_sanitize(tokens: list[Token]) -> tuple[bool, list[Token]]: def _tokenize_sanitize(tokens: list[Token]) -> tuple[bool, list[Token]]:
@ -562,10 +573,12 @@ class ProgramConfig(pydantic.BaseModel):
class Config(pydantic.BaseModel): class Config(pydantic.BaseModel):
signal: typing.Optional[Signal] = None signal: typing.Optional[Signal] = None
timeout: int = 3000 timeout: int = 3000
init: typing.Optional[Filter] = None
programs: typing.List[ProgramConfig] programs: typing.List[ProgramConfig]
final_workspace: typing.Optional[str] = None final_workspace: typing.Optional[str] = None
class RuntimeData(pydantic.BaseModel): class RuntimeData(pydantic.BaseModel):
init: typing.Optional[Filter]
programs: typing.List[ProgramConfig] = [] programs: typing.List[ProgramConfig] = []
lock: Lock lock: Lock
event: Event event: Event
@ -578,7 +591,13 @@ 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), LocalEnvironment()) env = Environment(e.ipc_data)
local = LocalEnvironment()
if runtime_data.init is not None:
runtime_data.init.reduce(env, local)
cfg.match.reduce(env, local)
if debug:
print(cfg.match.reduced)
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')
@ -598,6 +617,7 @@ async def coro_wait_signal(coro, rt: RuntimeData):
async def init(config: Config, *, debug: bool) -> RuntimeData: async def init(config: Config, *, debug: bool) -> RuntimeData:
rd = RuntimeData( rd = RuntimeData(
init=str(config.init),
programs=[p for p in config.programs if p.workspace is not None], programs=[p for p in config.programs if p.workspace is not None],
lock=Lock(), lock=Lock(),
event=Event(), event=Event(),