get-noun-phrase.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * Module dependencies
  3. */
  4. var _ = require('@sailshq/lodash');
  5. var getDisplayTypeLabel = require('./get-display-type-label');
  6. var getRdt = require('./get-rdt');
  7. /**
  8. * getNounPhrase()
  9. *
  10. * Given an RTTC "display type" string (and some options)
  11. * return an appropriate human-readable noun-phrase.
  12. * Useful for error messages, user interfaces, etc.
  13. *
  14. * @required {String} type
  15. * Recognizes any of the standard RTTC types:
  16. * • string
  17. * • number
  18. * • boolean
  19. * • lamda
  20. * • dictionary
  21. * • array
  22. * • json
  23. * • ref
  24. *
  25. * (Also tolerates type schemas.)
  26. *
  27. *
  28. * @optional {Dictionary} options
  29. *
  30. * @property {Boolean} plural
  31. * If enabled, the returned noun phrase will be plural.
  32. * @default false
  33. *
  34. * @property {Boolean} completeSentence
  35. * If enabled, a complete sentence with a capital letter
  36. * & ending punctuation (a period) will be returned.
  37. * Otherwise (by default), the returned noun phrase will
  38. * be a fragment designed for use in an existing sentence.
  39. * @default false
  40. *
  41. * @property {String} determiner
  42. * One of:
  43. * • "the" (definite article)
  44. * • "a" (indefinite article)
  45. * • "any" (existential qualifier)
  46. * • "" (no determiner)
  47. * > Note that if "a" is specified, either "a" or "an" will be used,
  48. * > whichever is more appropriate.
  49. * > (for more background, see https://en.wikipedia.org/wiki/Article_(grammar)
  50. * > and/or https://en.wikipedia.org/wiki/English_determiners)
  51. * @default "a" (or "", if plural)
  52. *
  53. *
  54. * @return {String} [noun phrase]
  55. */
  56. module.exports = function getNounPhrase(type, options){
  57. if (typeof type === 'string') {
  58. // OK! This is probably an RDT, so we'll try it.
  59. } else if (!!type && typeof type === 'object') {
  60. // This might be a type schema, so we'll parsing an RDT from it first and use that.
  61. type = getRdt(type);
  62. } else {
  63. throw new Error('Usage error: rttc.getNounPhrase() expects a string display type such as '+
  64. '`dictionary` or `ref`. If you are trying to get the noun phrase for an exemplar, do '+
  65. '`rttc.getNounPhrase(rttc.inferDisplayType(exemplar))`. If you are trying to get a noun '+
  66. 'phrase to represent voidness (i.e. null exemplar), then you should determine that on a '+
  67. 'case-by-case basis-- there\'s no good sane default.');
  68. }
  69. // Set up defaults
  70. options = options || {};
  71. options = _.defaults(options, {
  72. plural: false,
  73. completeSentence: false,
  74. determiner: !options.plural ? 'a' : ''
  75. });
  76. // Tolerate "an" for "a"
  77. if (options.determiner === 'an') {
  78. options.determiner = 'a';
  79. }
  80. // Validate determiner
  81. if (!_.contains(['the', 'a', 'any', ''], options.determiner)) {
  82. throw new Error('Usage error: Unrecognized `determiner` option: `'+options.determiner+'`. '+
  83. 'Should be either "the", "a", "any", or "". (defaults to "a", or "" if plural)');
  84. }
  85. // Ensure we're not trying to use "a" in a plural noun phrase.
  86. if (options.plural && options.determiner === 'a') {
  87. throw new Error('Usage error: Cannot use that determiner ("a") to generate a plural noun phrase. '+
  88. 'Trust me, it wouldn\'t sound right.');
  89. }
  90. // Compute the display type label that will be used below.
  91. var displayTypeLabel = getDisplayTypeLabel(type, {
  92. capitalization: 'fragment',
  93. plural: options.plural
  94. });
  95. // Determine the appropriate naked noun phrase.
  96. // (i.e. with determiner, but without ending punctuation or start-sentence capitalization)
  97. var nounPhrase;
  98. if (type === 'string') {
  99. switch (options.determiner) {
  100. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  101. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  102. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  103. case '': nounPhrase = displayTypeLabel; break;
  104. }
  105. }
  106. else if (type === 'number') {
  107. switch (options.determiner) {
  108. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  109. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  110. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  111. case '': nounPhrase = displayTypeLabel; break;
  112. }
  113. }
  114. else if (type === 'boolean') {
  115. switch (options.determiner) {
  116. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  117. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  118. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  119. case '': nounPhrase = displayTypeLabel; break;
  120. }
  121. }
  122. else if (type === 'lamda') {
  123. switch (options.determiner) {
  124. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  125. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  126. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  127. case '': nounPhrase = displayTypeLabel; break;
  128. }
  129. }
  130. else if (type === 'dictionary') {
  131. switch (options.determiner) {
  132. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  133. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  134. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  135. case '': nounPhrase = displayTypeLabel; break;
  136. }
  137. }
  138. else if (type === 'array') {
  139. switch (options.determiner) {
  140. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  141. case 'a': nounPhrase = 'an '+displayTypeLabel; break;
  142. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  143. case '': nounPhrase = displayTypeLabel; break;
  144. }
  145. }
  146. else if (type === 'json') {
  147. switch (options.determiner) {
  148. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  149. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  150. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  151. case '': nounPhrase = displayTypeLabel; break;
  152. }
  153. // for future reference, this is where we could do:
  154. // > "might be a string, number, boolean, dictionary, array, or even null"
  155. }
  156. else if (type === 'ref') {
  157. switch (options.determiner) {
  158. case 'the': nounPhrase = 'the '+displayTypeLabel; break;
  159. case 'a': nounPhrase = 'a '+displayTypeLabel; break;
  160. case 'any': nounPhrase = 'any '+displayTypeLabel; break;
  161. case '': nounPhrase = displayTypeLabel; break;
  162. }
  163. }
  164. else {
  165. throw new Error('Unknown type: `'+type+'`');
  166. }
  167. // Finally, deal with sentence capitalization and ending punctuation (if relevant).
  168. if (options.completeSentence) {
  169. nounPhrase = nounPhrase[0].toUpperCase() + nounPhrase.slice(1);
  170. nounPhrase += '.';
  171. }
  172. // And return our noun phrase.
  173. return nounPhrase;
  174. };