Commit 486b06d0 authored by Roman Alifanov's avatar Roman Alifanov

Add foreach proc iteration, fix DCE/CSE/dispatch bugs, hyphenated commands

- Add foreach line in proc syntax for process handle iteration - Fix DCE not tracking class methods used via objects - Fix CSE precomputing is_empty/is_number (they echo, don't set __CT_RET) - Fix proc.read() leaking echo to stdout when assigning to variable - Fix if/while condition with shell calls resolving incorrectly - Support hyphenated shell commands in lexer (e.g. dbus-monitor)
parent 01353d48
......@@ -772,6 +772,17 @@ proc.pid # PID процесса
await proc
```
Итерация по stdout процесса:
```
proc = async dbus-monitor ("interface='org.freedesktop.portal.Settings'")
foreach line in proc {
print ("got: {line}")
}
```
Компилируется в `while read -r line <&$fd; do ... done`.
Контекстный менеджер с автоматическим cleanup:
```
......@@ -1246,10 +1257,16 @@ name = whoami ()
# Pipe — нативный bash pipe
files = ls ("-la") | grep ("txt") | wc ("-l")
# Дефисные команды работают напрямую
dbus-monitor ("interface='org.freedesktop.portal.Settings'")
xdg-open ("https://example.com")
```
Аргументы передаются в кавычках: `grep ("pattern")``grep "pattern"`.
Дефисные имена (`dbus-monitor`, `xdg-open`, `apt-get`) поддерживаются как идентификаторы. Оператор `-` (минус) требует пробелы вокруг: `a - b`, а не `a-b`.
**Legacy:** `shell.exec/capture/source` по-прежнему работают для обратной совместимости:
```
......
......@@ -316,6 +316,12 @@ proc.write ("hello")
proc.close ()
await proc
# Iterate over process stdout
proc = async dbus-monitor ("interface='org.freedesktop.portal.Settings'")
foreach line in proc {
print ("got: {line}")
}
# Access process PID
proc = async sleep ("10")
print ("PID: {proc.pid}")
......
......@@ -316,6 +316,12 @@ proc.write ("hello")
proc.close ()
await proc
# Итерация по stdout процесса
proc = async dbus-monitor ("interface='org.freedesktop.portal.Settings'")
foreach line in proc {
print ("got: {line}")
}
# Доступ к PID процесса
proc = async sleep ("10")
print ("PID: {proc.pid}")
......
......@@ -134,13 +134,16 @@ class CseMixin:
elif isinstance(call.callee, Identifier):
func_name = call.callee.name
if func_name in ('is_empty', 'is_number'):
continue
args = [self.generate_expr(arg) for arg in call.arguments]
args_str = " ".join([f'"{a}"' for a in args])
key = f"{func_name}({args_str})"
if key not in seen:
temp = self.new_temp()
call_line = f'{func_name} {args_str} >/dev/null'
resolved_call = self.generate_call_statement(call)
call_line = f'{resolved_call} >/dev/null'
assign_line = f'{temp}="${{{RET_VAR}}}"'
self.emit(call_line)
self.emit(assign_line)
......
......@@ -503,6 +503,12 @@ class UsageAnalyzer:
self.used_methods[obj_class].add(method)
else:
self._check_method(method)
for cls_name, cls_decl in self.defined_classes.items():
for m in cls_decl.methods:
if m.name == method:
if cls_name not in self.used_methods:
self.used_methods[cls_name] = set()
self.used_methods[cls_name].add(method)
else:
found_in_class = False
for cls_name, cls_decl in self.defined_classes.items():
......
......@@ -811,9 +811,10 @@ class DispatchMixin:
self.emit(f'echo "{data}" >&${cp}_wr')
elif method == "read":
self.emit(f'read -r {RET_VAR} <&${cp}_rd')
self.emit(f'echo "${RET_VAR}"')
if target:
self.emit_var_assign(target, f'${RET_VAR}')
else:
self.emit(f'echo "${RET_VAR}"')
elif method == "close":
self.emit(f'exec {{{cp}_wr}}>&-')
elif method == "kill":
......
......@@ -131,6 +131,9 @@ class Lexer:
if ch.isalnum () or ch == '_':
result.append (ch)
self.advance ()
elif ch == '-' and self.peek () and (self.peek ().isalpha () or self.peek () == '_'):
result.append (ch)
self.advance ()
else:
break
return ''.join (result)
......
......@@ -81,10 +81,15 @@ class StmtMixin:
def generate_if(self, stmt: IfStmt):
if isinstance(stmt.condition, CallExpr) and isinstance(stmt.condition.callee, Identifier):
func_name = stmt.condition.callee.name
args = [self.generate_expr(a) for a in stmt.condition.arguments]
args_str = " ".join([f'"{a}"' for a in args])
self.emit(f'{func_name} {args_str} >/dev/null')
self.emit(f'if [[ "${{{RET_VAR}}}" == "true" ]]; then')
resolved = self._resolve_name(func_name)
if resolved in self.functions or func_name in getattr(self, 'callback_vars', set()):
args = [self.generate_expr(a) for a in stmt.condition.arguments]
args_str = " ".join([f'"{a}"' for a in args])
self.emit(f'{resolved} {args_str} >/dev/null')
self.emit(f'if [[ "${{{RET_VAR}}}" == "true" ]]; then')
else:
cond = self.generate_condition(stmt.condition)
self.emit(f"if {cond}; then")
else:
mapping, _ = self.precompute_all_calls(stmt.condition)
if mapping:
......@@ -190,16 +195,18 @@ class StmtMixin:
if isinstance(stmt.condition, CallExpr) and isinstance(stmt.condition.callee, Identifier):
func_name = stmt.condition.callee.name
args = [self.generate_expr(a) for a in stmt.condition.arguments]
args_str = " ".join([f'"{a}"' for a in args])
self.emit("while true; do")
with self.indented():
self.emit(f'{func_name} {args_str} >/dev/null')
self.emit(f'if [[ "${{{RET_VAR}}}" != "true" ]]; then break; fi')
for s in stmt.body.statements:
self.generate_statement(s)
self.emit("done")
return
resolved = self._resolve_name(func_name)
if resolved in self.functions or func_name in getattr(self, 'callback_vars', set()):
args = [self.generate_expr(a) for a in stmt.condition.arguments]
args_str = " ".join([f'"{a}"' for a in args])
self.emit("while true; do")
with self.indented():
self.emit(f'{resolved} {args_str} >/dev/null')
self.emit(f'if [[ "${{{RET_VAR}}}" != "true" ]]; then break; fi')
for s in stmt.body.statements:
self.generate_statement(s)
self.emit("done")
return
cond = self.generate_condition(stmt.condition)
self.emit(f"while {cond}; do")
......@@ -251,6 +258,16 @@ class StmtMixin:
if isinstance(stmt.iterable, Identifier):
arr_name = stmt.iterable.name
if arr_name in getattr(self, 'process_handle_vars', set()):
cp = self.process_handle_map[arr_name]
var = stmt.variables[0]
self.emit(f'while read -r {var} <&${cp}_rd; do')
with self.indented():
for s in stmt.body.statements:
self.generate_statement(s)
self.emit("done")
return
param_map = getattr(self, 'param_name_map', {})
arr_name = param_map.get(arr_name, arr_name)
if len(stmt.variables) == 1:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment