1 /** 2 * Core inflection 3 * 4 * Copyright: © 2015 David Monagle 5 * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 * Authors: David Monagle 7 */ 8 module inflections.Inflector; 9 10 import std.regex; 11 import std.array; 12 import std.algorithm; 13 import std.uni; 14 15 interface InflectionInterface { 16 string inflect(const string input); 17 bool matches(const string input) const; 18 } 19 20 class InflectionRule(string expression, string format, string matchOptions = "") : InflectionInterface { 21 private static auto _regex = ctRegex!(expression, matchOptions); 22 23 static InflectionInterface opCall() { 24 return cast(InflectionInterface)(new InflectionRule!(expression, format, matchOptions)); 25 } 26 27 override string inflect(const string input) { 28 return input.replaceFirst(_regex, format); 29 } 30 31 bool matches(const string input) const { 32 return cast(bool)input.matchFirst(_regex); 33 } 34 } 35 36 unittest { 37 auto testInflector = InflectionRule!(`$`, `s`, "i")(); 38 assert (testInflector.inflect("apple") == "apples"); 39 } 40 41 struct Translation { 42 string singular; 43 string plural; 44 45 bool canPlural(const string input) { 46 if (input.length < singular.length) return false; 47 if (input[$ - singular.length..$].toLower == singular) { 48 return true; 49 } 50 return false; 51 } 52 53 string toPlural(const string input) { 54 assert(input.length >= singular.length); 55 return input[0..$-singular.length] ~ plural; 56 } 57 58 bool canSingular(const string input) { 59 if (input.length < plural.length) return false; 60 if (input[$ - plural.length..$].toLower == plural) { 61 return true; 62 } 63 return false; 64 } 65 66 string toSingular(const string input) { 67 assert(input.length >= plural.length); 68 return input[0..$-plural.length] ~ singular; 69 } 70 } 71 72 unittest { 73 auto t = Translation("person", "people"); 74 assert(t.toPlural("oneperson") == "onepeople"); 75 assert(t.toSingular("onepeople") == "oneperson"); 76 } 77 78 class Inflector { 79 static InflectionInterface[] _plurals; 80 static InflectionInterface[] _singulars; 81 static Translation[] _irregulars; 82 static string[] _uncountables; 83 84 static void plural(string expression, string format, string matchOptions = "")() { 85 _plurals ~= InflectionRule!(expression, format, matchOptions)(); 86 } 87 88 static void singular(string expression, string format, string matchOptions = "")() { 89 _singulars ~= InflectionRule!(expression, format, matchOptions)(); 90 } 91 92 static void irregular(string singular, string plural) { 93 _irregulars ~= Translation(singular, plural); 94 } 95 96 static void uncountable(string[] values ...) { 97 _uncountables ~= values; 98 } 99 100 static string transform(bool plural)(const string input) { 101 static if (plural) { 102 alias _transforms = _plurals; 103 } 104 else { 105 alias _transforms = _singulars; 106 } 107 108 auto lowerInput = input.toLower; 109 110 if (_uncountables.canFind(lowerInput)) return input; 111 112 foreach(irregular; _irregulars) { 113 static if (plural) { 114 if (irregular.canPlural(input)) return irregular.toPlural(input); 115 } 116 else { 117 if (irregular.canSingular(input)) return irregular.toSingular(input); 118 } 119 } 120 121 foreach(rule; _transforms) { 122 if (rule.matches(input)) 123 return rule.inflect(input); 124 } 125 126 return input; 127 } 128 129 alias pluralize = transform!true; 130 alias singularize = transform!false; 131 }