Commit c3bf25cc authored by Roman Alifanov's avatar Roman Alifanov

Refactor stdlib.py to use declarative method definitions

- Import method definitions from methods.py for consistency - Define bash implementations in STRING_IMPLS, ARRAY_IMPLS, DICT_IMPLS - Generate stdlib functions from these definitions - Use context manager (with self.indented()) for cleaner code - Reduce code duplication and improve maintainability
parent 0e7f2d60
"""Standard library code generation.
Generates bash implementations for ContenT stdlib functions.
Uses method definitions from methods.py for consistency.
"""
from .methods import STRING_METHODS, ARRAY_METHODS, DICT_METHODS, FILE_HANDLE_METHODS
# Bash implementations for string methods
STRING_IMPLS = {
"len": '__CT_RET=${#1}; echo "$__CT_RET"',
"substr": '__CT_RET="${1:$2:$3}"; echo "$__CT_RET"',
"index": 'local i="${1%%$2*}"; [[ "$i" == "$1" ]] && __CT_RET=-1 || __CT_RET=${#i}; echo "$__CT_RET"',
"contains": '[[ "$1" == *"$2"* ]] && __CT_RET=true || __CT_RET=false; echo "$__CT_RET"',
"starts": '[[ "$1" == "$2"* ]] && __CT_RET=true || __CT_RET=false; echo "$__CT_RET"',
"ends": '[[ "$1" == *"$2" ]] && __CT_RET=true || __CT_RET=false; echo "$__CT_RET"',
"replace": '__CT_RET="${1//"$2"/"$3"}"; echo "$__CT_RET"',
"split": 'local IFS="$2"; read -ra __CT_RET_ARR <<< "$1"',
"trim": 'local s="$1"; s="${s#"${s%%[![:space:]]*}"}"; __CT_RET="${s%"${s##*[![:space:]]}"}" ; echo "$__CT_RET"',
"upper": '__CT_RET="${1^^}"; echo "$__CT_RET"',
"lower": '__CT_RET="${1,,}"; echo "$__CT_RET"',
"charAt": '__CT_RET="${1:$2:1}"; printf \'%sX\' "$__CT_RET"',
"urlencode": "__CT_RET=$(printf '%s' \"$1\" | jq -sRr @uri); echo \"$__CT_RET\"",
}
# Bash implementations for array methods
ARRAY_IMPLS = {
"push": 'local -n __a=$1; shift; __a+=("$@")',
"pop": "local -n __a=$1; unset '__a[-1]'",
"shift": 'local -n __a=$1; __CT_RET="${__a[0]}"; __a=("${__a[@]:1}"); echo "$__CT_RET"',
"join": 'local -n __a=$1; local sep; printf -v sep \'%b\' "$2"; local IFS="$sep"; __CT_RET="${__a[*]}"; echo "$__CT_RET"',
"len": 'local -n __a=$1; __CT_RET=${#__a[@]}; echo "$__CT_RET"',
"get": 'local -n __a=$1; __CT_RET="${__a[$2]}"; echo "$__CT_RET"',
"set": 'local -n __a=$1; __a[$2]="$3"',
"slice": 'local -n __a=$1; __CT_RET_ARR=("${__a[@]:$2:$3}")',
}
# Bash implementations for dict methods
DICT_IMPLS = {
"set": 'local -n __d="$1"; __d["$2"]="$3"',
"get": 'local -n __d="$1"; __CT_RET="${__d[$2]}"; echo "$__CT_RET"',
"has": 'local -n __d="$1"; [[ -v "__d[$2]" ]] && __CT_RET=true || __CT_RET=false; echo "$__CT_RET"',
"del": 'local -n __d="$1"; unset "__d[$2]"',
"keys": 'local -n __d="$1"; __CT_RET_ARR=("${!__d[@]}")',
"len": 'local -n __d="$1"; __CT_RET=${#__d[@]}; echo "$__CT_RET"',
}
class StdlibMixin: class StdlibMixin:
"""Mixin class for standard library code generation. """Mixin class for standard library code generation."""
Provides emit_stdlib() method that generates all ContenT def emit_stdlib(self, used_categories: set = None):
standard library functions in Bash. """Emit the ContenT standard library."""
""" self.emit("# === ContenT Standard Library ===")
self.emit()
def emit_stdlib (self, used_categories: set = None): self._emit_core()
"""Emit the ContenT standard library.
Args:
used_categories: Set of category names to emit. If None, emit all.
"""
self.emit ("# === ContenT Standard Library ===")
self.emit ()
self._emit_core ()
if used_categories is None: if used_categories is None:
self._emit_http () self._emit_http()
self._emit_fs () self._emit_fs()
self._emit_json () self._emit_json()
self._emit_object_system () self._emit_object_system()
self._emit_exception () self._emit_exception()
self._emit_logger () self._emit_logger()
self._emit_string () self._emit_string()
self._emit_array () self._emit_array()
self._emit_regex () self._emit_regex()
self._emit_utils () self._emit_utils()
self._emit_awk_wrapper () self._emit_awk_wrapper()
self._emit_math () self._emit_math()
self._emit_dict () self._emit_dict()
self._emit_misc () self._emit_misc()
else: else:
if 'http' in used_categories: if 'http' in used_categories:
self._emit_http () self._emit_http()
if 'fs' in used_categories: if 'fs' in used_categories:
self._emit_fs () self._emit_fs()
if 'json' in used_categories: if 'json' in used_categories:
self._emit_json () self._emit_json()
if 'object' in used_categories: if 'object' in used_categories:
self._emit_object_system () self._emit_object_system()
if 'exception' in used_categories: if 'exception' in used_categories:
self._emit_exception () self._emit_exception()
if 'logger' in used_categories: if 'logger' in used_categories:
self._emit_logger () self._emit_logger()
if 'string' in used_categories: if 'string' in used_categories:
self._emit_string () self._emit_string()
if 'array' in used_categories: if 'array' in used_categories:
self._emit_array () self._emit_array()
if 'regex' in used_categories: if 'regex' in used_categories:
self._emit_regex () self._emit_regex()
if 'args' in used_categories: if 'args' in used_categories:
self._emit_args () self._emit_args()
else: else:
self._emit_utils_minimal () self._emit_utils_minimal()
if 'awk' in used_categories: if 'awk' in used_categories:
self._emit_awk_wrapper () self._emit_awk_wrapper()
if 'math' in used_categories: if 'math' in used_categories:
self._emit_math () self._emit_math()
if 'dict' in used_categories: if 'dict' in used_categories:
self._emit_dict () self._emit_dict()
if 'misc' in used_categories or 'time' in used_categories: if 'misc' in used_categories or 'time' in used_categories:
self._emit_misc () self._emit_misc()
if 'test' in used_categories: if 'test' in used_categories:
self._emit_test () self._emit_test()
self.emit ("# === End Standard Library ===") self.emit("# === End Standard Library ===")
self.emit () self.emit()
def _emit_core (self): def _emit_core(self):
"""Core functions: print, range, len.""" """Core functions: print, range, len."""
self.emit ("exec 3>&1 # Save stdout to FD3 for print()") self.emit("exec 3>&1 # Save stdout to FD3 for print()")
self.emit () self.emit()
self.emit ("__ct_print () {") self.emit("__ct_print () {")
self.indent_level += 1 with self.indented():
self.emit ('local msg="$1"') self.emit('local msg="$1"')
self.emit ('echo -e "$msg" >&3') self.emit('echo -e "$msg" >&3')
self.indent_level -= 1 self.emit("}")
self.emit ("}") self.emit()
self.emit ()
self.emit("__ct_range () {")
self.emit ("__ct_range () {") with self.indented():
self.indent_level += 1 self.emit('local start=0 end step=1')
self.emit ('local start=0 end step=1') self.emit('case $# in')
self.emit ('case $# in') with self.indented():
self.indent_level += 1 self.emit('1) end=$1 ;;')
self.emit ('1) end=$1 ;;') self.emit('2) start=$1; end=$2 ;;')
self.emit ('2) start=$1; end=$2 ;;') self.emit('3) start=$1; end=$2; step=$3 ;;')
self.emit ('3) start=$1; end=$2; step=$3 ;;') self.emit('esac')
self.indent_level -= 1 self.emit('seq "$start" "$step" "$((end - 1))"')
self.emit ('esac') self.emit("}")
self.emit ('seq "$start" "$step" "$((end - 1))"') self.emit()
self.indent_level -= 1
self.emit ("}") self.emit("__ct_len () {")
self.emit () with self.indented():
self.emit('local -n arr=$1')
self.emit ("__ct_len () {") self.emit('echo "${#arr[@]}"')
self.indent_level += 1 self.emit("}")
self.emit ('local -n arr=$1') self.emit()
self.emit ('echo "${#arr[@]}"')
self.indent_level -= 1 def _emit_http(self):
self.emit ("}")
self.emit ()
def _emit_http (self):
"""HTTP functions: get, post, put, delete.""" """HTTP functions: get, post, put, delete."""
self.emit ("__ct_http_get () {") http_funcs = [
self.indent_level += 1 ("get", ['url', 'timeout="${2:-30}"'],
self.emit ('local url="$1"') 'curl -sS --fail --show-error --max-time "$timeout" "$url"'),
self.emit ('local timeout="${2:-30}"') ("post", ['url', 'data', 'timeout="${3:-30}"'],
self.emit ('curl -sS --fail --show-error --max-time "$timeout" "$url"') 'curl -sS --fail --show-error --max-time "$timeout" -X POST -H "Content-Type: application/json" -d "$data" "$url"'),
self.indent_level -= 1 ("put", ['url', 'data', 'timeout="${3:-30}"'],
self.emit ("}") 'curl -sS --fail --show-error --max-time "$timeout" -X PUT -H "Content-Type: application/json" -d "$data" "$url"'),
self.emit () ("delete", ['url', 'timeout="${2:-30}"'],
'curl -sS --fail --show-error --max-time "$timeout" -X DELETE "$url"'),
self.emit ("__ct_http_post () {") ]
self.indent_level += 1 for name, params, body in http_funcs:
self.emit ('local url="$1"') self.emit(f"__ct_http_{name} () {{")
self.emit ('local data="$2"') with self.indented():
self.emit ('local timeout="${3:-30}"') for i, p in enumerate(params):
self.emit ('curl -sS --fail --show-error --max-time "$timeout" -X POST -H "Content-Type: application/json" -d "$data" "$url"') if '=' in p:
self.indent_level -= 1 self.emit(f'local {p}')
self.emit ("}") else:
self.emit () self.emit(f'local {p}="${{{i+1}}}"')
self.emit(body)
self.emit ("__ct_http_put () {") self.emit("}")
self.indent_level += 1 self.emit()
self.emit ('local url="$1"')
self.emit ('local data="$2"') def _emit_fs(self):
self.emit ('local timeout="${3:-30}"') """Filesystem functions."""
self.emit ('curl -sS --fail --show-error --max-time "$timeout" -X PUT -H "Content-Type: application/json" -d "$data" "$url"') fs_simple = [
self.indent_level -= 1 ("read", 'cat "$1"'),
self.emit ("}") ("write", 'echo -n "$2" > "$1"'),
self.emit () ("append", 'echo -n "$2" >> "$1"'),
("exists", '[[ -e "$1" ]] && echo "true" || echo "false"'),
self.emit ("__ct_http_delete () {") ("remove", 'rm -f "$1"'),
self.indent_level += 1 ("mkdir", 'mkdir -p "$1"'),
self.emit ('local url="$1"') ("list", 'ls -1 "$1" 2>/dev/null || true'),
self.emit ('local timeout="${2:-30}"') ]
self.emit ('curl -sS --fail --show-error --max-time "$timeout" -X DELETE "$url"') for name, body in fs_simple:
self.indent_level -= 1 self.emit(f"__ct_fs_{name} () {{")
self.emit ("}") with self.indented():
self.emit () self.emit(body)
self.emit("}")
def _emit_fs (self): self.emit()
"""Filesystem functions: read, write, append, exists, remove, mkdir, list."""
self.emit ("__ct_fs_read () {")
self.indent_level += 1
self.emit ('cat "$1"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_write () {")
self.indent_level += 1
self.emit ('echo -n "$2" > "$1"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_append () {")
self.indent_level += 1
self.emit ('echo -n "$2" >> "$1"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_exists () {")
self.indent_level += 1
self.emit ('[[ -e "$1" ]] && echo "true" || echo "false"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_remove () {")
self.indent_level += 1
self.emit ('rm -f "$1"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_mkdir () {")
self.indent_level += 1
self.emit ('mkdir -p "$1"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_fs_list () {")
self.indent_level += 1
self.emit ('ls -1 "$1" 2>/dev/null || true')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self._emit_file_handles() self._emit_file_handles()
def _emit_file_handles(self): def _emit_file_handles(self):
"""File handle functions for fs.open() / f.close().""" """File handle functions for fs.open() / f.close()."""
self.emit ("# File handle system") self.emit("# File handle system")
self.emit ("declare -gA __ct_file_handles=()") self.emit("declare -gA __ct_file_handles=()")
self.emit ("declare -g __ct_file_fd=10") self.emit("declare -g __ct_file_fd=10")
self.emit () self.emit()
self.emit ("__ct_fs_open () {") self.emit("__ct_fs_open () {")
self.indent_level += 1 with self.indented():
self.emit ('local path="$1"') self.emit('local path="$1"')
self.emit ('local mode="${2:-r}"') self.emit('local mode="${2:-r}"')
self.emit ('local fd=$__ct_file_fd') self.emit('local fd=$__ct_file_fd')
self.emit ('__ct_file_fd=$((fd + 1))') self.emit('__ct_file_fd=$((fd + 1))')
self.emit ('local h="__fh_${fd}"') self.emit('local h="__fh_${fd}"')
self.emit ('local __key_path="${h}_path"') for key in ['path', 'fd', 'mode']:
self.emit ('local __key_fd="${h}_fd"') self.emit(f'__ct_file_handles["${{h}}_{key}"]="${{{key}}}"')
self.emit ('local __key_mode="${h}_mode"') self.emit('__CT_RET="$h"')
self.emit ('__ct_file_handles[$__key_path]="$path"') self.emit('echo "$h"')
self.emit ('__ct_file_handles[$__key_fd]="$fd"') self.emit("}")
self.emit ('__ct_file_handles[$__key_mode]="$mode"') self.emit()
self.emit ('__CT_RET="$h"')
self.emit ('echo "$h"') # File handle methods - using FILE_HANDLE_METHODS for names
self.indent_level -= 1 fh_impls = {
self.emit ("}") "read": [
self.emit () 'local h="$1"',
'local path="${__ct_file_handles[${h}_path]}"',
self.emit ("__ct_fh_read () {") '__CT_RET=$(cat "$path")',
self.indent_level += 1 'echo "$__CT_RET"',
self.emit ('local h="$1"') ],
self.emit ('local __key="${h}_path"') "readline": [
self.emit ('local path="${__ct_file_handles[$__key]}"') 'local h="$1"',
self.emit ('__CT_RET=$(cat "$path")') 'local path="${__ct_file_handles[${h}_path]}"',
self.emit ('echo "$__CT_RET"') 'local pos="${__ct_file_handles[${h}_pos]:-1}"',
self.indent_level -= 1 '__CT_RET=$(sed -n "${pos}p" "$path")',
self.emit ("}") '__ct_file_handles[${h}_pos]=$((pos + 1))',
self.emit () 'echo "$__CT_RET"',
],
self.emit ("__ct_fh_readline () {") "write": [
self.indent_level += 1 'local h="$1" data="$2"',
self.emit ('local h="$1"') 'local path="${__ct_file_handles[${h}_path]}"',
self.emit ('local __key_path="${h}_path"') 'local mode="${__ct_file_handles[${h}_mode]}"',
self.emit ('local __key_pos="${h}_pos"') '[[ "$mode" == "a" ]] && echo -n "$data" >> "$path" || echo -n "$data" > "$path"',
self.emit ('local path="${__ct_file_handles[$__key_path]}"') ],
self.emit ('local pos="${__ct_file_handles[$__key_pos]:-1}"') "writeln": [
self.emit ('__CT_RET=$(sed -n "${pos}p" "$path")') 'local h="$1" data="$2"',
self.emit ('__ct_file_handles[$__key_pos]=$((pos + 1))') 'local path="${__ct_file_handles[${h}_path]}"',
self.emit ('echo "$__CT_RET"') 'local mode="${__ct_file_handles[${h}_mode]}"',
self.indent_level -= 1 '[[ "$mode" == "a" ]] && echo "$data" >> "$path" || echo "$data" > "$path"',
self.emit ("}") ],
self.emit () "close": [
'local h="$1"',
self.emit ("__ct_fh_write () {") 'for k in path fd mode pos; do unset "__ct_file_handles[${h}_$k]"; done',
self.indent_level += 1 ],
self.emit ('local h="$1"') }
self.emit ('local data="$2"')
self.emit ('local __key_path="${h}_path"') for method_name, method_def in FILE_HANDLE_METHODS.items():
self.emit ('local __key_mode="${h}_mode"') if method_name in fh_impls:
self.emit ('local path="${__ct_file_handles[$__key_path]}"') self.emit(f"{method_def.bash_func} () {{")
self.emit ('local mode="${__ct_file_handles[$__key_mode]}"') with self.indented():
self.emit ('if [[ "$mode" == "a" ]]; then') for line in fh_impls[method_name]:
self.indent_level += 1 self.emit(line)
self.emit ('echo -n "$data" >> "$path"') self.emit("}")
self.indent_level -= 1 self.emit()
self.emit ('else')
self.indent_level += 1 self.emit("# Context manager support")
self.emit ('echo -n "$data" > "$path"') self.emit('__ct_fh___enter__ () { echo "$1"; }')
self.indent_level -= 1 self.emit('__ct_fh___exit__ () { __ct_fh_close "$1"; }')
self.emit ('fi') self.emit()
self.indent_level -= 1
self.emit ("}") def _emit_json(self):
self.emit () """JSON functions."""
self.emit("__ct_json_parse () {")
self.emit ("__ct_fh_writeln () {") with self.indented():
self.indent_level += 1 self.emit('local __json="$1" __dict_name="$2"')
self.emit ('local h="$1"') self.emit('eval "declare -gA $__dict_name=()"')
self.emit ('local data="$2"') self.emit('while IFS= read -r __line; do')
self.emit ('local __key_path="${h}_path"') with self.indented():
self.emit ('local __key_mode="${h}_mode"') self.emit('local __key="${__line%%=*}" __val="${__line#*=}"')
self.emit ('local path="${__ct_file_handles[$__key_path]}"') self.emit('[[ -n "$__key" ]] && eval "${__dict_name}[\\"$__key\\"]=\"\\$__val\""')
self.emit ('local mode="${__ct_file_handles[$__key_mode]}"') self.emit("done < <(echo \"$__json\" | jq -r 'to_entries[] | \"\\(.key)=\\(.value)\"' 2>/dev/null)")
self.emit ('if [[ "$mode" == "a" ]]; then') self.emit("}")
self.indent_level += 1 self.emit()
self.emit ('echo "$data" >> "$path"')
self.indent_level -= 1 self.emit("__ct_json_get () {")
self.emit ('else') with self.indented():
self.indent_level += 1 self.emit('echo "$1" | jq -r "$2" 2>/dev/null')
self.emit ('echo "$data" > "$path"') self.emit("}")
self.indent_level -= 1 self.emit()
self.emit ('fi')
self.indent_level -= 1 self.emit("__ct_json_stringify () {")
self.emit ("}") with self.indented():
self.emit () self.emit('local -n __d="$1"')
self.emit('local __first=1')
self.emit ("__ct_fh_close () {") self.emit('printf "{"')
self.indent_level += 1 self.emit('for __k in "${!__d[@]}"; do')
self.emit ('local h="$1"') with self.indented():
self.emit ('local __key_path="${h}_path"') self.emit('[[ $__first -eq 1 ]] && __first=0 || printf ","')
self.emit ('local __key_fd="${h}_fd"') self.emit('printf "\\"%s\\":" "$__k"')
self.emit ('local __key_mode="${h}_mode"') self.emit('local __v="${__d[$__k]}"')
self.emit ('local __key_pos="${h}_pos"') self.emit('if [[ "$__v" =~ ^-?[0-9]+$ ]] || [[ "$__v" =~ ^(true|false|null)$ ]]; then')
self.emit ('unset "__ct_file_handles[$__key_path]"') with self.indented():
self.emit ('unset "__ct_file_handles[$__key_fd]"') self.emit('printf "%s" "$__v"')
self.emit ('unset "__ct_file_handles[$__key_mode]"') self.emit('else')
self.emit ('unset "__ct_file_handles[$__key_pos]"') with self.indented():
self.indent_level -= 1 self.emit('printf "\\"%s\\"" "$__v"')
self.emit ("}") self.emit('fi')
self.emit () self.emit('done')
self.emit('printf "}\\n"')
self.emit ("# Context manager support for file handles") self.emit("}")
self.emit ('__ct_fh___enter__ () { echo "$1"; }') self.emit()
self.emit ('__ct_fh___exit__ () { __ct_fh_close "$1"; }')
self.emit () def _emit_object_system(self):
"""Object system: class tracking, method dispatch."""
def _emit_json (self): self.emit("declare -gA __ct_obj_class=()")
"""JSON functions: parse, stringify.""" self.emit("declare -gA __CT_OBJ=()")
self.emit ("__ct_json_parse () {") self.emit("declare -g __ct_last_instance=''")
self.indent_level += 1 self.emit()
self.emit ('local __json="$1"')
self.emit ('local __dict_name="$2"') self.emit("__ct_call_method () {")
self.emit ('eval "declare -gA $__dict_name=()"') with self.indented():
self.emit ('while IFS= read -r __line; do') self.emit('local __obj="$1" __method="$2"')
self.indent_level += 1 self.emit('shift 2')
self.emit ('local __key="${__line%%=*}"') self.emit('local __class="${__ct_obj_class[$__obj]:-}"')
self.emit ('local __val="${__line#*=}"') self.emit('[[ -z "$__class" ]] && { echo "Error: Unknown object $__obj" >&2; return 1; }')
self.emit ('[[ -n "$__key" ]] && eval "${__dict_name}[\\"$__key\\"]=\"\\$__val\""') self.emit('"__ct_class_${__class}_${__method}" "$__obj" "$@"')
self.indent_level -= 1 self.emit("}")
self.emit ('done < <(echo "$__json" | jq -r \'to_entries[] | "\\(.key)=\\(.value)\"\' 2>/dev/null)') self.emit()
self.indent_level -= 1
self.emit ("}") self.emit('__ct_get_field () { echo "${__CT_OBJ[$1.$2]}"; }')
self.emit () self.emit()
self.emit("declare -g __CT_RET=''")
self.emit ("__ct_json_get () {") self.emit()
self.indent_level += 1
self.emit ('local __json="$1"') def _emit_exception(self):
self.emit ('local __path="$2"')
self.emit ('echo "$__json" | jq -r "$__path" 2>/dev/null')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_json_stringify () {")
self.indent_level += 1
self.emit ('local -n __d="$1"')
self.emit ('local __first=1')
self.emit ('printf "{"')
self.emit ('for __k in "${!__d[@]}"; do')
self.indent_level += 1
self.emit ('[[ $__first -eq 1 ]] && __first=0 || printf ","')
self.emit ('printf "\\"%s\\":" "$__k"')
self.emit ('local __v="${__d[$__k]}"')
self.emit ('if [[ "$__v" =~ ^-?[0-9]+$ ]]; then')
self.indent_level += 1
self.emit ('printf "%s" "$__v"')
self.indent_level -= 1
self.emit ('elif [[ "$__v" == "true" || "$__v" == "false" || "$__v" == "null" ]]; then')
self.indent_level += 1
self.emit ('printf "%s" "$__v"')
self.indent_level -= 1
self.emit ('else')
self.indent_level += 1
self.emit ('printf "\\"%s\\"" "$__v"')
self.indent_level -= 1
self.emit ('fi')
self.indent_level -= 1
self.emit ('done')
self.emit ('printf "}\\n"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
def _emit_object_system (self):
"""Object system: class tracking, method dispatch, field access."""
self.emit ("declare -gA __ct_obj_class=()")
self.emit ("declare -gA __CT_OBJ=()")
self.emit ("declare -g __ct_last_instance=''")
self.emit ()
self.emit ("__ct_call_method () {")
self.indent_level += 1
self.emit ('local __obj="$1"')
self.emit ('local __method="$2"')
self.emit ('shift 2')
self.emit ('local __class="${__ct_obj_class[$__obj]:-}"')
self.emit ('if [[ -z "$__class" ]]; then')
self.indent_level += 1
self.emit ('echo "Error: Unknown object $__obj" >&2')
self.emit ('return 1')
self.indent_level -= 1
self.emit ('fi')
self.emit ('"__ct_class_${__class}_${__method}" "$__obj" "$@"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_get_field () {")
self.indent_level += 1
self.emit ('echo "${__CT_OBJ[$1.$2]}"')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("declare -g __CT_RET=''")
self.emit ()
def _emit_exception (self):
"""Exception handling.""" """Exception handling."""
self.emit ("declare -g __ct_exception=''") self.emit("declare -g __ct_exception=''")
self.emit ("declare -g __ct_exception_type=''") self.emit("declare -g __ct_exception_type=''")
self.emit () self.emit()
self.emit("__ct_throw () {")
self.emit ("__ct_throw () {") with self.indented():
self.indent_level += 1 self.emit('__ct_exception_type="$1"')
self.emit ('__ct_exception_type="$1"') self.emit('__ct_exception="$2"')
self.emit ('__ct_exception="$2"') self.emit('return 1')
self.emit ('return 1') self.emit("}")
self.indent_level -= 1 self.emit()
self.emit ("}")
self.emit () def _emit_logger(self):
"""Logger functions."""
def _emit_logger (self): levels = [("info", "INFO", ""), ("warn", "WARN", " >&2"),
"""Logger functions: info, warn, error, debug.""" ("error", "ERROR", " >&2"), ("debug", "DEBUG", "")]
self.emit ("__ct_logger_info () {") for name, label, redir in levels:
self.indent_level += 1 self.emit(f'__ct_logger_{name} () {{ echo "[{label}] $1"{redir}; }}')
self.emit ('echo "[INFO] $1"') self.emit()
self.indent_level -= 1
self.emit ("}") def _emit_string(self):
self.emit () """String functions from STRING_METHODS."""
self.emit("# String functions")
self.emit ("__ct_logger_warn () {") for method_name, method_def in STRING_METHODS.items():
self.indent_level += 1 if method_name in STRING_IMPLS:
self.emit ('echo "[WARN] $1" >&2') self.emit(f"{method_def.bash_func} () {{ {STRING_IMPLS[method_name]}; }}")
self.indent_level -= 1 self.emit("__ct_str_concat () { __CT_RET=\"$1$2\"; echo \"$__CT_RET\"; }")
self.emit ("}") self.emit()
self.emit () self.emit("# Character code functions")
self.emit("__ct_str_ord () { __CT_RET=$(printf '%d' \"'$1\"); echo \"$__CT_RET\"; }")
self.emit ("__ct_logger_error () {") self.emit("__ct_str_chr () { printf -v __CT_RET '%b' \"\\\\x$(printf '%02x' \"$1\")\"; echo \"$__CT_RET\"; }")
self.indent_level += 1 self.emit()
self.emit ('echo "[ERROR] $1" >&2')
self.indent_level -= 1 def _emit_array(self):
self.emit ("}") """Array functions from ARRAY_METHODS."""
self.emit () self.emit("# Array functions")
for method_name, method_def in ARRAY_METHODS.items():
self.emit ("__ct_logger_debug () {") if method_name in ARRAY_IMPLS:
self.indent_level += 1 self.emit(f"{method_def.bash_func} () {{ {ARRAY_IMPLS[method_name]}; }}")
self.emit ('echo "[DEBUG] $1"') self.emit()
self.indent_level -= 1
self.emit ("}") self.emit("# Array map/filter with lambda functions")
self.emit () self.emit("__ct_arr_map () {")
with self.indented():
def _emit_string (self): self.emit('local -n __a=$1')
"""String functions.""" self.emit('local __fn=$2')
self.emit ("# String functions") self.emit('local -a __result=()')
self.emit ("__ct_str_len () { __CT_RET=${#1}; echo \"$__CT_RET\"; }") self.emit('for __item in "${__a[@]}"; do __result+=("$($__fn "$__item")"); done')
self.emit ("__ct_str_substr () { __CT_RET=\"${1:$2:$3}\"; echo \"$__CT_RET\"; }") self.emit('__CT_RET=("${__result[@]}")')
self.emit ("__ct_str_index () { local i=\"${1%%$2*}\"; [[ \"$i\" == \"$1\" ]] && __CT_RET=-1 || __CT_RET=${#i}; echo \"$__CT_RET\"; }") self.emit("}")
self.emit ("__ct_str_contains () { [[ \"$1\" == *\"$2\"* ]] && __CT_RET=true || __CT_RET=false; echo \"$__CT_RET\"; }") self.emit()
self.emit ("__ct_str_starts () { [[ \"$1\" == \"$2\"* ]] && __CT_RET=true || __CT_RET=false; echo \"$__CT_RET\"; }")
self.emit ("__ct_str_ends () { [[ \"$1\" == *\"$2\" ]] && __CT_RET=true || __CT_RET=false; echo \"$__CT_RET\"; }") self.emit("__ct_arr_filter () {")
self.emit ("__ct_str_replace () { __CT_RET=\"${1//\"$2\"/\"$3\"}\"; echo \"$__CT_RET\"; }") with self.indented():
self.emit ("__ct_str_split () { local IFS=\"$2\"; read -ra __CT_RET_ARR <<< \"$1\"; }") self.emit('local -n __a=$1')
self.emit ("__ct_str_trim () { local s=\"$1\"; s=\"${s#\"${s%%[![:space:]]*}\"}\"; __CT_RET=\"${s%\"${s##*[![:space:]]}\"}\" ; echo \"$__CT_RET\"; }") self.emit('local __fn=$2')
self.emit ("__ct_str_upper () { __CT_RET=\"${1^^}\"; echo \"$__CT_RET\"; }") self.emit('local -a __result=()')
self.emit ("__ct_str_lower () { __CT_RET=\"${1,,}\"; echo \"$__CT_RET\"; }") self.emit('for __item in "${__a[@]}"; do')
self.emit ("__ct_str_char_at () { __CT_RET=\"${1:$2:1}\"; printf '%sX' \"$__CT_RET\"; }") with self.indented():
self.emit ("__ct_str_urlencode () { __CT_RET=$(printf '%s' \"$1\" | jq -sRr @uri); echo \"$__CT_RET\"; }") self.emit('[[ "$($__fn "$__item")" == "true" ]] && __result+=("$__item")')
self.emit ("__ct_str_concat () { __CT_RET=\"$1$2\"; echo \"$__CT_RET\"; }") self.emit('done')
self.emit () self.emit('__CT_RET=("${__result[@]}")')
self.emit("}")
self.emit ("# Character code functions") self.emit()
self.emit ("__ct_str_ord () { __CT_RET=$(printf '%d' \"'$1\"); echo \"$__CT_RET\"; }")
self.emit ("__ct_str_chr () { printf -v __CT_RET '%b' \"\\\\x$(printf '%02x' \"$1\")\"; echo \"$__CT_RET\"; }") self.emit("# Fast array functions (no echo)")
self.emit () self.emit("__ct_arr_len_fast () { local -n __a=$1; __CT_RET=${#__a[@]}; }")
self.emit("__ct_arr_get_fast () { local -n __a=$1; __CT_RET=\"${__a[$2]}\"; }")
def _emit_array (self): self.emit()
"""Array functions."""
self.emit ("# Array functions (optimized - set __CT_RET and echo)") def _emit_regex(self):
self.emit ("__ct_arr_push () { local -n __a=$1; shift; __a+=(\"$@\"); }")
self.emit ("__ct_arr_pop () { local -n __a=$1; unset '__a[-1]'; }")
self.emit ("__ct_arr_shift () { local -n __a=$1; __CT_RET=\"${__a[0]}\"; __a=(\"${__a[@]:1}\"); echo \"$__CT_RET\"; }")
self.emit ("__ct_arr_join () { local -n __a=$1; local sep; printf -v sep '%b' \"$2\"; local IFS=\"$sep\"; __CT_RET=\"${__a[*]}\"; echo \"$__CT_RET\"; }")
self.emit ("__ct_arr_len () { local -n __a=$1; __CT_RET=${#__a[@]}; echo \"$__CT_RET\"; }")
self.emit ("__ct_arr_get () { local -n __a=$1; __CT_RET=\"${__a[$2]}\"; echo \"$__CT_RET\"; }")
self.emit ("__ct_arr_set () { local -n __a=$1; __a[$2]=\"$3\"; }")
self.emit ("__ct_arr_slice () { local -n __a=$1; __CT_RET_ARR=(\"${__a[@]:$2:$3}\"); }")
self.emit ()
self.emit ("# Array map/filter with lambda functions")
self.emit ("__ct_arr_map () {")
self.indent_level += 1
self.emit ('local -n __a=$1')
self.emit ('local __fn=$2')
self.emit ('local -a __result=()')
self.emit ('for __item in "${__a[@]}"; do')
self.indent_level += 1
self.emit ('__result+=("$($__fn "$__item")")')
self.indent_level -= 1
self.emit ('done')
self.emit ('__CT_RET=("${__result[@]}")')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_arr_filter () {")
self.indent_level += 1
self.emit ('local -n __a=$1')
self.emit ('local __fn=$2')
self.emit ('local -a __result=()')
self.emit ('for __item in "${__a[@]}"; do')
self.indent_level += 1
self.emit ('if [[ "$($__fn "$__item")" == "true" ]]; then')
self.indent_level += 1
self.emit ('__result+=("$__item")')
self.indent_level -= 1
self.emit ('fi')
self.indent_level -= 1
self.emit ('done')
self.emit ('__CT_RET=("${__result[@]}")')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("# Fast array functions (no echo - use __CT_RET directly)")
self.emit ("__ct_arr_len_fast () { local -n __a=$1; __CT_RET=${#__a[@]}; }")
self.emit ("__ct_arr_get_fast () { local -n __a=$1; __CT_RET=\"${__a[$2]}\"; }")
self.emit ()
def _emit_regex (self):
"""Regex functions.""" """Regex functions."""
self.emit ("# ngrep - pattern search") self.emit("# Regex functions")
self.emit ("__ct_ngrep () {") self.emit("__ct_ngrep () { echo \"$2\" | grep -n \"$1\" || true; }")
self.indent_level += 1 self.emit('__ct_regex_match () { [[ "$1" =~ $2 ]] && echo true || echo false; }')
self.emit ('local pattern="$1"') self.emit("__ct_regex_extract () {")
self.emit ('local text="$2"') with self.indented():
self.emit ('echo "$text" | grep -n "$pattern" || true') self.emit('[[ "$1" =~ $2 ]] && echo "${BASH_REMATCH[0]}"')
self.indent_level -= 1 self.emit("}")
self.emit ("}") self.emit()
self.emit ()
def _emit_utils(self):
self.emit ("__ct_regex_match () {")
self.indent_level += 1
self.emit ('local str="$1"')
self.emit ('local pattern="$2"')
self.emit ('[[ "$str" =~ $pattern ]] && echo true || echo false')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_regex_extract () {")
self.indent_level += 1
self.emit ('local str="$1"')
self.emit ('local pattern="$2"')
self.emit ('if [[ "$str" =~ $pattern ]]; then')
self.indent_level += 1
self.emit ('echo "${BASH_REMATCH[0]}"')
self.indent_level -= 1
self.emit ('fi')
self.indent_level -= 1
self.emit ("}")
self.emit ()
def _emit_utils (self):
"""Utility functions.""" """Utility functions."""
self.emit ("__ct_exit () { exit \"${1:-0}\"; }") self.emit('__ct_exit () { exit "${1:-0}"; }')
self.emit () self.emit()
self.emit('__ct_is_number () { [[ "$1" =~ ^-?[0-9]+$ ]] && echo true || echo false; }')
self.emit ("__ct_is_number () { [[ \"$1\" =~ ^-?[0-9]+$ ]] && echo true || echo false; }") self.emit('__ct_is_empty () { [[ -z "$1" ]] && echo true || echo false; }')
self.emit ("__ct_is_empty () { [[ -z \"$1\" ]] && echo true || echo false; }") self.emit()
self.emit () self.emit('__ct_args=("$@")')
self.emit('__ct_args_count () { echo ${#__ct_args[@]}; }')
self.emit ('__ct_args=("$@")') self.emit("__ct_args_get () { printf '%s\\n' \"${__ct_args[$1]}\"; }")
self.emit ('__ct_args_count () { echo ${#__ct_args[@]}; }') self.emit()
self.emit ("__ct_args_get () { printf '%s\\n' \"${__ct_args[$1]}\"; }")
self.emit () def _emit_utils_minimal(self):
def _emit_utils_minimal (self):
"""Minimal utility functions (without args).""" """Minimal utility functions (without args)."""
self.emit ("__ct_exit () { exit \"${1:-0}\"; }") self.emit('__ct_exit () { exit "${1:-0}"; }')
self.emit () self.emit()
self.emit('__ct_is_number () { [[ "$1" =~ ^-?[0-9]+$ ]] && echo true || echo false; }')
self.emit ("__ct_is_number () { [[ \"$1\" =~ ^-?[0-9]+$ ]] && echo true || echo false; }") self.emit('__ct_is_empty () { [[ -z "$1" ]] && echo true || echo false; }')
self.emit ("__ct_is_empty () { [[ -z \"$1\" ]] && echo true || echo false; }") self.emit()
self.emit ()
def _emit_args (self): def _emit_args(self):
"""Args functions only.""" """Args functions only."""
self.emit ("__ct_exit () { exit \"${1:-0}\"; }") self._emit_utils()
self.emit ()
self.emit ("__ct_is_number () { [[ \"$1\" =~ ^-?[0-9]+$ ]] && echo true || echo false; }")
self.emit ("__ct_is_empty () { [[ -z \"$1\" ]] && echo true || echo false; }")
self.emit ()
self.emit ('__ct_args=("$@")')
self.emit ('__ct_args_count () { echo ${#__ct_args[@]}; }')
self.emit ("__ct_args_get () { printf '%s\\n' \"${__ct_args[$1]}\"; }")
self.emit ()
def _emit_awk_wrapper (self): def _emit_awk_wrapper(self):
"""AWK wrapper functions.""" """AWK wrapper functions."""
self.emit ("# AWK wrapper - uses mawk if available (2-10x faster)") self.emit("# AWK wrapper - uses mawk if available (2-10x faster)")
self.emit ("__ct_awk_cmd=''") self.emit("__ct_awk_cmd=''")
self.emit ("if command -v mawk &>/dev/null; then __ct_awk_cmd=mawk") self.emit("if command -v mawk &>/dev/null; then __ct_awk_cmd=mawk")
self.emit ("elif command -v gawk &>/dev/null; then __ct_awk_cmd=gawk; echo '[WARN] mawk not found, using gawk (slower)' >&2") self.emit("elif command -v gawk &>/dev/null; then __ct_awk_cmd=gawk; echo '[WARN] mawk not found, using gawk (slower)' >&2")
self.emit ("elif command -v awk &>/dev/null; then __ct_awk_cmd=awk; echo '[WARN] mawk not found, using awk (slower)' >&2") self.emit("elif command -v awk &>/dev/null; then __ct_awk_cmd=awk; echo '[WARN] mawk not found, using awk (slower)' >&2")
self.emit ("else echo '[ERROR] No awk implementation found' >&2; exit 1; fi") self.emit("else echo '[ERROR] No awk implementation found' >&2; exit 1; fi")
self.emit ('__ct_awk () { "$__ct_awk_cmd" "$@"; }') self.emit('__ct_awk () { "$__ct_awk_cmd" "$@"; }')
self.emit ('__ct_awk_file () { "$__ct_awk_cmd" -f "$1" "$2"; }') self.emit('__ct_awk_file () { "$__ct_awk_cmd" -f "$1" "$2"; }')
self.emit () self.emit()
def _emit_math (self): def _emit_math(self):
"""Math functions.""" """Math functions."""
self.emit ("# Math functions (fast - no external commands)") self.emit("# Math functions")
self.emit ("__ct_math_add () { echo $(($1 + $2)); }") math_ops = [
self.emit ("__ct_math_sub () { echo $(($1 - $2)); }") ("add", "+"), ("sub", "-"), ("mul", "*"), ("div", "/"), ("mod", "%")
self.emit ("__ct_math_mul () { echo $(($1 * $2)); }") ]
self.emit ("__ct_math_div () { echo $(($1 / $2)); }") for name, op in math_ops:
self.emit ("__ct_math_mod () { echo $(($1 % $2)); }") self.emit(f"__ct_math_{name} () {{ echo $(($1 {op} $2)); }}")
self.emit ("__ct_math_min () { (($1 < $2)) && echo $1 || echo $2; }") self.emit("__ct_math_min () { (($1 < $2)) && echo $1 || echo $2; }")
self.emit ("__ct_math_max () { (($1 > $2)) && echo $1 || echo $2; }") self.emit("__ct_math_max () { (($1 > $2)) && echo $1 || echo $2; }")
self.emit ("__ct_math_abs () { local n=$1; echo ${n#-}; }") self.emit("__ct_math_abs () { local n=$1; echo ${n#-}; }")
self.emit () self.emit()
def _emit_dict (self): def _emit_dict(self):
"""Dict/hash functions.""" """Dict functions from DICT_METHODS."""
self.emit ("# Dict/hash functions (using associative arrays)") self.emit("# Dict functions")
self.emit ('__ct_dict_set () { local -n __d="$1"; __d["$2"]="$3"; }') for method_name, method_def in DICT_METHODS.items():
self.emit ('__ct_dict_get () { local -n __d="$1"; __CT_RET="${__d[$2]}"; echo "$__CT_RET"; }') if method_name in DICT_IMPLS:
self.emit ('__ct_dict_has () { local -n __d="$1"; [[ -v "__d[$2]" ]] && __CT_RET=true || __CT_RET=false; echo "$__CT_RET"; }') self.emit(f"{method_def.bash_func} () {{ {DICT_IMPLS[method_name]}; }}")
self.emit ('__ct_dict_del () { local -n __d="$1"; unset "__d[$2]"; }') # len is not in DICT_METHODS, add separately
self.emit ('__ct_dict_keys () { local -n __d="$1"; __CT_RET_ARR=("${!__d[@]}"); }') self.emit(f"__ct_dict_len () {{ {DICT_IMPLS['len']}; }}")
self.emit ('__ct_dict_len () { local -n __d="$1"; __CT_RET=${#__d[@]}; echo "$__CT_RET"; }') self.emit()
self.emit ()
def _emit_misc(self):
def _emit_misc (self):
"""Miscellaneous functions.""" """Miscellaneous functions."""
self.emit ("# Byte/binary functions") self.emit("# Byte/binary functions")
self.emit ("__ct_byte_to_hex () { printf '%02x' \"$1\"; }") self.emit("__ct_byte_to_hex () { printf '%02x' \"$1\"; }")
self.emit ("__ct_hex_to_byte () { printf '%d' \"0x$1\"; }") self.emit("__ct_hex_to_byte () { printf '%d' \"0x$1\"; }")
self.emit () self.emit()
self.emit("# Time functions")
self.emit ("# Time functions") self.emit("__ct_time_now () { date +%s; }")
self.emit ("__ct_time_now () { date +%s; }") self.emit("__ct_time_ms () { date +%s%3N; }")
self.emit ("__ct_time_ms () { date +%s%3N; }") self.emit()
self.emit () self.emit("# Random")
self.emit("__ct_random () { echo $RANDOM; }")
self.emit ("# Random") self.emit("__ct_random_range () { echo $(($1 + RANDOM % ($2 - $1 + 1))); }")
self.emit ("__ct_random () { echo $RANDOM; }") self.emit()
self.emit ("__ct_random_range () { echo $(($1 + RANDOM % ($2 - $1 + 1))); }") self.emit("__CT_NL=$'\\n'")
self.emit () self.emit()
self.emit ("__CT_NL=$'\\n'") def _emit_test(self):
self.emit () """Test framework functions."""
self.emit("# Test framework")
def _emit_test (self): self.emit("declare -g __ct_test_passed=0")
"""Test framework functions: assert, test runner.""" self.emit("declare -g __ct_test_failed=0")
self.emit ("# Test framework") self.emit("declare -g __ct_test_current=''")
self.emit ("declare -g __ct_test_passed=0") self.emit("declare -g __ct_test_start_time=0")
self.emit ("declare -g __ct_test_failed=0") self.emit()
self.emit ("declare -g __ct_test_current=''")
self.emit ("declare -g __ct_test_start_time=0") self.emit("__ct_test_colors () {")
self.emit () with self.indented():
self.emit('if [[ -t 1 ]]; then')
self.emit ("__ct_test_colors () {") with self.indented():
self.indent_level += 1 for c in ['GREEN=32', 'RED=31', 'YELLOW=33', 'CYAN=36']:
self.emit ('if [[ -t 1 ]]; then') name, code = c.split('=')
self.indent_level += 1 self.emit(f'__CT_{name}="\\033[{code}m"')
self.emit ('__CT_GREEN="\\033[32m"') self.emit('__CT_RESET="\\033[0m"')
self.emit ('__CT_RED="\\033[31m"') self.emit('else')
self.emit ('__CT_YELLOW="\\033[33m"') with self.indented():
self.emit ('__CT_CYAN="\\033[36m"') self.emit('__CT_GREEN="" __CT_RED="" __CT_YELLOW="" __CT_CYAN="" __CT_RESET=""')
self.emit ('__CT_RESET="\\033[0m"') self.emit('fi')
self.indent_level -= 1 self.emit("}")
self.emit ('else') self.emit("__ct_test_colors")
self.indent_level += 1 self.emit()
self.emit ('__CT_GREEN="" __CT_RED="" __CT_YELLOW="" __CT_CYAN="" __CT_RESET=""')
self.indent_level -= 1 self.emit("__ct_test_start () {")
self.emit ('fi') with self.indented():
self.indent_level -= 1 self.emit('__ct_test_current="$1"')
self.emit ("}") self.emit('__ct_test_start_time=$(date +%s%3N)')
self.emit ("__ct_test_colors") self.emit('printf "${__CT_CYAN}RUN${__CT_RESET} %s\\n" "$1"')
self.emit () self.emit("}")
self.emit()
self.emit ("__ct_test_start () {")
self.indent_level += 1 self.emit("__ct_test_pass () {")
self.emit ('__ct_test_current="$1"') with self.indented():
self.emit ('__ct_test_start_time=$(date +%s%3N)') self.emit('local elapsed=$(($(date +%s%3N) - __ct_test_start_time))')
self.emit ('printf "${__CT_CYAN}RUN${__CT_RESET} %s\\n" "$1"') self.emit('printf "${__CT_GREEN}PASS${__CT_RESET} %s ${__CT_YELLOW}(%dms)${__CT_RESET}\\n" "$__ct_test_current" "$elapsed"')
self.indent_level -= 1 self.emit('((__ct_test_passed++)) || true')
self.emit ("}") self.emit("}")
self.emit () self.emit()
self.emit ("__ct_test_pass () {") self.emit("__ct_test_fail () {")
self.indent_level += 1 with self.indented():
self.emit ('local elapsed=$(($(date +%s%3N) - __ct_test_start_time))') self.emit('local msg="$1"')
self.emit ('printf "${__CT_GREEN}PASS${__CT_RESET} %s ${__CT_YELLOW}(%dms)${__CT_RESET}\\n" "$__ct_test_current" "$elapsed"') self.emit('local elapsed=$(($(date +%s%3N) - __ct_test_start_time))')
self.emit ('((__ct_test_passed++)) || true') self.emit('printf "${__CT_RED}FAIL${__CT_RESET} %s ${__CT_YELLOW}(%dms)${__CT_RESET}\\n" "$__ct_test_current" "$elapsed"')
self.indent_level -= 1 self.emit('[[ -n "$msg" ]] && printf " ${__CT_RED}%s${__CT_RESET}\\n" "$msg"')
self.emit ("}") self.emit('((__ct_test_failed++)) || true')
self.emit () self.emit("}")
self.emit()
self.emit ("__ct_test_fail () {")
self.indent_level += 1 self.emit("__ct_assert () {")
self.emit ('local msg="$1"') with self.indented():
self.emit ('local elapsed=$(($(date +%s%3N) - __ct_test_start_time))') self.emit('local condition="$1" msg="${2:-Assertion failed}"')
self.emit ('printf "${__CT_RED}FAIL${__CT_RESET} %s ${__CT_YELLOW}(%dms)${__CT_RESET}\\n" "$__ct_test_current" "$elapsed"') self.emit('if eval "$condition"; then return 0')
self.emit ('if [[ -n "$msg" ]]; then') self.emit('else __ct_test_fail "$msg"; return 1; fi')
self.indent_level += 1 self.emit("}")
self.emit ('printf " ${__CT_RED}%s${__CT_RESET}\\n" "$msg"') self.emit()
self.indent_level -= 1
self.emit ('fi') self.emit("__ct_assert_eq () {")
self.emit ('((__ct_test_failed++)) || true') with self.indented():
self.indent_level -= 1 self.emit('local actual="$1" expected="$2"')
self.emit ("}") self.emit('[[ "$actual" != "$expected" ]] && { __ct_test_fail "Expected \'$expected\' but got \'$actual\'"; return 1; }')
self.emit () self.emit("}")
self.emit()
self.emit ("__ct_assert () {")
self.indent_level += 1 self.emit("__ct_test_summary () {")
self.emit ('local condition="$1"') with self.indented():
self.emit ('local msg="${2:-Assertion failed}"') self.emit('echo')
self.emit ('if eval "$condition"; then') self.emit('local total=$((__ct_test_passed + __ct_test_failed))')
self.indent_level += 1 self.emit('if [[ $__ct_test_failed -eq 0 ]]; then')
self.emit ('return 0') with self.indented():
self.indent_level -= 1 self.emit('printf "${__CT_GREEN}All %d tests passed${__CT_RESET}\\n" "$total"')
self.emit ('else') self.emit('else')
self.indent_level += 1 with self.indented():
self.emit ('__ct_test_fail "$msg"') self.emit('printf "${__CT_RED}%d of %d tests failed${__CT_RESET}\\n" "$__ct_test_failed" "$total"')
self.emit ('return 1') self.emit('fi')
self.indent_level -= 1 self.emit('return $__ct_test_failed')
self.emit ('fi') self.emit("}")
self.indent_level -= 1 self.emit()
self.emit ("}")
self.emit ()
self.emit ("__ct_assert_eq () {")
self.indent_level += 1
self.emit ('local actual="$1"')
self.emit ('local expected="$2"')
self.emit ("if [[ \"$actual\" != \"$expected\" ]]; then")
self.indent_level += 1
self.emit ("__ct_test_fail \"Expected '$expected' but got '$actual'\"")
self.emit ('return 1')
self.indent_level -= 1
self.emit ('fi')
self.indent_level -= 1
self.emit ("}")
self.emit ()
self.emit ("__ct_test_summary () {")
self.indent_level += 1
self.emit ('echo')
self.emit ('local total=$((__ct_test_passed + __ct_test_failed))')
self.emit ('if [[ $__ct_test_failed -eq 0 ]]; then')
self.indent_level += 1
self.emit ('printf "${__CT_GREEN}All %d tests passed${__CT_RESET}\\n" "$total"')
self.indent_level -= 1
self.emit ('else')
self.indent_level += 1
self.emit ('printf "${__CT_RED}%d of %d tests failed${__CT_RESET}\\n" "$__ct_test_failed" "$total"')
self.indent_level -= 1
self.emit ('fi')
self.emit ('return $__ct_test_failed')
self.indent_level -= 1
self.emit ("}")
self.emit ()
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