Commit dd891640 authored by Roman Alifanov's avatar Roman Alifanov

Optimize codegen: eliminate subshell forks, inline dict/fs methods

- Comparisons set __CT_RET directly instead of $() subshell fork - Logical NOT (!) inlined same way - fs.exists() inlined to [[ -e ]] in conditions - fs.read uses $(<file) instead of $(cat), fs.write/append use printf - fs.exists/remove/mkdir avoid unnecessary $() wrapping - Dict .get()/.has()/.len() inlined for local dict vars - __ct_print simplified: removed unnecessary local variable - String concat ..= uses native += append - Skip __CT_RET self-assignment in return statements
parent 3d28f1b4
......@@ -221,11 +221,6 @@ def _interp(node: IRExpr, ctx: 'EmitContext') -> str:
lv = _interp_arith(node.left, ctx)
rv = _interp_arith(node.right, ctx)
return f'$(({lv} {node.operator} {rv}))'
# comparison: emit as $([[ ]] && echo true || echo false)
lb = _expr(node.left, ctx)
rb = _expr(node.right, ctx)
op = node.operator
return f'$([[ {lb} {op} {rb} ]] && echo true || echo false)'
if isinstance(node, (IRMethodCall, IRCall)):
inner = _expr(node, ctx)
if inner.startswith('$(') and inner.endswith(')'):
......@@ -369,7 +364,7 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str:
return f'"$(echo "" | awk "BEGIN{{printf \\"%g\\", {la} / {ra}}}")"'
return f'"$(({la} {op} {ra}))"'
# Comparison
# Comparison — set __CT_RET directly instead of $() subshell
if op in _CMP_OPS:
is_numeric = lt.kind in ('int', 'float') or rt.kind in ('int', 'float')
is_string = lt.kind == 'string' and rt.kind == 'string'
......@@ -377,20 +372,25 @@ def _binary(node: IRBinaryOp, ctx: 'EmitContext') -> str:
la = _to_arith(lv)
ra = _to_arith(rv)
arith_op = _ARITH_CMP[op]
return f'$([[ $(({la} {arith_op} {ra})) -ne 0 ]] && echo true || echo false)'
ctx.emit(f'(( {la} {arith_op} {ra} )) && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if is_numeric:
la = _to_arith(lv)
ra = _to_arith(rv)
arith_op = _ARITH_CMP[op]
return f'$([[ $(({la} {arith_op} {ra})) -ne 0 ]] && echo true || echo false)'
ctx.emit(f'(( {la} {arith_op} {ra} )) && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
str_op = _STR_CMP.get(op, op)
return f'$([[ {lv} {str_op} {rv} ]] && echo true || echo false)'
ctx.emit(f'[[ {lv} {str_op} {rv} ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
# Logical
if op == '&&':
return f'$([[ {lv} == "true" && {rv} == "true" ]] && echo true || echo false)'
ctx.emit(f'[[ {lv} == "true" && {rv} == "true" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if op == '||':
return f'$([[ {lv} == "true" || {rv} == "true" ]] && echo true || echo false)'
ctx.emit(f'[[ {lv} == "true" || {rv} == "true" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
return f'"{lv} {op} {rv}"'
......@@ -519,7 +519,8 @@ def _unary(node: IRUnaryOp, ctx: 'EmitContext') -> str:
operand = _expr(node.operand, ctx)
op = node.operator
if op == '!':
return f'$([[ {operand} == "true" ]] && echo false || echo true)'
ctx.emit(f'[[ {operand} == "true" ]] && {RET_VAR}=false || {RET_VAR}=true')
return f'"${{{RET_VAR}}}"'
if op == '-':
inner = _to_arith(operand)
return f'"$((-{inner}))"'
......@@ -759,7 +760,7 @@ def _try_inline_string_method(recv_name: str, method: str, args: list, ctx: 'Emi
tmp_r = ctx.fresh_tmp()
ctx.emit(f'{tmp_p}={old_arg}')
ctx.emit(f'{tmp_r}={new_arg}')
ctx.emit(f'{RET_VAR}="${{{recv_name}//"${{{tmp_p}}}"/"{{{tmp_r}}}"}}"')
ctx.emit(f'{RET_VAR}="${{{recv_name}//"${{{tmp_p}}}"/"${{{tmp_r}}}"}}"')
return f'"${{{RET_VAR}}}"'
if method == 'contains':
......@@ -866,6 +867,17 @@ def _stdlib_method_expr(node: IRMethodCall, ctx: 'EmitContext') -> str:
# Dict path
if is_known_dict or is_actual_dict or is_param_dict or is_field_dict:
# Fast path: inline dict methods for local dict vars (not params/fields)
if (is_actual_dict or is_known_dict) and not is_field_recv and not is_param_dict:
if method == 'get' and node.args:
key = _expr(node.args[0], ctx)
return f'"${{{recv_name}[{key}]}}"'
if method == 'has' and node.args:
key = _expr(node.args[0], ctx)
ctx.emit(f'[[ -v "{recv_name}[{key}]" ]] && {RET_VAR}=true || {RET_VAR}=false')
return f'"${{{RET_VAR}}}"'
if method == 'len':
return f'"${{#{recv_name}[@]}}"'
if is_param_dict or is_field_dict:
dict_ref = recv
else:
......
......@@ -239,8 +239,7 @@ def _assign(node: IRAssign, ctx: 'EmitContext') -> None:
# String concatenation assignment
if op == '..=':
val = _expr(value, ctx)
val_inner = val.strip('"')
ctx.emit(f'{target}="${{{target}}}{val_inner}"')
ctx.emit(f'{target}+={val}')
return
# Augmented assignment with arithmetic
......@@ -355,7 +354,8 @@ def _return(node: IRReturn, ctx: 'EmitContext') -> None:
ctx.emit(f'{RET_ARR}=("${{{RET_ARR}[@]}}")')
ctx.emit('return 0')
return
ctx.emit(f'{RET_VAR}={val}')
if val != f'"${{{RET_VAR}}}"':
ctx.emit(f'{RET_VAR}={val}')
ctx.emit('return 0')
......@@ -421,6 +421,14 @@ def _condition_bash(cond, ctx: 'EmitContext') -> str:
inner = _condition_bash(cond.operand, ctx)
return f'! ( {inner} )'
# Inline fs.exists(path) → [[ -e path ]]
from ...ir.nodes import IRMethodCall as _IRMCall
if (isinstance(cond, _IRMCall) and isinstance(cond.receiver, IRIdentifier)
and cond.receiver.name == 'fs' and cond.method_name == 'exists'
and cond.args):
path = expr_(cond.args[0], ctx)
return f'[[ -e {path} ]]'
# Variable or call that returns "true"/"false"
bash = expr_(cond, ctx)
if bash == '"true"':
......
......@@ -5,7 +5,7 @@ class CoreFunctions:
print = Method(
name="print",
bash_func="__ct_print",
bash_impl='local msg="$1"; echo -e "$msg" >&3',
bash_impl='echo -e "$1" >&3',
awk_builtin=lambda a: f"print {', '.join(a)}" if a else "print",
min_args=1, max_args=1,
)
......
......@@ -5,43 +5,43 @@ class FsMethods:
read = Method(
name="read",
bash_func="__ct_fs_read",
bash_impl='cat "$1"',
bash_impl='__CT_RET=$(<"$1")',
min_args=1, max_args=1,
)
write = Method(
name="write",
bash_func="__ct_fs_write",
bash_impl='echo -n "$2" > "$1"',
bash_impl='printf "%s" "$2" > "$1"; __CT_RET=""',
min_args=2, max_args=2,
)
append = Method(
name="append",
bash_func="__ct_fs_append",
bash_impl='echo -n "$2" >> "$1"',
bash_impl='printf "%s" "$2" >> "$1"; __CT_RET=""',
min_args=2, max_args=2,
)
exists = Method(
name="exists",
bash_func="__ct_fs_exists",
bash_impl='[[ -e "$1" ]] && echo "true" || echo "false"',
bash_impl='[[ -e "$1" ]] && __CT_RET=true || __CT_RET=false',
min_args=1, max_args=1,
)
remove = Method(
name="remove",
bash_func="__ct_fs_remove",
bash_impl='rm -f "$1"',
bash_impl='rm -f "$1"; __CT_RET=""',
min_args=1, max_args=1,
)
mkdir = Method(
name="mkdir",
bash_func="__ct_fs_mkdir",
bash_impl='mkdir -p "$1"',
bash_impl='mkdir -p "$1"; __CT_RET=""',
min_args=1, max_args=1,
)
list = Method(
name="list",
bash_func="__ct_fs_list",
bash_impl='ls -1 "$1" 2>/dev/null || true',
bash_impl='__CT_RET=$(ls -1 "$1" 2>/dev/null || true)',
min_args=1, max_args=1,
)
open = Method(
......
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