# Shell command language code generator
#
# Copyright (C) 2016 Patrick "P. J." McDermott
#
# This file is part of the Eggshell Compiler.
#
# The Eggshell Compiler 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 Compiler 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 Compiler. If not, see
# .
sbufi=
sbufc=
sc=
tu_id=
static_fn_n=
pragma_nostack=false
sgetc()
{
if [ ${sbufi} -ge ${sbufc} ]; then
sc=''
else
eval "sc=\${sbufv_${sbufi}}"
sbufi=$((${sbufi} + 1))
fi
}
add_static_fn()
{
local fn="${1}"
printf '__%s_static_fns="${__%s_static_fns} %s:__%s_fn%d"\n' \
"${tu_id}" "${tu_id}" "${fn}" "${tu_id}" ${static_fn_n}
printf '__%s_fn%d' "${tu_id}" ${static_fn_n}
static_fn_n=$((${static_fn_n} + 1))
}
codegen_sub()
{
local array="${1}"
shift 1
local print_spc=
local ionum=
local pragma=
local fname=
local static=
local static_fname=
local type=
local params=
local types=
local t=
print_spc=false
ionum=false
pragma=false
fname=''
static=false
static_fname=false
type=false
params=0
types=''
IFS="${RS}"
for t in ${array}; do
if ${print_spc}; then
case "${t%${US}*}" in
T_NEWLINE)
;;
*)
printf ' '
;;
esac
print_spc=false
fi
# Handle I/O number false positives.
if ${ionum}; then
case "${t%${US}*}" in
T_WORD|T_CMDNAME|T_FNAME)
printf ' '
;;
esac
ionum=false
fi
case "${t%${US}*}" in
T_USE)
pragma=true
continue
;;
T_FNAME)
fname="${t}"
params=0
types=''
;;
T_STATIC)
static=true
continue
;;
T_TYPE)
type=true
params=$((${params} + 1))
types="${types}${t#*${US}}:"
continue
;;
T_VOID)
params=-1
continue
;;
T_COMMA)
continue
;;
esac
if ${pragma}; then
pragma=false
eval "pragma_${t#*${US}}=true"
continue
fi
# State machine for static variables and functions
if ${static}; then
static=false
case "${t%${US}*}" in
T_FNAME)
static_fname=true
continue
;;
*)
printf '__static '
;;
esac
elif ${static_fname}; then
static_fname=false
case "${t%${US}*}" in
T_LPAREN)
add_static_fn "${fname#*${US}}"
;;
*)
printf '__static '
toktext "${fname}"
;;
esac
elif ${type}; then
type=false
types="${types}${t#*${US}} "
continue
fi
# Function start/end tokens
case "${t%${US}*}" in
T_FN_START)
if ! ${pragma_nostack}; then
printf '__fn_start %s %s;' \
"${tu_id}" "${fname#*${US}}"
else
printf ':;'
fi
if [ ${params} -gt 0 ]; then
printf ' __check_args %s %d %s %s;' \
"${fname#*${US}}" ${params} \
"'${types% }'" '"${@}"'
elif [ ${params} -eq -1 ]; then
printf ' __check_args %s 0 "" "${@}";' \
"${fname#*${US}}"
fi
;;
T_FN_END)
if ! ${pragma_nostack}; then
printf '__fn_end;'
else
printf ':;'
fi
;;
T_RETURN)
if ! ${pragma_nostack}; then
printf '__fn_end; '
fi
;;
T_STATIC)
printf '__static '
continue
;;
T_LOCAL)
printf '__local '
continue
;;
esac
toktext "${t}"
case "${t%${US}*}" in
T_NEWLINE|T_LPAREN)
# Indenting lines can mess up here-documents.
# Adding a space after "(" can mess up function
# definitions (on zsh at least, while other
# shells accept the space).
;;
T_IO_NUMBER)
ionum=true
;;
*)
print_spc=true
;;
esac
done
unset IFS
}
# The token stack is encoded in a string in the following grammar:
# Terminal symbols:
# TOKEN
# Production rules:
# stack = tokens [ '' type '' stack '' [ tokens ] ] ;
# tokens = TOKEN { '' TOKEN } ;
# type = 'C' ;
# We need to recurse through this stack to get to all the tokens.
# Each element in the stack (an array of tokens) gets run through the codegen to
# become text that is inserted into the array below.
parse_stack()
{
local array=
local res=
array=''
while :; do
sgetc
case "${sc}" in
'')
# EOF
break
;;
"${SOH}")
# New stack element
sgetc
case "${sc}" in
'C')
# Command substitution
sgetc # STX
res="$(parse_stack)"
sbufi="${res%% *}"
array="${array}${res#* }"
;;
esac
;;
"${ETX}")
# End of stack element
break
;;
*)
# Token character
array="${array}${sc}"
;;
esac
done
printf '%d ' ${sbufi}
codegen_sub "${array}"
}
sh_parse_stack()
{
local buf="${1}"
shift 1
local res=
eval "$(printf '%s' "${buf}" | awk -v FS='' -v j=0 \
-v squote="'" -v esc_squote="'\\\\''" '
{
for (i = 1; i <= NF; ++i) {
sub(squote, esc_squote, $i);
printf("sbufv_%d='\''%s'\''\n", j++, $i);
};
printf("sbufv_%d='\''\n'\''\n", j++);
}
')"
sbufi=0
sbufc=${#buf}
res="$(parse_stack; printf '.')"
res="${res%.}"
printf '%s' "${res#* }"
}
sh_set_tu_id()
{
local toks="${1}"
tu_id="$(printf '%s' "${toks}" | sha256sum -)"
tu_id="${tu_id% -}"
return 0
}
sh_start_tu()
{
printf '__tu=%s\n' "${tu_id}"
printf '__%s_static_vars=\n' "${tu_id}"
printf '__%s_static_var_vals=\n' "${tu_id}"
printf '__%s_static_fns=\n' "${tu_id}"
static_fn_n=0
}
sh_end_tu()
{
printf '__tu_end\n'
}