#!/bin/bash # # mkmetakind # Morgan Deters for CVC4 # Copyright (c) 2010-2013 The CVC4 Project # # The purpose of this script is to create metakind.h from a template # and a list of theory kinds. # # This is kept distinct from kind.h because kind.h is a public header # and metakind.h is intended for the expr package only. # # Invocation: # # mkmetakind template-file theory-kind-files... # # Output is to standard out. # copyright=2010-2013 cat <&2 exit 1 fi # this script doesn't care about the theory class information, but # makes does make sure it's there seen_theory=true if [ "$1" = THEORY_BUILTIN ]; then if $seen_theory_builtin; then echo "$kf:$lineno: error: \"builtin\" theory redefined" >&2 exit 1 fi seen_theory_builtin=true elif [ -z "$1" -o -z "$2" -o -z "$3" ]; then echo "$kf:$lineno: error: \"theory\" directive missing class or header argument" >&2 exit 1 elif ! expr "$2" : '\(::*\)' >/dev/null; then echo "$kf:$lineno: warning: theory class \`$1' isn't fully-qualified (e.g., ::CVC4::theory::foo)" >&2 elif ! expr "$2" : '\(::CVC4::theory::*\)' >/dev/null; then echo "$kf:$lineno: warning: theory class not under ::CVC4::theory namespace" >&2 fi theory_class=$1 metakind_includes="${metakind_includes} // #include \"theory/$b/$2\"" } function alternate { # alternate ID name T header lineno=${BASH_LINENO[0]} if $seen_theory; then echo "$kf:$lineno: error: multiple theories defined in one file !?" >&2 exit 1 fi seen_theory=true seen_endtheory=true theory_id="$1" name="$2" theory_class="$3" theory_header="$4" theory_includes="${theory_includes}#include \"$theory_header\" " use_theory_validations="${use_theory_validations} if(theory == \"$name\") { return true; }" theory_alternate_doc="$theory_alternate_doc$name - alternate implementation for $theory_id\\n\\ " } function properties { # properties prop* lineno=${BASH_LINENO[0]} check_theory_seen } function endtheory { # endtheory lineno=${BASH_LINENO[0]} check_theory_seen seen_endtheory=true } function enumerator { # enumerator KIND enumerator-class header lineno=${BASH_LINENO[0]} check_theory_seen } function typechecker { # typechecker header lineno=${BASH_LINENO[0]} check_theory_seen } function typerule { # typerule OPERATOR typechecking-class lineno=${BASH_LINENO[0]} check_theory_seen } function construle { # construle OPERATOR isconst-checking-class lineno=${BASH_LINENO[0]} check_theory_seen } function rewriter { # rewriter class header lineno=${BASH_LINENO[0]} check_theory_seen } function sort { # sort TYPE cardinality [well-founded ground-term header | not-well-founded] ["comment"] lineno=${BASH_LINENO[0]} check_theory_seen } function cardinality { # cardinality TYPE cardinality-computer [header] lineno=${BASH_LINENO[0]} check_theory_seen } function well-founded { # well-founded TYPE wellfoundedness-computer [header] lineno=${BASH_LINENO[0]} check_theory_seen } function variable { # variable K ["comment"] lineno=${BASH_LINENO[0]} check_theory_seen register_metakind VARIABLE "$1" 0 } function operator { # operator K #children ["comment"] lineno=${BASH_LINENO[0]} check_theory_seen register_metakind OPERATOR "$1" "$2" } function parameterized { # parameterized K1 K2 #children ["comment"] lineno=${BASH_LINENO[0]} check_theory_seen register_metakind PARAMETERIZED "$1" "$3" registerOperatorToKind "$1" "$2" } function constant { # constant K T Hasher header ["comment"] lineno=${BASH_LINENO[0]} check_theory_seen if ! expr "$2" : '\(::*\)' >/dev/null; then if ! primitive_type "$2"; then # if there's an embedded space, we're probably doing something # tricky to specify the CONST payload, like "int const*"; in any # case, this warning gives too many false positives, so disable it if ! expr "$2" : '..* ..*' >/dev/null; then echo "$kf:$lineno: warning: constant $1 class \`$2' isn't fully-qualified (e.g., ::CVC4::Rational)" >&2 fi fi fi if ! expr "$3" : '\(::*\)' >/dev/null; then echo "$kf:$lineno: warning: constant $1 hasher \`$3' isn't fully-qualified (e.g., ::CVC4::RationalHashFunction)" >&2 fi if [ -n "$4" ]; then metakind_includes="${metakind_includes} #include \"$4\"" fi register_metakind CONSTANT "$1" 0 metakind_constantMaps="${metakind_constantMaps} }/* CVC4::kind::metakind namespace */ }/* CVC4::kind namespace */ namespace expr { // The reinterpret_cast of d_children to \"$2 const*\" // flags a \"strict aliasing\" warning; it's okay, because we never access // the embedded constant as a NodeValue* child, and never access an embedded // NodeValue* child as a constant. #pragma GCC diagnostic ignored \"-Wstrict-aliasing\" template <> inline $2 const& NodeValue::getConst< $2 >() const { AssertArgument(getKind() == ::CVC4::kind::$1, *this, \"Improper kind for getConst<$2>()\"); // To support non-inlined CONSTANT-kinded NodeValues (those that are // \"constructed\" when initially checking them against the NodeManager // pool), we must check d_nchildren here. return d_nchildren == 0 ? *reinterpret_cast< $2 const* >(d_children) : *reinterpret_cast< $2 const* >(d_children[0]); } // re-enable the warning #pragma GCC diagnostic warning \"-Wstrict-aliasing\" }/* CVC4::expr namespace */ namespace kind { namespace metakind { template <> struct ConstantMap< $2 > { // typedef $theory_class OwningTheory; enum { kind = ::CVC4::kind::$1 }; };/* ConstantMap< $2 > */ template <> struct ConstantMapReverse< ::CVC4::kind::$1 > { typedef $2 T; };/* ConstantMapReverse< ::CVC4::kind::$1 > */ " metakind_compares="${metakind_compares} case kind::$1: return NodeValueConstCompare< kind::$1, pool >::compare(nv1, nv2); " metakind_constHashes="${metakind_constHashes} case kind::$1: #line $lineno \"$kf\" return $3()(nv->getConst< $2 >()); " metakind_constPrinters="${metakind_constPrinters} case kind::$1: #line $lineno \"$kf\" out << nv->getConst< $2 >(); break; " cname=`echo "$2" | awk 'BEGIN {FS="::"} {print$NF}'` metakind_constDeleters="${metakind_constDeleters} case kind::$1: #line $lineno \"$kf\" std::allocator< $2 >().destroy(reinterpret_cast< $2* >(nv->d_children)); break; " } function registerOperatorToKind { operatorKind=$1 applyKind=$2 metakind_operatorKinds="${metakind_operatorKinds} case kind::$applyKind: return kind::$operatorKind; "; } function register_metakind { mk=$1 k=$2 nc=$3 metakind_kinds="${metakind_kinds} metakind::$mk, /* $k */ "; # figure out the range given by $nc if expr "$nc" : '[0-9][0-9]*$' >/dev/null; then lb=$nc ub=$nc elif expr "$nc" : '[0-9][0-9]*:$' >/dev/null; then let `echo "$nc" | awk 'BEGIN{FS=":"}{print"lb="$1}'` ub=MAX_CHILDREN elif expr "$nc" : '[0-9][0-9]*:[0-9][0-9]*$' >/dev/null; then let `echo "$nc" | awk 'BEGIN{FS=":"}{print"lb="$1" ub="$2}'` if [ $ub -lt $lb ]; then echo "$kf:$lineno: error in range \`$nc': LB < UB (in definition of $k)" >&2 exit 1 fi else echo "$kf:$lineno: can't parse range \`$nc' in definition of $k" >&2 exit 1 fi metakind_lbchildren="${metakind_lbchildren} $lb, /* $k */" metakind_ubchildren="${metakind_ubchildren} $ub, /* $k */" } # Returns 0 if arg is a primitive C++ type, or a pointer to same; 1 # otherwise. Really all this does is check whether we should issue a # "not fully qualified" warning or not. function primitive_type { strip=`expr "$1" : ' *\(.*\)\* *'` if [ -n "$strip" ]; then primitive_type "$strip" >&2 return $? fi case "$1" in bool|int|size_t|long|void|char|float|double) return 0;; *) return 1;; esac } function check_theory_seen { if $seen_endtheory; then echo "$kf:$lineno: error: command after \"endtheory\" declaration (endtheory has to be last)" >&2 exit 1 fi if ! $seen_theory; then echo "$kf:$lineno: error: no \"theory\" declaration found (it has to be first)" >&2 exit 1 fi } function check_builtin_theory_seen { if ! $seen_theory_builtin; then echo "$me: warning: no declaration for the builtin theory found" >&2 fi } while [ $# -gt 0 ]; do kf=$1 seen_theory=false seen_endtheory=false b=$(basename $(dirname "$kf")) metakind_kinds="${metakind_kinds} /* from $b */ " metakind_operatorKinds="${metakind_operatorKinds} /* from $b */ " source "$kf" if ! $seen_theory; then echo "$kf: error: no theory content found in file!" >&2 exit 1 fi if ! $seen_endtheory; then echo "$kf:$lineno: error: no \"endtheory\" declaration found (it is required at the end)" >&2 exit 1 fi shift done check_builtin_theory_seen ## output # generate warnings about incorrect #line annotations in templates nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 if [ -z "$theory_alternate_doc" ]; then theory_alternate_doc="[none defined]" fi text=$(cat "$template") for var in \ metakind_includes \ metakind_kinds \ metakind_constantMaps \ metakind_compares \ metakind_constHashes \ metakind_constPrinters \ metakind_constDeleters \ metakind_ubchildren \ metakind_lbchildren \ metakind_operatorKinds \ use_theory_validations \ theory_alternate_doc \ template \ ; do eval text="\${text//\\\$\\{$var\\}/\${$var}}" done error=`expr "$text" : '.*\${\([^}]*\)}.*'` if [ -n "$error" ]; then echo "$template:0: error: undefined replacement \${$error}" >&2 exit 1 fi echo "$text"