#include "alfe/main.h" #ifndef INCLUDED_FUNCTION_H #define INCLUDED_FUNCTION_H #include "alfe/type.h" #include "alfe/identifier.h" #include "alfe/concrete.h" // This is not a real type - we can't do anything with it. A funco does not // in general have a type (or even a tyco, because we can't do kind checking). // However, OverloadedFunctionSet needs to be wrapped in a Value and placed in // a symbol table (e.g. in ConfigFile), so this is the stub type for that. template class FuncoTypeT : public NamedNullary> { public: static String name() { return "@FunctionConstructor"; } }; template class FuncoT; typedef FuncoT Funco; template class FuncoT : public Handle { protected: class Body : public Handle::Body { public: virtual FunctionType type() const { return FuncoType(); }; virtual Value evaluate(List arguments, Span span) const = 0; virtual Identifier identifier() const = 0; virtual bool argumentsMatch(List argumentTypes) const = 0; virtual int compareTo(Funco other) const { List fTycos = parameterTycos(); List bTycos = other.parameterTycos(); assert(fTycos.count() == bTycos.count()); auto bIterator = bTycos.begin(); int r = 0; for (auto fTyco : fTycos) { Tyco bTyco = *bIterator; Type fType = fTyco; Type bType = bTyco; if (fType.valid() && bType.valid()) { if (!fType.canConvertTo(bType)) r |= 2; if (!bType.canConvertTo(fType)) r |= 1; } else { // This is enough for Berapa's built-in concrete functions, // but eventually we'll want to generalize this. if (bTyco == ConcreteTyco() && (fTyco == IntegerType() || fTyco == RationalType())) r |= 1; if (fTyco == ConcreteTyco() && (bTyco == IntegerType() || bTyco == RationalType())) r |= 2; } ++bIterator; } return r; } virtual List parameterTycos() const = 0; virtual String toString() const { return type().toString(); } }; const Body* body() const { return as(); } public: FuncoT() { } FuncoT(const Handle& other) : Handle(other) { } Value evaluate(List arguments, Span span) const { return body()->evaluate(arguments, span); } Identifier identifier() const { return body()->identifier(); } bool argumentsMatch(List argumentTypes) const { return body()->argumentsMatch(argumentTypes); } int compareTo(Funco other) const { return body()->compareTo(other); } String toString() const { return body()->toString(); } FunctionType type() const { return body()->type(); } List parameterTycos() const { return body()->parameterTycos(); } }; class Function : public Funco { public: Function(const Handle& other) : Funco(other) { } Function(const Funco& other) : Funco(to(other)) { } protected: class Body : public Funco::Body { public: bool argumentsMatch(List argumentTypes) const { return type().argumentsMatch(argumentTypes.begin()); } List parameterTycos() const { List tycos; type().addParameterTycos(&tycos); return tycos; } }; }; template class OverloadedFunctionSetT : public Handle { class Body : public Handle::Body { public: Body(Identifier identifier) : _identifier(identifier) { } void add(Funco funco) { _funcos.add(funco); } Value evaluate(List arguments, Span span) const { List<::Type> argumentTypes; for (auto i : arguments) argumentTypes.add(i.type()); List bestCandidates; for (auto f : _funcos) { if (!f.argumentsMatch(argumentTypes)) continue; List newBestCandidates; bool newBest = true; for (auto b : bestCandidates) { int r = f.compareTo(b); if (r == 2) { // b better than f newBest = false; break; } if (r != 1) newBestCandidates.add(b); } if (newBest) { bestCandidates = newBestCandidates; bestCandidates.add(f); } } for (auto f : bestCandidates) { for (auto b : bestCandidates) { int r = f.compareTo(b); if (r == 3) { span.throwError("Ambiguous function call of " + _identifier.toString() + " with argument types " + argumentTypesString(argumentTypes) + ". Could be " + b.toString() + " or " + f.toString() + "."); } } } if (bestCandidates.count() == 0) { span.throwError("No matches for function " + _identifier.toString() + " with argument types " + argumentTypesString(argumentTypes) + "."); } // We have a choice of possible funcos here. Logically they should // be equivalent, but some may be more optimal. For now we'll just // choose the first one, but later we may want to try to figure out // which one is most optimal. auto i = *bestCandidates.begin(); List convertedArguments; if (Function(i).valid()) { List parameterTycos = i.parameterTycos(); auto ii = parameterTycos.begin(); for (auto a : arguments) { Type type = *ii; if (!type.valid()) { a.span().throwError("Function parameter's type " "constructor is not a type."); } convertedArguments.add(a.convertTo(type)); ++ii; } } else { // Funcos that are not functions don't get their arguments // converted. convertedArguments = arguments; } return i.evaluate(convertedArguments, span); } private: String argumentTypesString(List<::Type> argumentTypes) const { String s; bool needComma = false; for (auto t : argumentTypes) { if (needComma) s += ", "; needComma = true; s += t.toString(); } return s; } List _funcos; Identifier _identifier; }; Body* body() { return as(); } const Body* body() const { return as(); } public: OverloadedFunctionSetT(Identifier identifier) : Handle(create(identifier)) { } void add(Funco funco) { body()->add(funco); } static Type type() { return FuncoType(); } Value evaluate(List arguments, Span span) const { return body()->evaluate(arguments, span); } }; #endif // INCLUDED_FUNCTION_H