diff options
-rw-r--r-- | eshld/Makefile | 3 | ||||
-rw-r--r-- | eshld/eshrt.esh | 248 | ||||
-rw-r--r-- | eshld/link.esh | 28 | ||||
-rw-r--r-- | eshld/main.esh | 2 |
4 files changed, 257 insertions, 24 deletions
diff --git a/eshld/Makefile b/eshld/Makefile index 03b3c3b..a768ca6 100644 --- a/eshld/Makefile +++ b/eshld/Makefile @@ -1,6 +1,7 @@ SOURCES = \ main.esh \ - link.esh + link.esh \ + eshrt.esh OBJECTS = $(SOURCES:.esh=.sh) all: eshld diff --git a/eshld/eshrt.esh b/eshld/eshrt.esh new file mode 100644 index 0000000..0a6fdb7 --- /dev/null +++ b/eshld/eshrt.esh @@ -0,0 +1,248 @@ +# Eggshell Linker runtime library +# +# Copyright (C) 2016 Patrick "P. J." McDermott +# +# This file is part of the Eggshell Linker. +# +# The Eggshell Linker is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# The Eggshell Linker is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty +# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with the Eggshell Linker If not, see +# <http://www.gnu.org/licenses/>. + +eshrtbegin() +{ + cat <<-'EOF' +__ESC="$(printf '\033.')"; __ESC="${__ESC%.}" + __RS="$(printf '\036.')"; __RS="${__RS%.}" + __US="$(printf '\037.')"; __US="${__US%.}" +__fn_stack='' +__fn_frame='' +__fn_name='' +__fn_tu='' +__fn_vars='' +__fn_var_vals='' +__fn='' +__prev_tu='' +__prev_vars='' +__var='' +__val='' +__init_funcs='' + +__fn_stack_push() +{ + __fn_stack="${__fn_stack}${__RS}${__fn_frame}" +} + +__fn_stack_peek() +{ + __fn_frame="${__fn_stack##*${__RS}}" +} + +__fn_stack_pop() +{ + __fn_stack_peek + __fn_stack="${__fn_stack%${__RS}*}" +} + +__fn_frame_encode() +{ + # RS and US in frame variable values must be encoded. + __fn_var_vals="$(printf '%s' "${__fn_var_vals}" | sed " + s|${__ESC}|${__ESC}0|g; + s|${__RS}|${__ESC}1|g; s|${__US}|${__ESC}2|g; + ")" + __fn_frame="${__fn_name}${__US}${__fn_tu}" + __fn_frame="${__fn_frame}${__US}${__fn_vars}${__US}${__fn_var_vals}" +} + +__fn_frame_decode() +{ + __fn_name="${__fn_frame%%${__US}*}" + __fn_frame="${__fn_frame#*${__US}}" + __fn_tu="${__fn_frame%%${__US}*}" + __fn_frame="${__fn_frame#*${__US}}" + __fn_vars="${__fn_frame%%${__US}*}" + __fn_var_vals="${__fn_frame#*${__US}}" + __fn_var_vals="$(printf '%s' "${__fn_var_vals}" | sed " + s|${__ESC}0|${__ESC}|g; + s|${__ESC}1|${__RS}|g; s|${__ESC}2|${__US}|g; + ")" +} + +__fn_ctxsw() +{ + unset IFS + case "${__prev_tu}" in + ${__fn_tu});; + ?*) + # Unset static variables and functions from previous TU. + eval "unset \${__${__prev_tu}_static_vars}" + for __fn in $(eval printf '%s' \ + "\"\${__${__prev_tu}_static_fns}\""); do + unset -f "${__fn%:*}" + done + ;; esac + case "${__fn_tu}" in + ${__prev_tu});; + ?*) + # Set static variables and function for the current TU. + eval "$(eval printf '%s' "\"$(eval printf '%s' \ + "'\${__${__fn_tu}_static_var_vals}'")\"")" + for __fn in $(eval printf '%s' \ + "\"\${__${__fn_tu}_static_fns}\""); do + eval "${__fn%:*}() { ${__fn#*:}; }" + done + ;; esac + unset ${__prev_vars} + eval "${__fn_var_vals}" +} + +__fn_update_vars() +{ + for __var in ${__fn_vars}; do + __val="$(eval "printf '%s' \"\${${__var}}\"" | \ + sed "s|'|'\\\\''|g;")" + __fn_var_vals="${__fn_var_vals} ${__var}='${__val}'" + done +} + +__fn_start() +{ + # Old state. + __prev_tu="${__fn_tu}" + __prev_vars="${__fn_vars}" + + # Update local vars in stack. + __fn_stack_pop + __fn_vars="${__prev_vars}" + __fn_update_vars + __fn_frame_encode + __fn_stack_push + + # New state. + __fn_name="${2}" + __fn_tu="${1}" + __fn_vars= + __fn_var_vals= + __fn_frame_encode + __fn_stack_push + + # Switch. + __fn_ctxsw +} + +__fn_end() +{ + # Old state. + __fn_stack_pop + __fn_frame_decode + __prev_tu="${__fn_tu}" + __prev_vars="${__fn_vars}" + + # New state. + __fn_stack_peek + __fn_frame_decode + + # Switch. + __fn_ctxsw +} + +__tu_end() +{ + # Save TU static vars. + for __var in $(eval "printf '%s' \"\${__${__tu}_static_vars}\""); do + __val="$(eval "printf '%s' \"\${${__var}}\"" | \ + sed "s|'|'\\\\''|g")" + eval "__${__tu}_static_var_vals=\"\${__${__tu}_static_var_vals}\ + ${__var}='\${__val}'\"" + unset ${__var} + done + + __tu='' +} + +__stack_trace() +{ + IFS="${__RS}" + for __fn_frame in ${__fn_stack#${__RS}}; do + printf '%s\n' "${__fn_frame%%${__US}*}" + done +} + +__local() +{ + for __var in "${@}"; do + case "${__var}" in + *=*) + __val="${__var#*=}" + __var="${__var%%=*}" + ;; + *) + __val='' + ;; + esac + case "${__var}" in [!A-Za-z_]* | *[!0-9A-Za-z_]*) + printf 'Syntax error: %s %s\n' \ + 'illegal static variable name:' "${__var}" >&2 + exit 1 + ;; esac + eval "${__var}=\${__val}" + __fn_vars="${__fn_vars} ${__var}" + done +} + +static() +{ + case "${__tu}" in '') + printf 'Error: Cannot declare static variables in functions' >&2 + exit 1 + ;; esac + for __var in "${@}"; do + case "${__var}" in + *=*) + __val="${__var#*=}" + __var="${__var%%=*}" + ;; + *) + __val='' + ;; + esac + case "${__var}" in [!A-Za-z_]* | *[!0-9A-Za-z_]*) + printf 'Syntax error: %s %s\n' \ + 'illegal static variable name:' "${__var}" >&2 + exit 1 + ;; esac + eval "${__var}=\${__val}" + eval "__${__tu}_static_vars=\"\${__${__tu}_static_vars} \ + ${__var}\"" + done +} + +__init() +{ + __init_funcs="${__init_funcs} ${1}" +} +EOF +} + +eshrtend() +{ + local entry="${1}" + shift 1 + + cat <<-'EOF' +for __func in ${__init_funcs}; do + ${__func} +done +EOF + printf '%s "${@}"\n' "${entry_point}" +} diff --git a/eshld/link.esh b/eshld/link.esh index d01fc4d..d674821 100644 --- a/eshld/link.esh +++ b/eshld/link.esh @@ -1,6 +1,6 @@ -# Eggshell Linker entry point +# Eggshell Linker program linking functions # -# Copyright (C) 2015 Patrick "P. J." McDermott +# Copyright (C) 2015, 2016 Patrick "P. J." McDermott # # This file is part of the Eggshell Linker. # @@ -50,20 +50,11 @@ link_begin() die 'Cannot open file "%s"' "${output}~" fi - # Write magic number and interpreter path. + # Add a magic number with interpreter path and the runtime library. if ${make_executable}; then printf '#!%s\n' "${interpreter}" >&3 + eshrtbegin >&3 fi - - # Write __init() function. - cat >&3 <<-'EOF' - __init_funcs='' - __init() - { - __init_funcs="${__init_funcs} ${1}" - } - EOF - } link_file() @@ -77,16 +68,9 @@ link_file() link_end() { - # Add call to init functions. - cat >&3 <<-'EOF' - for __func in ${__init_funcs}; do - ${__func} - done - EOF - - # Add call to entry point. + # Add calls to __init functions and the entry point. if ${make_executable}; then - printf '%s "${@}"\n' "${entry_point}" >&3 + eshrtend "${entry_point}" >&3 fi # Close output file, make it executable, and set its name. diff --git a/eshld/main.esh b/eshld/main.esh index 775179f..24cdae1 100644 --- a/eshld/main.esh +++ b/eshld/main.esh @@ -1,6 +1,6 @@ # Eggshell Linker entry point # -# Copyright (C) 2015 Patrick "P. J." McDermott +# Copyright (C) 2015, 2016 Patrick "P. J." McDermott # # This file is part of the Eggshell Linker. # |