blob: 3b2f8801cfee0bc8a0e4aa1882fe14bbd5ae0a4e [file] [log] [blame]
#!/bin/sh
# Minimal Object-Oriented style PreProcessor.
# Copyright (C) 2006-2008, 2016, 2018-2019 Free Software Foundation, Inc.
# Written by Bruno Haible <bruno@clisp.org>, 2006.
#
# This program 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.
#
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Usage: moopp source.oo.c source.oo.h superclass.oo.h ...
# Arguments:
# - the source file of the class,
# - the header file declaring the class,
# - the header file declaring its superclass,
# - etc. up to the root class.
# Creates four files in the current directory:
# - source.c, the preprocessing result of source.oo.c,
# - source.h, the preprocessing result of source.oo.h,
# - class.priv.h, a file declaring the private fields of the class,
# - class.vt.h, a file declaring the virtual function table of the class.
# This implementation of the preprocessor is a quick hack. It makes assumptions
# about the source code:
# - GNU indentation style,
# - the struct declaration must be in a single line,
# - no comments on the struct declaration line,
# - no #ifs in relevant position,
# - ...
# Someday this should be rewritten to use a proper tokenizer and parser.
# func_usage
# outputs to stdout the --help usage message.
func_usage ()
{
echo "\
Usage: moopp [OPTION]... SOURCE.oo.c SOURCE.oo.h SUPERCLASS.oo.h ...
Preprocesses SOURCE.oo.c into CLASS.c and SOURCE.oo.h into CLASS.h,
where CLASS is the name of the class defined in these files.
See moo.h for the object-oriented features and the syntax of the input files.
Options:
--help print this help and exit
--version print version information and exit
--dllexport=NAME Arrange so that the specified class name can be accessed
from outside the shared library it is compiled into.
This option can be repeated.
Report bugs to <bruno@clisp.org>."
}
# func_version
# outputs to stdout the --version message.
func_version ()
{
echo "$progname (GNU $package) $version"
echo "Copyright (C) 2006-2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law."
echo "Written by" "Bruno Haible"
}
# func_fatal_error message
# outputs to stderr a fatal error message, and terminates the program.
func_fatal_error ()
{
echo "moopp: *** $1" 1>&2
echo "moopp: *** Stop." 1>&2
exit 1
}
# Command-line option processing.
# Removes the OPTIONS from the arguments. Sets the variables:
# - dllexports list of class names to export from Woe32 DLLs
dllexports=
while test $# -gt 0; do
case "$1" in
--dllexport | --dllexpor | --dllexpo | --dllexp | --dllex | --dlle )
shift
if test $# = 0; then
func_fatal_error "missing argument for --dllexport"
fi
case "$1" in
-*) func_fatal_error "missing argument for --dllexport" ;;
esac
dllexports="$dllexports $1"
shift ;;
--dllexport=* )
arg=`echo "X$1" | sed -e 's/^X--dllexport=//'`
dllexports="$dllexports $arg"
shift ;;
--help | --hel | --he | --h )
func_usage
exit 0 ;;
--version | --versio | --versi | --vers | --ver | --ve | --v )
func_version
exit 0 ;;
-- ) # Stop option prcessing
shift; break ;;
-* )
func_fatal_error "unrecognized option: $option"
;;
* )
break ;;
esac
done
if test $# -lt 2; then
func_fatal_error "Usage: $0 [OPTION]... source.oo.c source.oo.h superclass.oo.h ..."
fi
# Check that all files exist.
for file
do
test -r "$file" || {
func_fatal_error "file $file does not exist"
}
done
source_impl_file="$1"
source_header_file="$2"
shift
shift
case "$source_impl_file" in
*.oo.c) ;;
*) func_fatal_error "invalid class source file name: $source_impl_file" ;;
esac
case "$source_header_file" in
*.oo.h) ;;
*) func_fatal_error "invalid class header file name: $source_header_file" ;;
esac
# A sed expression that removes empty lines.
sed_remove_empty_lines='/^$/d'
# A sed expression that removes ANSI C and ISO C99 comments.
sed_remove_comments="
/[/][/*]/{
ta
:a
s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)//.*,\\1,
te
s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*]\\([^*]\\|[*][^/*]\\)*[*][*]*/,\\1 ,
ta
/^\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*[/][*]/{
s,^\\(\\([^\"'/]\\|\"\\([^\\\"]\\|[\\].\\)*\"\\|'\\([^\\']\\|[\\].\\)*'\\|[/][^\"'/*]\\|[/]\"\\([^\\\"]\\|[\\].\\)*\"\\|[/]'\\([^\\']\\|[\\].\\)*'\\)*\\)/[*].*,\\1 ,
tu
:u
n
s,^\\([^*]\\|[*][^/*]\\)*[*][*]*/,,
tv
s,^.*\$,,
bu
:v
}
:e
}"
# The same thing as an extended regular expression, for use with
# sed --regexp-extended.
sed_remove_comments_ERE="
/[/][/*]/{
ta
:a
s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)//.*,\\1,
te
s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*]([^*]|[*][^/*])*[*][*]*/,\\1 ,
ta
/^([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*[/][*]/{
s,^(([^\"'/]|\"([^\\\"]|[\\].)*\"|'([^\\']|[\\].)*'|[/][^\"'/*]|[/]\"([^\\\"]|[\\].)*\"|[/]'([^\\']|[\\].)*')*)/[*].*,\\1 ,
tu
:u
n
s,^([^*]|[*][^/*])*[*][*]*/,,
tv
s,^.*\$,,
bu
:v
}
:e
}"
# Check that 'sed' supports the kind of regular expressions used in
# sed_remove_comments. The use of \| meaning alternation of basic regular
# expressions is a GNU extension.
sed_test='s,^\(\(a\|X\)*\)//.*,\1,'
sed_result=`echo 'aaa//bcd' | sed -e "$sed_test"`
test "$sed_result" = 'aaa' \
|| func_fatal_error "The 'sed' program is not GNU sed. Try installing GNU sed."
# func_check_impl_syntax file
# Check the syntax of the source implementation file.
# Output:
# - classname name of the class being defined (without 'struct')
# - superclassname name of the superclass, or empty for a root class
# - impl_decl_lineno line number of the class name declaration ('struct')
# - impl_beg_lineno line number of the start of the class declaration ('{')
# - impl_end_lineno line number of the end of the class declaration ('}')
# - fields field declarations, including preprocessor directives
func_check_impl_syntax ()
{
file="$1"
sed -e "$sed_remove_comments" < "$file" | grep '^fields:' > /dev/null || {
func_fatal_error "$file does not contain 'fields:'"
}
test `sed -e "$sed_remove_comments" < "$file" | grep -c '^fields:'` = 1 || {
func_fatal_error "$file contains more than one 'fields:'"
}
fields_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^fields:' | sed -e 's,:.*,,'`
sed_before_fields="$fields_lineno"',$d'
impl_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^struct[ ]' | tail -n 1 | sed -e 's,:.*,,'`
test -n "$impl_decl_lineno" || {
func_fatal_error "$file: class declaration not found"
}
class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$impl_decl_lineno"'p'`
sed_extract_classname='s,^struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
test -n "$classname" || {
func_fatal_error "$0: $file: class name not recognized at line $impl_decl_lineno"
}
superclassname=
if echo "$class_line" | grep ':' > /dev/null; then
sed_extract_superclassname='s,^.*:[ ]*struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
test -n "$superclassname" || {
func_fatal_error "$file: superclass name not recognized at line $impl_decl_lineno"
}
fi
impl_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_fields" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
{ test -n "$impl_beg_lineno" && test "$impl_decl_lineno" -lt "$impl_beg_lineno"; } || {
func_fatal_error "$file: opening brace of class declaration not found after line $impl_decl_lineno"
}
sed_after_fields='1,'"$fields_lineno"'d'
impl_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_fields" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
test -n "$impl_end_lineno" || {
func_fatal_error "$file: closing brace of class declaration not found after line $fields_lineno"
}
impl_end_lineno=`expr $fields_lineno + $impl_end_lineno`
sed_extract_fields="$impl_end_lineno"',$d;1,'"$fields_lineno"'d'
fields=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_fields"`
}
# func_check_header_syntax file
# Check the syntax of a header file.
# Output:
# - classname name of the class being defined (without 'struct')
# - superclassname name of the superclass, or empty for a root class
# - class_decl_lineno line number of the class name declaration ('struct')
# - class_beg_lineno line number of the start of the class declaration ('{')
# - class_end_lineno line number of the end of the class declaration ('}')
# - methods newline-separated list of method declarations
func_check_header_syntax ()
{
file="$1"
sed -e "$sed_remove_comments" < "$file" | grep '^methods:' > /dev/null || {
func_fatal_error "$file does not contain 'methods:'"
}
test `sed -e "$sed_remove_comments" < "$file" | grep -c '^methods:'` = 1 || {
func_fatal_error "$file contains more than one 'methods:'"
}
methods_lineno=`sed -e "$sed_remove_comments" < "$file" | grep -n '^methods:' | sed -e 's,:.*,,'`
sed_before_methods="$methods_lineno"',$d'
class_decl_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^struct[ ]' | tail -n 1 | sed -e 's,:.*,,'`
test -n "$class_decl_lineno" || {
func_fatal_error "$file: class declaration not found"
}
class_line=`sed -e "$sed_remove_comments" < "$file" | sed -n -e "$class_decl_lineno"'p'`
sed_extract_classname='s,^struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
classname=`echo "$class_line" | sed -n -e "$sed_extract_classname"`
test -n "$classname" || {
func_fatal_error "$0: $file: class name not recognized at line $class_decl_lineno"
}
superclassname=
if echo "$class_line" | grep ':' > /dev/null; then
sed_extract_superclassname='s,^.*:[ ]*struct[ ][ ]*\([A-Za-z_0-9]*\).*,\1,p'
superclassname=`echo "$class_line" | sed -n -e "$sed_extract_superclassname"`
test -n "$superclassname" || {
func_fatal_error "$file: superclass name not recognized at line $class_decl_lineno"
}
fi
class_beg_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_before_methods" | grep -n '^{' | tail -n 1 | sed -e 's,:.*,,'`
{ test -n "$class_beg_lineno" && test "$class_decl_lineno" -lt "$class_beg_lineno"; } || {
func_fatal_error "$file: opening brace of class declaration not found after line $class_decl_lineno"
}
sed_after_methods='1,'"$methods_lineno"'d'
class_end_lineno=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_after_methods" | grep -n '^}' | sed -e '1q' | sed -e 's,:.*,,'`
test -n "$class_end_lineno" || {
func_fatal_error "$file: closing brace of class declaration not found after line $methods_lineno"
}
class_end_lineno=`expr $methods_lineno + $class_end_lineno`
sed_extract_methods="$class_end_lineno"',$d;1,'"$methods_lineno"'d'
methods=`sed -e "$sed_remove_comments" < "$file" | sed -e "$sed_extract_methods" | tr '\015\n' ' ' | tr ';' '\n' | sed -e 's,[ ]*$,,'`
sed_remove_valid_arg1_lines='/([ ]*'"$classname"'_t[ ]*[A-Za-z_0-9]*[ ]*[,)]/d'
sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*(.*$,\1,'
methods_with_bad_arg1=`echo "$methods" | sed -e "$sed_remove_empty_lines" -e "$sed_remove_valid_arg1_lines" -e "$sed_extract_method_name"`
if test -n "$methods_with_bad_arg1"; then
methods_with_bad_arg1=`{ echo "$methods_with_bad_arg1" | sed -e 's/$/, /' | tr -d '\015\n'; echo; } | sed -e 's/\(, \)*$//'`
func_fatal_error "$file: some methods don't have a first argument of type ${classname}_t: $methods_with_bad_arg1"
fi
}
func_check_impl_syntax "$source_impl_file"
impl_classname="$classname"
impl_superclassname="$superclassname"
func_check_header_syntax "$source_header_file"
main_classname="$classname"
main_superclassname="$superclassname"
main_class_decl_lineno="$class_decl_lineno"
main_class_beg_lineno="$class_beg_lineno"
main_class_end_lineno="$class_end_lineno"
main_methods="$methods"
all_superclasses=
all_methods="$methods"
inherited_methods=
last_header_file="$source_header_file"
expected_superclassname="$superclassname"
for file
do
if test -z "$expected_superclassname"; then
func_fatal_error "file $last_header_file does not specify a superclass; superfluous command line argument $file"
fi
func_check_header_syntax "$file"
all_superclasses="$classname $all_superclasses"
all_methods="$methods
$all_methods"
inherited_methods="$methods
$inherited_methods"
if test "$classname" != "$expected_superclassname"; then
func_fatal_error "file $last_header_file specifies superclass '$expected_superclassname', but file $file defines class '$classname'"
fi
last_header_file="$file"
expected_superclassname="$superclassname"
done
if test -n "$expected_superclassname"; then
func_fatal_error "$0: file $last_header_file specifies superclass '$expected_superclassname', please specify the header file that defines it as additional command line argument"
fi
if test "$impl_classname" != "$main_classname"; then
func_fatal_error "file $source_header_file specifies class '$main_classname', but file $source_impl_file specifies class '$impl_classname'"
fi
if test "$impl_superclassname" != "$main_superclassname"; then
if test -z "$main_superclassname"; then
func_fatal_error "file $source_header_file specifies no superclass, but file $source_impl_file specifies a superclass '$impl_superclassname'"
fi
if test -z "$impl_superclassname"; then
func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies no superclass"
fi
func_fatal_error "file $source_header_file specifies a superclass '$main_superclassname', but file $source_impl_file specifies a superclass '$impl_superclassname'"
fi
# func_start_creation file
# starts the creation of the named file.
func_start_creation ()
{
file="$1"
if test -f "$file"; then
echo "Updating $file (backup in ${file}~)"
mv -f "$file" "${file}~" || func_fatal_error "failed"
else
echo "Creating $file"
fi
}
# func_emit_priv_h newfile
# outputs to $newfile the contents of class.priv.h.
func_emit_priv_h ()
{
newfile="$1"
{
echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
echo
if test -n "${main_superclassname}"; then
echo "/* Field layout of superclass. */"
echo "#include \"${main_superclassname}.priv.h\""
echo
fi
echo "/* Field layout of ${main_classname} class. */"
echo "struct ${main_classname}_representation"
echo "{"
if test -n "${main_superclassname}"; then
echo " struct ${main_superclassname}_representation base;"
else
echo " const void *vtable;"
fi
echo "$fields" | sed -e "$sed_remove_empty_lines"
echo "};"
} > "$newfile"
}
# func_emit_vt_h newfile
# outputs to $newfile the contents of class.vt.h.
func_emit_vt_h ()
{
newfile="$1"
{
echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
echo
if test -n "${main_superclassname}"; then
echo "/* Virtual function table layout of superclass. */"
echo "#include \"${main_superclassname}.vt.h\""
echo
fi
echo "/* Virtual function table layout of ${main_classname} class. */"
echo "$main_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*([^,)]*/\1(*\2) (THIS_ARG/' -e 's,$,;,'
} > "$newfile"
}
# In C++ mode, we have a precise type checking. But in C mode, we have only
# loose type checking: So that rootclass_t and subclass_t are assignment
# compatible, we have to define subclass_t as identical to rootclass_t.
# Therefore we need an alias name for the representation of any type in the
# hierarchy.
if test -z "$main_superclassname"; then
main_repclassalias="any_${main_classname}_representation"
else
main_repclassalias="${main_classname}_representation"
fi
sed_extract_method_rettype='s,^\(.*[^A-Za-z_0-9]\)[A-Za-z_0-9][A-Za-z_0-9]*[ ]*(.*$,\1,
s,^[ ]*,,
s,[ ]*$,,'
sed_extract_method_name='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*(.*$,\1,'
sed_extract_method_arglist='s,^.*[^A-Za-z_0-9][A-Za-z_0-9][A-Za-z_0-9]*[ ]*([^,)]*\(.*\)).*$,'"${main_classname}_t"' first_arg\1,'
sed_extract_method_args='s,^.*[^A-Za-z_0-9]\([A-Za-z_0-9][A-Za-z_0-9]*\)$,\1,'
# func_emit_source_h newfile newfile_base
# outputs to $newfile the contents of source.h.
source_header_file_base=`echo "$source_header_file" | sed -e 's,^.*/,,'`
func_emit_source_h ()
{
newfile="$1"
newfile_base="$2"
# Use DLL_VARIABLE if and only if the main classname is among the names
# specified with --dllexport options.
dllexport_for_variables=
for name in $dllexports; do
if test "${main_classname}" = "${name}"; then
dllexport_for_variables=" DLL_VARIABLE"
break
fi
done
{
echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
echo
echo "#line 1 \"${source_header_file_base}\""
cat "$source_header_file" | sed -e "${main_class_decl_lineno}"',$d'
echo "#line "`expr 3 + ${main_class_decl_lineno} + 1`" \"$newfile_base\""
echo "struct ${main_repclassalias};"
echo "/* ${main_classname}_t is defined as a pointer to struct ${main_repclassalias}."
echo " In C++ mode, we use a smart pointer class."
echo " In C mode, we have no other choice than a typedef to the root class type. */"
echo "#if IS_CPLUSPLUS"
echo "struct ${main_classname}_t"
echo "{"
echo "private:"
echo " struct ${main_repclassalias} *_pointer;"
echo "public:"
echo " ${main_classname}_t () : _pointer (NULL) {}"
echo " ${main_classname}_t (struct ${main_repclassalias} *pointer) : _pointer (pointer) {}"
echo " struct ${main_repclassalias} * operator -> () { return _pointer; }"
echo " operator struct ${main_repclassalias} * () { return _pointer; }"
atroot=yes
for classname in $all_superclasses; do
if test -n "$atroot"; then
repclassalias="any_${classname}_representation"
else
repclassalias="${classname}_representation"
fi
echo " operator struct ${repclassalias} * () { return (struct ${repclassalias} *) _pointer; }"
atroot=
done
# The 'operator void *' is needed to avoid ambiguous conversion chains.
echo " operator void * () { return _pointer; }"
# The 'operator ==' and 'operator !=' are needed to avoid ambiguous comparisons with NULL.
echo " bool operator == (const void *p) { return _pointer == p; }"
echo " bool operator != (const void *p) { return _pointer != p; }"
atroot=yes
for classname in $all_superclasses; do
if test -n "$atroot"; then
repclassalias="any_${classname}_representation"
else
repclassalias="${classname}_representation"
fi
echo " operator ${classname}_t () { return (${classname}_t) (struct ${repclassalias} *) _pointer; }"
# The 'explicit' constructors allow to downcast.
echo " explicit ${main_classname}_t (${classname}_t x) : _pointer ((struct ${main_repclassalias} *) (void *) x) {}"
atroot=
done
echo "};"
echo "#else"
if test -n "${main_superclassname}"; then
echo "typedef ${main_superclassname}_t ${main_classname}_t;"
else
echo "typedef struct ${main_repclassalias} * ${main_classname}_t;"
fi
echo "#endif"
echo
echo "/* Functions that invoke the methods. */"
echo "#ifdef __cplusplus"
echo "extern \"C\" {"
echo "#endif"
echo "$all_methods" | sed -e "$sed_remove_empty_lines" -e 's/\([^A-Za-z_0-9]\)\([A-Za-z_0-9][A-Za-z_0-9]*\)[ ]*([^,)]*/\1'"${main_classname}_"'\2 ('"${main_classname}_t first_arg"'/' -e 's,^,extern ,' -e 's,$,;,'
echo "#ifdef __cplusplus"
echo "}"
echo "#endif"
echo
# Now come the implementation details.
echo "/* Type representing an implementation of ${main_classname}_t. */"
echo "struct ${main_classname}_implementation"
echo "{"
echo " const typeinfo_t * const *superclasses;"
echo " size_t superclasses_length;"
echo " size_t instance_size;"
echo "#define THIS_ARG ${main_classname}_t first_arg"
echo "#include \"${main_classname}.vt.h\""
echo "#undef THIS_ARG"
echo "};"
echo
echo "/* Public portion of the object pointed to by a ${main_classname}_t. */"
echo "struct ${main_classname}_representation_header"
echo "{"
echo " const struct ${main_classname}_implementation *vtable;"
echo "};"
echo
echo "#if HAVE_INLINE"
echo
echo "/* Define the functions that invoke the methods as inline accesses to"
echo " the ${main_classname}_implementation."
echo " Use #define to avoid a warning because of extern vs. static. */"
echo
echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
while read method; do
rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
name=`echo "$method" | sed -e "$sed_extract_method_name"`
arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
if test "$arglist" = "void"; then
args=
else
args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
fi
if test "$rettype" = "void"; then
return=
else
return="return "
fi
echo "# define ${main_classname}_${name} ${main_classname}_${name}_inline"
echo "static inline $rettype"
echo "${main_classname}_${name} ($arglist)"
echo "{"
echo " const struct ${main_classname}_implementation *vtable ="
echo " ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
echo " ${return}vtable->${name} ($args);"
echo "}"
echo
done
echo "#endif"
echo
echo "extern${dllexport_for_variables} const typeinfo_t ${main_classname}_typeinfo;"
if test -n "${main_superclassname}"; then
superclasses_array_initializer="${main_superclassname}_SUPERCLASSES"
else
superclasses_array_initializer="NULL"
fi
echo "#define ${main_classname}_SUPERCLASSES &${main_classname}_typeinfo, ${superclasses_array_initializer}"
if test -n "${main_superclassname}"; then
echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + ${main_superclassname}_SUPERCLASSES_LENGTH)"
else
echo "#define ${main_classname}_SUPERCLASSES_LENGTH (1 + 1)"
fi
echo
echo "extern${dllexport_for_variables} const struct ${main_classname}_implementation ${main_classname}_vtable;"
echo
echo "#line "`expr $main_class_end_lineno + 1`" \"${source_header_file_base}\""
cat "$source_header_file" | sed -e "1,${main_class_end_lineno}d"
} > "$newfile"
}
# func_emit_source_c newfile newfile_base
# outputs to $newfile the contents of source.c.
source_impl_file_base=`echo "$source_impl_file" | sed -e 's,^.*/,,'`
func_emit_source_c ()
{
newfile="$1"
newfile_base="$2"
{
echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'
echo
# In C mode, where subclass_t is identical to rootclass_t, we define the
# any_rootclass_representation type to the right one for subclass.
if test -n "$all_superclasses"; then
for classname in $all_superclasses; do
rootclassname="$classname"
break
done
else
rootclassname="$main_classname"
fi
echo "#if !IS_CPLUSPLUS"
echo "#define ${main_classname}_representation any_${rootclassname}_representation"
echo "#endif"
echo "#line 1 \"${source_impl_file_base}\""
cat "$source_impl_file" | sed -e "${impl_decl_lineno}"',$d'
echo "#line "`expr 6 + ${impl_decl_lineno} + 1`" \"$newfile_base\""
echo "#include \"${main_classname}.priv.h\""
echo
echo "const typeinfo_t ${main_classname}_typeinfo = { \"${main_classname}\" };"
echo
echo "static const typeinfo_t * const ${main_classname}_superclasses[] ="
echo " { ${main_classname}_SUPERCLASSES };"
echo
if test -n "${main_superclassname}"; then
echo "#define super ${main_superclassname}_vtable"
echo
fi
echo "#line "`expr $impl_end_lineno + 1`" \"${source_impl_file_base}\""
cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "s,${main_classname}::,${main_classname}__,g"
echo
lineno=`wc -l < "$newfile"`
echo "#line "`expr $lineno + 2`" \"$newfile_base\""
# Define trivial stubs for methods that are not defined or overridden.
inherited_method_names=`echo "$inherited_methods" | sed -e "$sed_remove_empty_lines" | sed -e "$sed_extract_method_name"`
echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
while read method; do
rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
name=`echo "$method" | sed -e "$sed_extract_method_name"`
arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
if test "$arglist" = "void"; then
args=
else
args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
fi
if test "$rettype" = "void"; then
return=
else
return="return "
fi
if cat "$source_impl_file" | sed -e "1,${impl_end_lineno}d" | sed -e "$sed_remove_comments" | grep "${main_classname}::${name} *(" > /dev/null; then
# The class defines or overrides the method.
:
else
# Add a stub for the method.
inherited=
for i in $inherited_method_names; do
if test "$i" = "$name"; then
inherited=yes
fi
done
# First a prototype, to avoid gcc -Wmissing-prototypes warnings.
echo "$rettype ${main_classname}__${name} ($arglist);"
echo "$rettype"
echo "${main_classname}__${name} ($arglist)"
echo "{"
if test -n "$inherited"; then
echo " ${return}super.${name} ($args);"
else
echo " /* Abstract (unimplemented) method called. */"
echo " abort ();"
# Avoid C++ compiler warning about missing return value.
echo " #ifndef __GNUC__"
echo " ${return}${main_classname}__${name} ($args);"
echo " #endif"
fi
echo "}"
echo
fi
done
echo
echo "const struct ${main_classname}_implementation ${main_classname}_vtable ="
echo "{"
echo " ${main_classname}_superclasses,"
echo " sizeof (${main_classname}_superclasses) / sizeof (${main_classname}_superclasses[0]),"
echo " sizeof (struct ${main_classname}_representation),"
echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
while read method; do
name=`echo "$method" | sed -e "$sed_extract_method_name"`
echo " ${main_classname}__${name},"
done
echo "};"
echo
echo "#if !HAVE_INLINE"
echo
echo "/* Define the functions that invoke the methods. */"
echo
echo "$all_methods" | sed -e "$sed_remove_empty_lines" |
while read method; do
rettype=`echo "$method" | sed -e "$sed_extract_method_rettype"`
name=`echo "$method" | sed -e "$sed_extract_method_name"`
arglist=`echo "$method" | sed -e "$sed_extract_method_arglist"`
if test "$arglist" = "void"; then
args=
else
args=`{ echo "$arglist" | tr ',' '\n' | sed -e "$sed_extract_method_args" | tr '\n' ','; echo; } | sed -e 's/,$//'`
fi
if test "$rettype" = "void"; then
return=
else
return="return "
fi
echo "$rettype"
echo "${main_classname}_${name} ($arglist)"
echo "{"
echo " const struct ${main_classname}_implementation *vtable ="
echo " ((struct ${main_classname}_representation_header *) (struct ${main_repclassalias} *) first_arg)->vtable;"
echo " ${return}vtable->${name} ($args);"
echo "}"
echo
done
echo "#endif"
} > "$newfile"
}
# Generate the files in the source directory, not in the current directory.
# This is needed because they need to be distributed, since not all platforms
# have GNU 'sed' preinstalled.
sed_butbase='s,[^/]*$,,'
destdir=`echo "$source_impl_file" | sed -e "$sed_butbase"`
# Generate the source.h file first. The Makefile.am snippets rely on the
# fact that the other generated files have the same or a newer timestamp.
#
# Also, generate the source.c file last. The Makefile.am snippets don't know
# about the other generated files; they assume that when the source.c file
# is finished, this command is complete.
new_source_header_file_base=`echo "$source_header_file_base" | sed -e 's,\.oo\.h$,.h,'`
new_source_header_file="${destdir}$new_source_header_file_base"
func_start_creation "$new_source_header_file"
func_emit_source_h "$new_source_header_file" "$new_source_header_file_base" \
|| func_fatal_error "failed"
new_priv_header_file="${destdir}${main_classname}.priv.h"
func_start_creation "$new_priv_header_file"
func_emit_priv_h "$new_priv_header_file" \
|| func_fatal_error "failed"
new_vt_header_file="${destdir}${main_classname}.vt.h"
func_start_creation "$new_vt_header_file"
func_emit_vt_h "$new_vt_header_file" \
|| func_fatal_error "failed"
new_source_impl_file_base=`echo "$source_impl_file_base" | sed -e 's,\.oo\.c$,.c,'`
new_source_impl_file="${destdir}$new_source_impl_file_base"
func_start_creation "$new_source_impl_file"
func_emit_source_c "$new_source_impl_file" "$new_source_impl_file_base" \
|| func_fatal_error "failed"