// Written in the D programming language. /**Functions that manipulate other functions. This module provides functions for compile time function composition. Thesefunctions are helpful when constructing predicates for the algorithms in$(MREF std, algorithm) or $(MREF std, range). $(SCRIPT inhibitQuickIndex = 1;)$(DIVC quickindex,$(BOOKTABLE ,$(TR $(TH Function Name) $(TH Description)) $(TR $(TD $(LREF adjoin)) $(TD Joins a couple of functions into one that executes the original functions independently and returns a tuple with all the results. )) $(TR $(TD $(LREF bind)) $(TD Passes the fields of a struct as arguments to a function. )) $(TR $(TD $(LREF compose), $(LREF pipe)) $(TD Join a couple of functions into one that executes the original functions one after the other, using one function's result for the next function's argument. )) $(TR $(TD $(LREF ctEval)) $(TD Enforces the evaluation of an expression during compile-time. )) $(TR $(TD $(LREF curry)) $(TD Converts a multi-argument function into a series of single-argument functions. `f(x, y) == curry(f)(x)(y)` )) $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo)) $(TD Ready-made predicate functions to compare two values. )) $(TR $(TD $(LREF memoize)) $(TD Creates a function that caches its result for fast re-evaluation. )) $(TR $(TD $(LREF not)) $(TD Creates a function that negates another. )) $(TR $(TD $(LREF partial)) $(TD Creates a function that binds the first argument of a given function to a given value. )) $(TR $(TD $(LREF reverseArgs)) $(TD Predicate that reverses the order of its arguments. )) $(TR $(TD $(LREF toDelegate)) $(TD Converts a callable to a delegate. )) $(TR $(TD $(LREF unaryFun), $(LREF binaryFun)) $(TD Create a unary or binary function from a string. Most often used when defining algorithms on ranges. )))) Copyright: Copyright Andrei Alexandrescu 2008 - 2009.License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).Authors: $(HTTP erdani.org, Andrei Alexandrescu)Source: $(PHOBOSSRC std/functional.d)*//* Copyright Andrei Alexandrescu 2008 - 2009.Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)*/module std.functional; import std.meta : AliasSeq, Reverse;import std.traits : isCallable, Parameters;import std.conv : toCtString; import std.internal.attributes : betterC; public import core.lifetime : forward; private template needOpCallAlias(alias fun){ /* Determine whether or not unaryFun and binaryFun need to alias to fun or * fun.opCall. Basically, fun is a function object if fun(...) compiles. We * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is * any function object. There are 4 possible cases: * * 1) fun is the type of a function object with static opCall; * 2) fun is an instance of a function object with static opCall; * 3) fun is the type of a function object with non-static opCall; * 4) fun is an instance of a function object with non-static opCall. * * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun * aliases itself to fun, because typeof(fun) is an error when fun itself * is a type. So it must be aliased to fun.opCall instead. All other cases * should be aliased to fun directly. */ static if (is(typeof(fun.opCall) == function)) { enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { return fun(Parameters!fun.init); }); } else enum needOpCallAlias = false;} /**Transforms a `string` representing an expression into a unaryfunction. The `string` must either use symbol name `a` asthe parameter or provide the symbol via the `parmName` argument. Params: fun = a `string` or a callable parmName = the name of the parameter if `fun` is a string. Defaults to `"a"`.Returns: If `fun` is a `string`, a new single parameter function If `fun` is not a `string`, an alias to `fun`.*/template unaryFun(alias fun, string parmName = "a"){ static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchUnary(parmName)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto unaryFun(ElementType)(auto ref ElementType __a) { mixin("alias " ~ parmName ~ " = __a ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias unaryFun = fun.opCall; } else { alias unaryFun = fun; }} ///@safe unittest{ // Strings are compiled into functions: alias isEven = unaryFun!("(a & 1) == 0"); assert(isEven(2) && !isEven(1));} @safe unittest{ static int f1(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f1)(1)) == int)); assert(unaryFun!(f1)(41) == 42); int f2(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f2)(1)) == int)); assert(unaryFun!(f2)(41) == 42); assert(unaryFun!("a + 1")(41) == 42); //assert(unaryFun!("return a + 1;")(41) == 42); int num = 41; assert(unaryFun!"a + 1"(num) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int n) { return true; } } static assert(needOpCallAlias!Seen); static assert(is(typeof(unaryFun!Seen(1)))); assert(unaryFun!Seen(1)); Seen s; static assert(!needOpCallAlias!s); static assert(is(typeof(unaryFun!s(1)))); assert(unaryFun!s(1)); struct FuncObj { bool opCall(int n) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(unaryFun!fo))); assert(unaryFun!fo(1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(unaryFun!FuncObj)));} /**Transforms a `string` representing an expression into a binary function. The`string` must either use symbol names `a` and `b` as the parameters orprovide the symbols via the `parm1Name` and `parm2Name` arguments. Params: fun = a `string` or a callable parm1Name = the name of the first parameter if `fun` is a string. Defaults to `"a"`. parm2Name = the name of the second parameter if `fun` is a string. Defaults to `"b"`.Returns: If `fun` is not a string, `binaryFun` aliases itself away to `fun`.*/template binaryFun(alias fun, string parm1Name = "a", string parm2Name = "b"){ static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto binaryFun(ElementType1, ElementType2) (auto ref ElementType1 __a, auto ref ElementType2 __b) { mixin("alias "~parm1Name~" = __a ;"); mixin("alias "~parm2Name~" = __b ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias binaryFun = fun.opCall; } else { alias binaryFun = fun; }} ///@safe unittest{ alias less = binaryFun!("a < b"); assert(less(1, 2) && !less(2, 1)); alias greater = binaryFun!("a > b"); assert(!greater("1", "2") && greater("2", "1"));} @safe unittest{ static int f1(int a, string b) { return a + 1; } static assert(is(typeof(binaryFun!(f1)(1, "2")) == int)); assert(binaryFun!(f1)(41, "a") == 42); string f2(int a, string b) { return b ~ "2"; } static assert(is(typeof(binaryFun!(f2)(1, "1")) == string)); assert(binaryFun!(f2)(1, "4") == "42"); assert(binaryFun!("a + b")(41, 1) == 42); //@@BUG //assert(binaryFun!("return a + b;")(41, 1) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int x, int y) { return true; } } static assert(is(typeof(binaryFun!Seen))); assert(binaryFun!Seen(1,1)); struct FuncObj { bool opCall(int x, int y) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(binaryFun!fo))); assert(unaryFun!fo(1,1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(binaryFun!FuncObj)));} // skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'.private uint _ctfeSkipOp(ref string op){ if (!__ctfe) assert(false); import std.ascii : isASCII, isAlphaNum; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) op = op[1..$]; else break; } return oldLength != op.length;} // skip all digitsprivate uint _ctfeSkipInteger(ref string op){ if (!__ctfe) assert(false); import std.ascii : isDigit; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isDigit()) op = op[1..$]; else break; } return oldLength != op.length;} // skip nameprivate uint _ctfeSkipName(ref string op, string name){ if (!__ctfe) assert(false); if (op.length >= name.length && op[0 .. name.length] == name) { op = op[name.length..$]; return 1; } return 0;} // returns 1 if `fun` is trivial unary functionprivate uint _ctfeMatchUnary(string fun, string name){ if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0;} @safe unittest{ static assert(!_ctfeMatchUnary("sqrt(ё)", "ё")); static assert(!_ctfeMatchUnary("ё.sqrt", "ё")); static assert(!_ctfeMatchUnary(".ё+ё", "ё")); static assert(!_ctfeMatchUnary("_ё+ё", "ё")); static assert(!_ctfeMatchUnary("ёё", "ё")); static assert(_ctfeMatchUnary("a+a", "a")); static assert(_ctfeMatchUnary("a + 10", "a")); static assert(_ctfeMatchUnary("4 == a", "a")); static assert(_ctfeMatchUnary("2 == a", "a")); static assert(_ctfeMatchUnary("1 != a", "a")); static assert(_ctfeMatchUnary("a != 4", "a")); static assert(_ctfeMatchUnary("a< 1", "a")); static assert(_ctfeMatchUnary("434 < a", "a")); static assert(_ctfeMatchUnary("132 > a", "a")); static assert(_ctfeMatchUnary("123 >a", "a")); static assert(_ctfeMatchUnary("a>82", "a")); static assert(_ctfeMatchUnary("ё>82", "ё")); static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё")); static assert(_ctfeMatchUnary("ё[21]", "ё"));} // returns 1 if `fun` is trivial binary functionprivate uint _ctfeMatchBinary(string fun, string name1, string name2){ if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0;} @safe unittest{ static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b")); static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b")); static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("ёё", "ё", "b")); static assert(_ctfeMatchBinary("a+a", "a", "b")); static assert(_ctfeMatchBinary("a + 10", "a", "b")); static assert(_ctfeMatchBinary("4 == a", "a", "b")); static assert(_ctfeMatchBinary("2 == a", "a", "b")); static assert(_ctfeMatchBinary("1 != a", "a", "b")); static assert(_ctfeMatchBinary("a != 4", "a", "b")); static assert(_ctfeMatchBinary("a< 1", "a", "b")); static assert(_ctfeMatchBinary("434 < a", "a", "b")); static assert(_ctfeMatchBinary("132 > a", "a", "b")); static assert(_ctfeMatchBinary("123 >a", "a", "b")); static assert(_ctfeMatchBinary("a>82", "a", "b")); static assert(_ctfeMatchBinary("ё>82", "ё", "q")); static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q")); static assert(_ctfeMatchBinary("ё[21]", "ё", "q")); static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё")); static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё")); static assert(!_ctfeMatchBinary(".ё+b", "b", "ё")); static assert(!_ctfeMatchBinary("_b+ё", "b", "ё")); static assert(!_ctfeMatchBinary("ba", "b", "a")); static assert(_ctfeMatchBinary("a+b", "b", "a")); static assert(_ctfeMatchBinary("a + b", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b != a", "b", "a")); static assert(_ctfeMatchBinary("a != b", "b", "a")); static assert(_ctfeMatchBinary("a< b", "b", "a")); static assert(_ctfeMatchBinary("b < a", "b", "a")); static assert(_ctfeMatchBinary("b > a", "b", "a")); static assert(_ctfeMatchBinary("b >a", "b", "a")); static assert(_ctfeMatchBinary("a>b", "b", "a")); static assert(_ctfeMatchBinary("ё>b", "b", "ё")); static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё")); static assert(_ctfeMatchBinary("ё[-21]", "b", "ё"));} //undocumentedtemplate safeOp(string S)if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!="){ import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure if (isIntegral!ElementType1 && isIntegral!ElementType2) { import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); return mixin("cast(T)a "~S~" cast(T) b"); } bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b) { import std.traits : mostNegative; static if (isIntegral!T0 && isIntegral!T1 && (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { static if (S == "<=" || S == "<") { static if (mostNegative!T0 < 0) immutable result = a < 0 || unsafeOp(a, b); else immutable result = b >= 0 && unsafeOp(a, b); } else { static if (mostNegative!T0 < 0) immutable result = a >= 0 && unsafeOp(a, b); else immutable result = b < 0 || unsafeOp(a, b); } } else { static assert(is(typeof(mixin("a "~S~" b"))), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); immutable result = mixin("a "~S~" b"); } return result; }} @safe unittest //check user defined types{ import std.algorithm.comparison : equal; struct Foo { int a; auto opEquals(Foo foo) { return a == foo.a; } }Lines 1–500 of 2226. Load more
// Written in the D programming language. /** Functions that manipulate other functions. This module provides functions for compile time function composition. These functions are helpful when constructing predicates for the algorithms in $(MREF std, algorithm) or $(MREF std, range). $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) $(TR $(TD $(LREF adjoin)) $(TD Joins a couple of functions into one that executes the original functions independently and returns a tuple with all the results. )) $(TR $(TD $(LREF bind)) $(TD Passes the fields of a struct as arguments to a function. )) $(TR $(TD $(LREF compose), $(LREF pipe)) $(TD Join a couple of functions into one that executes the original functions one after the other, using one function's result for the next function's argument. )) $(TR $(TD $(LREF ctEval)) $(TD Enforces the evaluation of an expression during compile-time. )) $(TR $(TD $(LREF curry)) $(TD Converts a multi-argument function into a series of single-argument functions. `f(x, y) == curry(f)(x)(y)` )) $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo)) $(TD Ready-made predicate functions to compare two values. )) $(TR $(TD $(LREF memoize)) $(TD Creates a function that caches its result for fast re-evaluation. )) $(TR $(TD $(LREF not)) $(TD Creates a function that negates another. )) $(TR $(TD $(LREF partial)) $(TD Creates a function that binds the first argument of a given function to a given value. )) $(TR $(TD $(LREF reverseArgs)) $(TD Predicate that reverses the order of its arguments. )) $(TR $(TD $(LREF toDelegate)) $(TD Converts a callable to a delegate. )) $(TR $(TD $(LREF unaryFun), $(LREF binaryFun)) $(TD Create a unary or binary function from a string. Most often used when defining algorithms on ranges. )) )) Copyright: Copyright Andrei Alexandrescu 2008 - 2009. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) Source: $(PHOBOSSRC std/functional.d) */ /* Copyright Andrei Alexandrescu 2008 - 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ module std.functional; import std.meta : AliasSeq, Reverse; import std.traits : isCallable, Parameters; import std.conv : toCtString; import std.internal.attributes : betterC; public import core.lifetime : forward; private template needOpCallAlias(alias fun) { /* Determine whether or not unaryFun and binaryFun need to alias to fun or * fun.opCall. Basically, fun is a function object if fun(...) compiles. We * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is * any function object. There are 4 possible cases: * * 1) fun is the type of a function object with static opCall; * 2) fun is an instance of a function object with static opCall; * 3) fun is the type of a function object with non-static opCall; * 4) fun is an instance of a function object with non-static opCall. * * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun * aliases itself to fun, because typeof(fun) is an error when fun itself * is a type. So it must be aliased to fun.opCall instead. All other cases * should be aliased to fun directly. */ static if (is(typeof(fun.opCall) == function)) { enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { return fun(Parameters!fun.init); }); } else enum needOpCallAlias = false; } /** Transforms a `string` representing an expression into a unary function. The `string` must either use symbol name `a` as the parameter or provide the symbol via the `parmName` argument. Params: fun = a `string` or a callable parmName = the name of the parameter if `fun` is a string. Defaults to `"a"`. Returns: If `fun` is a `string`, a new single parameter function If `fun` is not a `string`, an alias to `fun`. */ template unaryFun(alias fun, string parmName = "a") { static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchUnary(parmName)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto unaryFun(ElementType)(auto ref ElementType __a) { mixin("alias " ~ parmName ~ " = __a ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias unaryFun = fun.opCall; } else { alias unaryFun = fun; } } /// @safe unittest { // Strings are compiled into functions: alias isEven = unaryFun!("(a & 1) == 0"); assert(isEven(2) && !isEven(1)); } @safe unittest { static int f1(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f1)(1)) == int)); assert(unaryFun!(f1)(41) == 42); int f2(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f2)(1)) == int)); assert(unaryFun!(f2)(41) == 42); assert(unaryFun!("a + 1")(41) == 42); //assert(unaryFun!("return a + 1;")(41) == 42); int num = 41; assert(unaryFun!"a + 1"(num) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int n) { return true; } } static assert(needOpCallAlias!Seen); static assert(is(typeof(unaryFun!Seen(1)))); assert(unaryFun!Seen(1)); Seen s; static assert(!needOpCallAlias!s); static assert(is(typeof(unaryFun!s(1)))); assert(unaryFun!s(1)); struct FuncObj { bool opCall(int n) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(unaryFun!fo))); assert(unaryFun!fo(1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(unaryFun!FuncObj))); } /** Transforms a `string` representing an expression into a binary function. The `string` must either use symbol names `a` and `b` as the parameters or provide the symbols via the `parm1Name` and `parm2Name` arguments. Params: fun = a `string` or a callable parm1Name = the name of the first parameter if `fun` is a string. Defaults to `"a"`. parm2Name = the name of the second parameter if `fun` is a string. Defaults to `"b"`. Returns: If `fun` is not a string, `binaryFun` aliases itself away to `fun`. */ template binaryFun(alias fun, string parm1Name = "a", string parm2Name = "b") { static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto binaryFun(ElementType1, ElementType2) (auto ref ElementType1 __a, auto ref ElementType2 __b) { mixin("alias "~parm1Name~" = __a ;"); mixin("alias "~parm2Name~" = __b ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias binaryFun = fun.opCall; } else { alias binaryFun = fun; } } /// @safe unittest { alias less = binaryFun!("a < b"); assert(less(1, 2) && !less(2, 1)); alias greater = binaryFun!("a > b"); assert(!greater("1", "2") && greater("2", "1")); } @safe unittest { static int f1(int a, string b) { return a + 1; } static assert(is(typeof(binaryFun!(f1)(1, "2")) == int)); assert(binaryFun!(f1)(41, "a") == 42); string f2(int a, string b) { return b ~ "2"; } static assert(is(typeof(binaryFun!(f2)(1, "1")) == string)); assert(binaryFun!(f2)(1, "4") == "42"); assert(binaryFun!("a + b")(41, 1) == 42); //@@BUG //assert(binaryFun!("return a + b;")(41, 1) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int x, int y) { return true; } } static assert(is(typeof(binaryFun!Seen))); assert(binaryFun!Seen(1,1)); struct FuncObj { bool opCall(int x, int y) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(binaryFun!fo))); assert(unaryFun!fo(1,1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(binaryFun!FuncObj))); } // skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'. private uint _ctfeSkipOp(ref string op) { if (!__ctfe) assert(false); import std.ascii : isASCII, isAlphaNum; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) op = op[1..$]; else break; } return oldLength != op.length; } // skip all digits private uint _ctfeSkipInteger(ref string op) { if (!__ctfe) assert(false); import std.ascii : isDigit; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isDigit()) op = op[1..$]; else break; } return oldLength != op.length; } // skip name private uint _ctfeSkipName(ref string op, string name) { if (!__ctfe) assert(false); if (op.length >= name.length && op[0 .. name.length] == name) { op = op[name.length..$]; return 1; } return 0; } // returns 1 if `fun` is trivial unary function private uint _ctfeMatchUnary(string fun, string name) { if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0; } @safe unittest { static assert(!_ctfeMatchUnary("sqrt(ё)", "ё")); static assert(!_ctfeMatchUnary("ё.sqrt", "ё")); static assert(!_ctfeMatchUnary(".ё+ё", "ё")); static assert(!_ctfeMatchUnary("_ё+ё", "ё")); static assert(!_ctfeMatchUnary("ёё", "ё")); static assert(_ctfeMatchUnary("a+a", "a")); static assert(_ctfeMatchUnary("a + 10", "a")); static assert(_ctfeMatchUnary("4 == a", "a")); static assert(_ctfeMatchUnary("2 == a", "a")); static assert(_ctfeMatchUnary("1 != a", "a")); static assert(_ctfeMatchUnary("a != 4", "a")); static assert(_ctfeMatchUnary("a< 1", "a")); static assert(_ctfeMatchUnary("434 < a", "a")); static assert(_ctfeMatchUnary("132 > a", "a")); static assert(_ctfeMatchUnary("123 >a", "a")); static assert(_ctfeMatchUnary("a>82", "a")); static assert(_ctfeMatchUnary("ё>82", "ё")); static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё")); static assert(_ctfeMatchUnary("ё[21]", "ё")); } // returns 1 if `fun` is trivial binary function private uint _ctfeMatchBinary(string fun, string name1, string name2) { if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0; } @safe unittest { static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b")); static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b")); static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("ёё", "ё", "b")); static assert(_ctfeMatchBinary("a+a", "a", "b")); static assert(_ctfeMatchBinary("a + 10", "a", "b")); static assert(_ctfeMatchBinary("4 == a", "a", "b")); static assert(_ctfeMatchBinary("2 == a", "a", "b")); static assert(_ctfeMatchBinary("1 != a", "a", "b")); static assert(_ctfeMatchBinary("a != 4", "a", "b")); static assert(_ctfeMatchBinary("a< 1", "a", "b")); static assert(_ctfeMatchBinary("434 < a", "a", "b")); static assert(_ctfeMatchBinary("132 > a", "a", "b")); static assert(_ctfeMatchBinary("123 >a", "a", "b")); static assert(_ctfeMatchBinary("a>82", "a", "b")); static assert(_ctfeMatchBinary("ё>82", "ё", "q")); static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q")); static assert(_ctfeMatchBinary("ё[21]", "ё", "q")); static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё")); static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё")); static assert(!_ctfeMatchBinary(".ё+b", "b", "ё")); static assert(!_ctfeMatchBinary("_b+ё", "b", "ё")); static assert(!_ctfeMatchBinary("ba", "b", "a")); static assert(_ctfeMatchBinary("a+b", "b", "a")); static assert(_ctfeMatchBinary("a + b", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b != a", "b", "a")); static assert(_ctfeMatchBinary("a != b", "b", "a")); static assert(_ctfeMatchBinary("a< b", "b", "a")); static assert(_ctfeMatchBinary("b < a", "b", "a")); static assert(_ctfeMatchBinary("b > a", "b", "a")); static assert(_ctfeMatchBinary("b >a", "b", "a")); static assert(_ctfeMatchBinary("a>b", "b", "a")); static assert(_ctfeMatchBinary("ё>b", "b", "ё")); static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё")); static assert(_ctfeMatchBinary("ё[-21]", "b", "ё")); } //undocumented template safeOp(string S) if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") { import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure if (isIntegral!ElementType1 && isIntegral!ElementType2) { import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); return mixin("cast(T)a "~S~" cast(T) b"); } bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b) { import std.traits : mostNegative; static if (isIntegral!T0 && isIntegral!T1 && (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { static if (S == "<=" || S == "<") { static if (mostNegative!T0 < 0) immutable result = a < 0 || unsafeOp(a, b); else immutable result = b >= 0 && unsafeOp(a, b); } else { static if (mostNegative!T0 < 0) immutable result = a >= 0 && unsafeOp(a, b); else immutable result = b < 0 || unsafeOp(a, b); } } else { static assert(is(typeof(mixin("a "~S~" b"))), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); immutable result = mixin("a "~S~" b"); } return result; } } @safe unittest //check user defined types { import std.algorithm.comparison : equal; struct Foo { int a; auto opEquals(Foo foo) { return a == foo.a; } }