Added Ulambda, namelist, stringsecs, and speak functions.
authornveid <nveid@cobramush.org>
Sun, 8 Apr 2007 07:22:21 +0000 (07:22 +0000)
committerAri Johnson <ari@theari.com>
Thu, 24 Mar 2011 15:58:45 +0000 (15:58 +0000)
(cherry picked from commit 9a75f746e3176d66906933be410d6f338fa0816d)

game/txt/changes/0.73
game/txt/hlp/cobra_func.hlp
hdrs/externs.h
src/function.c
src/fundb.c
src/funstr.c
src/funtime.c
src/funufun.c
win32/funs.h

index 35ff9fa27330d3174d87a0c48a617f40375c0ff4..2c7e9e71ca9016df6bab46b8b5ff8c1cf1e7c92b 100644 (file)
@@ -177,4 +177,8 @@ CobraMUSH Version 0.73
    * Width() and height() take optional second argument for defaults. [RLB]
    * Unique() removes contiguous duplicates in lists. [RLB]
    * NextDbref() now returns the next object to be created. [RLB]
+   * Added Ulambda(). [RLB]
+   * Add Speak(). [RLB]
+   * Added namelist(). [RLB]
+   * Added stringsecs(). [RLB]
 
index a9af65fe964ba9b231951c341c09dc3e01421f69..e6da2e573db514a09f1cf48699d85a6716510fdb 100644 (file)
   Dbref functions return a dbref or list of dbrefs related to some value
   on an object.
   
-  children()    con()         entrances()   exit()        followers()   
-  following()   home()        lcon()        lexits()      loc()         
-  locate()      lparent()     lplayers()    lsearch()     lvcon()       
-  lvexits()     lvplayers()   next()        num()         owner()       
-  parent()      pmatch()      rloc()        rnum()        room()        
-  where()       zone()
-  
+  children()                con()                     entrances()
+  exit()                    followers()               following()
+  home()                    lcon()                    lexits()
+  loc()                     locate()                  lparent()
+  lplayers()                lsearch()                 lvcon()
+  lvexits()                 lvplayers()               namelist()
+  next()                    num()                     owner()
+  parent()                  pmatch()                  rloc()
+  rnum()                    room()                    where()
+  zone()
+
   See also: DBREF
 & Information functions
   Information functions return values related to objects or the game.
   
   convsecs()    convutcsecs() convtime()    ctime()       etimefmt()
   isdaylight()  mtime()       restarttime() secs()        starttime()
-  time()        timefmt()     timestring()  utctime()   
+  stringsecs()  time()        timefmt()     timestring()  utctime()   
 
 & Utility functions
   These functions don't quite fit into any other category.
@@ -2544,6 +2548,15 @@ for an object named "Test", preferring a thing over other types.
   that it will continue to do this for the time being. Don't rely on it.
 
   Related functions: FULLNAME(), ACCNAME(), INAME() 
+& NAMELIST()
+  namelist(<player-list>)
+
+  Namelist takes a list of players of the form used by the page
+  command and returns a corresponding list of dbrefs.
+
+  >"[namelist(#1 Javelin "ringo spar" bogus)]
+  You say, "#1 #7 #56 #-1"
 & NAMEGRAB()
 & NAMEGRABALL()
   namegrab(<dbref list>,<name>)
@@ -3427,6 +3440,151 @@ for an object named "Test", preferring a thing over other types.
   Prints <number> number of spaces. Useful for times when you want to
   be able to use lots of spaces to separate things. For example,
   "a[space(5)]b  would print, "Amberyl says, "a     b"".
+
+& SPEAK()
+  speak(<speaker>, <string>[, <say string>
+                  [, [<transform obj>/]<transform attr>
+                  [, [<isnull obj>/]<isnull attr>[, <open>[, <close>]]]]])
+
+  This function is used to format speech-like constructs, and is
+  capable of transforming text within a speech string; it is useful for
+  implementing "language code" and the like.
+
+  When only <speaker> and <string> are given, this function formats
+  <string> as if it were speech from <speaker>, as follows.
+
+  If <string> is...  the resulting string is...
+  :<pose>            <speaker's name> <pose>
+  : <pose>           <speaker's name><pose>
+  ;<pose>            <speaker's name><pose>
+  |<emit>            <emit>
+  "<speech>          <speaker's name> says, "<speech>"
+  <speech>           <speaker's name> says, "<speech>"
+
+  If <say string> is specified, it is used instead of "says," / "says".
+
+  Continued in 'help Speak2'.
+& SPEAK2
+
+  Examples:
+
+    > say [name(me)]
+    You say, "Wizard"
+    > @emit [speak(me, :tests.)]
+    Wizard tests.
+    > @emit [speak(me, : 's testing.)]
+    Wizard's testing.
+    > @emit [speak(me, ;'s testing.)]
+    Wizard's testing.
+    > @emit [speak(me, |Test.)]
+    Test.
+    > @emit [speak(me, "Test.)]
+    Wizard says, "Test."
+    > @emit [speak(me, Test.)]
+    Wizard says, "Test."
+    > @emit [speak(me, Test., yells:)]
+    Wizard yells: "Test."
+
+  Continued in 'help Speak3'.
+
+& SPEAK3
+  If <transform> is specified (an object/attribute pair or attribute,
+  as with map() and similar functions), the speech portions of <string>
+  are passed through the transformation function.
+
+  Speech is delimited by double-quotes (i.e., "text"), or by the
+  specified <open> and <close> strings. For instance, if you wanted
+  <<text>> to denote text to be transformed, you would specify <open>
+  as << and close as >> in the function call. Only the portions of the
+  string between those delimiters are transformed. If <close> is not
+  specified, it defaults to <open>.
+
+  The transformation function receives the speech text as %0, the
+  dbref of <speaker> as %1, and the speech fragment number as %2.
+  For non-say input strings (i.e., for an original <string> beginning
+  with the :, ;, or | tokens), fragments are numbered starting with 1;
+  otherwise, fragments are numbered starting with 0. (A fragment is
+  a chunk of speech text within the overall original input string.)
+
+  Continued in 'help Speak4'.
+
+& SPEAK4
+  Examples:
+
+  > @va me = "Fragment %2 is: %0"
+  > @emit speak(me, test, ,va)
+  Wizard says, "Fragment 0 is: test"
+  > @emit speak(me, "test, ,va)
+  Wizard says, "Fragment 0 is: test"
+  > @emit speak(me, "test, yells:, va)
+  Wizard yells: "Fragment 0 is: test"
+  > @emit speak(me, :tests. "Hi.", ,va)
+  Wizard tests. "Fragment 1 is: Hi."
+  > @emit speak(me, : 's testing. "Hi.", ,va)
+  Wizard's testing. "Fragment 1 is: Hi."
+  > @emit speak(me, ;'s testing. "Hi.", ,va)
+  Wizard's testing. "Fragment 1 is: Hi."
+  > @emit speak(me, |This is a test. "Hi.", ,va)
+  This is a test. "Fragment 1 is: Hi."
+  > @emit speak(me, :tests. "Hi." And... "Bye." The end., ,va)
+  Wizard tests. "Fragment 1 is: Hi." And... "Fragment 2 is: Bye." The end.
+  > @emit speak(me, :tests. "Hi." And... <<Bye.>> The end., ,va, , <<, >>)
+  Wizard tests. "Hi." And... "Fragment 1 is: Bye." The end.
+
+  Continued in 'help Speak5'.
+& SPEAK5
+  If the result of transforming a given speech fragment is a null string,
+  and <isnull> is specified (an object/attribute pair or attribute),
+  that function is used evaluate an alternative result, with %0 as
+  the dbref of <speaker>, and %1 as the speech fragment number.
+
+  The <isnull> functionality can be useful for gracefully handling cases
+  where speech may be processed down to nothing, such as with language
+  code where no words are successfully translated.
+
+  Consider this example, where the speech string may be randomly removed:
+
+  > &MUTTER_FN me = [ifelse(rand(2),"%0",)]
+  > &NONE_FN me = [capstr(subj(%0))] mutters something.
+  > @emit speak(me, :tests. "Hello there.", mutters:, MUTTER_FN, NONE_FN)
+  Wizard tests. "Hello there."
+  OR
+  Wizard tests. He mutters something.
+
+  Continued in 'help Speak6'.
+& SPEAK6
+  Elegantly handling an empty string when the type of speech is a plain say
+  is a bit more difficult. In order to facilitate this, when the speech type
+  is a plain say, the '<speaker> says,' is only prepended to the output if
+  the transformation of the first speech fragment produces something
+  non-null. Also note that quotes are not placed around such speech
+  automatically, to allow the user's code to insert whatever is appropriate.
+
+  Below is a more elegant version of the mutter example. Here, we find
+  the use for say-speech fragments being numbered starting from 0 rather
+  than 1 -- if the speech fragment number is 0, we know we haven't
+  given any output yet.
+
+  > &MUTTER_FN me = [ifelse(rand(2),"%0",)]
+  > &NONE_FN me = [switch(%1,0,name(%0),capstr(subj(%0)))] mutters something.
+  > @emit speak(me, Hello there., mutters:, MUTTER_FN, NONE_FN)
+  Wizard mutters: "Hello there."
+  OR
+  Wizard mutters something.
+
+  Continued in 'help Speak7'.
+& SPEAK7
+  Here's another example, where words between + signs are reversed,
+  but those within double-quotes are untouched (demonstrating a technique
+  useful in something where you want to allow users to mix ordinary speech
+  with transformed speech).
+
+  > &REV_FN me = [switch(%2,0,backwards,[capstr(subj(%1))] says backwards)],
+                 "[revwords(%0)]"
+  > @emit speak(me,:tests. "Normal speech." +Mixed up speech+ Success!, ,
+                REV_FN, ,+)
+  Wizard tests. "Normal speech." He says backwards, "speech up Mixed" Success!
+
 & SPELLNUM()
   spellnum(<number>)
 
@@ -3955,6 +4113,18 @@ for an object named "Test", preferring a thing over other types.
   > say [timestring(301,2)]
   You say, "00d 00h 05m 01s"
 
+& STRINGSECS()
+  stringsecs(<timestring>)
+
+  The stringsecs() function takes a string of the form produced by
+  timestring() and converts it back into seconds.
+
+  Example:
+  > say [stringsecs(5m 1s)]
+  You say, "301"
+
+See also: timestring(), etimefmt()
+
 & TR()
   tr(<string>,<find>,<replace>)
 
index a53f6f0a983001c13d6d75cf5570ba178866a547..bdb2a6b6f32231dc393161b3a693cc0fb75c265f 100644 (file)
@@ -65,6 +65,9 @@ extern dbref short_page(const char *match);
 extern dbref visible_short_page(dbref player, const char *match);
 extern void do_doing(dbref player, const char *message);
 
+/* from funtime.c */
+extern int etime_to_secs(char *str1, int *secs);
+
 /* the following symbols are provided by game.c */
 extern void process_command(dbref player, char *command,
                            dbref cause, dbref realcause, int from_port);
index 5f1fee0d2b7758cb083ead0a34867d18c4b5e008..398f04063784043685c94c069e5e3945f20d8567 100644 (file)
@@ -497,6 +497,7 @@ FUNTAB flist[] = {
   {"NAME", fun_name, 0, 2, FN_REG},
   {"NAMEGRAB", fun_namegrab, 2, 3, FN_REG},
   {"NAMEGRABALL", fun_namegraball, 2, 3, FN_REG},
+  {"NAMELIST", fun_namelist, 1, 1, FN_REG},
   {"NAND", fun_nand, 1, INT_MAX, FN_REG},
   {"NATTR", fun_nattr, 1, 1, FN_REG},
   {"NCHILDREN", fun_lsearch, 1, 1, FN_REG},
@@ -632,6 +633,7 @@ FUNTAB flist[] = {
   {"STARTTIME", fun_starttime, 0, 0, FN_REG},
   {"STEP", fun_step, 3, 5, FN_REG},
   {"STRCAT", fun_strcat, 1, INT_MAX, FN_REG},
+  {"STRINGSECS", fun_stringsecs, 1, 1, FN_REG},
   {"STRINSERT", fun_strinsert, 3, -3, FN_REG},
   {"STRIPACCENTS", fun_stripaccents, 1, 1, FN_REG},
   {"STRIPANSI", fun_stripansi, 1, -1, FN_REG},
@@ -661,6 +663,7 @@ FUNTAB flist[] = {
   {"UCSTR", fun_ucstr, 1, -1, FN_REG},
   {"UDEFAULT", fun_uldefault, 2, 12, FN_NOPARSE},
   {"UFUN", fun_ufun, 1, 11, FN_REG},
+  {"ULAMBDA", fun_ulambda, 1, 11, FN_REG},
   {"ULDEFAULT", fun_uldefault, 1, 12, FN_NOPARSE},
   {"ULOCAL", fun_ulocal, 1, 11, FN_REG},
   {"UNIQUE", fun_unique, 1, 4, FN_REG},
index 0bec8f9a7c91fff1708946f1a8ee4f792b73f8dc..35b19711721f60acdeba63ba949f146b2d01b791 100644 (file)
@@ -1612,6 +1612,36 @@ FUNCTION(fun_pmatch)
   }
 }
 
+/* ARGUSED */
+FUNCTION(fun_namelist)
+{
+
+  int first = 1;
+  char *current;
+  dbref target;
+  const char *start;
+
+  start = args[0];
+  while (start && *start) {
+    if (!first)
+      safe_str(" ", buff, bp);
+    first = 0;
+    current = next_in_list(&start);
+    if (*current == '*')
+      current = current + 1;
+    target = lookup_player(current);
+    if (!GoodObject(target))
+      target = visible_short_page(executor, current);
+    if (target == NOTHING) {
+      safe_str("#-1", buff, bp);
+    } else if (target == AMBIGUOUS) {
+      safe_str("#-2", buff, bp);
+    } else {
+      safe_dbref(target, buff, bp);
+    }
+  }
+}
+
 /* ARGSUSED */
 FUNCTION(fun_locate)
 {
index 427d3fb28024922207ddefe2ec8309839e27802a..a47d9b652a453d7a6881b73c9df8ed049401f3b7 100644 (file)
@@ -2115,3 +2115,198 @@ FUNCTION(fun_align)
   }
   return;
 }
+
+
+FUNCTION(fun_speak)
+{
+  ufun_attrib transufun;
+  ufun_attrib nullufun;
+  dbref speaker;
+  char *speaker_str;
+  char *open, *close;
+  int transform = 0, null = 0, say = 0;
+  char *wenv[3];
+  int funccount;
+  int fragment = 0;
+  char *say_string;
+  char *string;
+  char rbuff[BUFFER_LEN];
+
+  speaker = match_thing(executor, args[0]);
+  if (speaker == NOTHING || speaker == AMBIGUOUS) {
+    safe_str(T(e_match), buff, bp);
+    return;
+  }
+  speaker_str = unparse_dbref(speaker);
+
+  if (!args[1] || !*args[1])
+    return;
+
+  string = args[1];
+
+  if (nargs > 2 && *args[2] != '\0' && *args[2] != ' ')
+    say_string = args[2];
+  else
+    say_string = (char *) "says,";
+
+  BEGINOOREF_L
+  if (nargs > 3) {
+    if (args[3] != '\0') {
+      /* we have a transform attr */
+      transform = 1;
+      if (!fetch_ufun_attrib(args[3], executor, &transufun, 1)) {
+       ENDOOREF_L
+       safe_str(T(e_atrperm), buff, bp);
+       return;
+      }
+      if (nargs > 4) {
+       if (args[4] != '\0') {
+         /* we have an attr to use when transform returns an empty string */
+         null = 1;
+         if (!fetch_ufun_attrib(args[4], executor, &nullufun, 1)) {
+           ENDOOREF_L
+           safe_str(T(e_atrperm), buff, bp);
+           return;
+         }
+       }
+      }
+    }
+  }
+
+  if (nargs < 6 || args[5] == '\0')
+    open = (char *) "\"";
+  else
+    open = args[5];
+  if (nargs < 7 || args[6] == '\0')
+    close = open;
+  else
+    close = args[6];
+
+  switch (*string) {
+  case ':':
+    safe_str(Name(speaker), buff, bp);
+    string++;
+    if (*string == ' ') {
+      /* semipose it instead */
+      while (*string == ' ')
+       string++;
+    } else
+      safe_chr(' ', buff, bp);
+    break;
+  case ';':
+    string++;
+    safe_str(Name(speaker), buff, bp);
+    if (*string == ' ') {
+      /* pose it instead */
+      safe_chr(' ', buff, bp);
+      while (*string == ' ')
+       string++;
+    }
+    break;
+  case '|':
+    string++;
+    break;
+  case '"':
+    if (CHAT_STRIP_QUOTE)
+      string++;
+  default:
+    say = 1;
+    break;
+  }
+
+  if (!transform || (!say && !strstr(string, open))) {
+    /* nice and easy */
+    if (say)
+      safe_format(buff, bp, "%s %s \"%s\"", Name(speaker), say_string, string);
+    else
+      safe_str(string, buff, bp);
+    ENDOOREF_L
+    return;
+  }
+
+
+  if (say && !strstr(string, close)) {
+    /* the whole string has to be transformed */
+    wenv[0] = string;
+    wenv[1] = speaker_str;
+    wenv[2] = unparse_integer(fragment);
+    if (call_ufun(&transufun, wenv, 3, rbuff, executor, enactor, pe_info)) {
+      ENDOOREF_L
+      return;
+    }
+    if (strlen(rbuff) > 0) {
+      safe_format(buff, bp, "%s %s %s", Name(speaker), say_string, rbuff);
+      ENDOOREF_L
+      return;
+    } else if (null == 1) {
+      wenv[0] = speaker_str;
+      wenv[1] = unparse_integer(fragment);
+      if (call_ufun(&nullufun, wenv, 2, rbuff, executor, enactor, pe_info)) {
+       ENDOOREF_L
+       return;
+      }
+      safe_str(rbuff, buff, bp);
+      ENDOOREF_L
+      return;
+    }
+  } else {
+    /* only transform portions of string between open and close */
+    char *speech;
+    int indx;
+    int finished = 0;
+    int delete = 0;
+
+    if (say) {
+      safe_str(Name(speaker), buff, bp);
+      safe_chr(' ', buff, bp);
+      safe_str(say_string, buff, bp);
+      safe_chr(' ', buff, bp);
+    }
+    funccount = pe_info->fun_invocations;
+    while (!finished && ((say && fragment == 0 && (speech = string))
+                        || (speech = strstr(string, open)))) {
+      fragment++;
+      indx = string - speech;
+      if (indx < 0)
+       indx *= -1;
+      if (string != NULL && strlen(string) > 0 && indx > 0)
+       safe_strl(string, indx, buff, bp);
+      if (!say || fragment > 1)
+       speech = speech + strlen(open); /* move past open char */
+      /* find close-char */
+      string = strstr(speech, close);
+      if (!string || !(string = string + strlen(close))) {
+       /* no close char, or nothing after it; we're at the end! */
+       finished = 1;
+      }
+      delete = (string == NULL ? strlen(speech) : strlen(speech) -
+               (strlen(string) + strlen(close)));
+      speech = chopstr(speech, delete);
+      wenv[0] = speech;
+      wenv[1] = speaker_str;
+      wenv[2] = unparse_integer(fragment);
+      if (call_ufun(&transufun, wenv, 3, rbuff, executor, enactor, pe_info))
+       break;
+      if (*bp == (buff + BUFFER_LEN - 1) &&
+         pe_info->fun_invocations == funccount)
+       break;
+      funccount = pe_info->fun_invocations;
+      if ((null == 1) && (strlen(rbuff) == 0)) {
+       wenv[0] = speaker_str;
+       wenv[1] = unparse_integer(fragment);
+       if (call_ufun(&nullufun, wenv, 2, rbuff, executor, enactor, pe_info))
+         break;
+      }
+      if (strlen(rbuff) > 0) {
+       safe_str(rbuff, buff, bp);
+      }
+      if (*bp == (buff + BUFFER_LEN - 1) &&
+         pe_info->fun_invocations == funccount)
+       break;
+    }
+    if (string != NULL && strlen(string) > 0) {
+      safe_str(string, buff, bp);      /* remaining string (not speech, so not t) */
+    }
+  }
+  ENDOOREF_L
+}
index 54c3a611d16f88e339d82384c0bc617e9f77bc06..7cac6dc2778ddc9ed4bb34fe64f57d698df3c08d 100644 (file)
@@ -205,6 +205,74 @@ FUNCTION(fun_etimefmt)
 
 }
 
+/* ARGSUSED */
+FUNCTION(fun_stringsecs)
+{
+  int secs;
+  if (etime_to_secs(args[0], &secs))
+    safe_integer(secs, buff, bp);
+  else
+    safe_str(T("#-1 INVALID TIMESTRING"), buff, bp);
+}
+
+/** Convert an elapsed time string (3d 2h 1m 10s) to seconds.
+ * \param str1 a time string.
+ * \param secs pointer to an int to fill with number of seconds.
+ * \retval 1 success.
+ * \retval 0 failure.
+ */
+int
+etime_to_secs(char *str1, int *secs)
+{
+  /* parse the result from timestring() back into a number of seconds */
+  char str2[BUFFER_LEN];
+  int i;
+
+  *secs = 0;
+  while (str1 && *str1) {
+    while (*str1 == ' ')
+      str1++;
+    i = 0;
+    while (isdigit((unsigned char) *str1)) {
+      str2[i] = *str1;
+      str1++;
+      i++;
+    }
+    if (i == 0) {
+      return 0;                        /* No numbers given */
+    }
+    str2[i] = '\0';
+    if (!*str1) {
+      *secs += parse_integer(str2);    // no more chars, just add seconds and stop
+      break;
+    }
+    switch (*str1) {
+    case 'd':
+    case 'D':
+      *secs += (parse_integer(str2) * 86400);  // days
+      break;
+    case 'h':
+    case 'H':
+      *secs += (parse_integer(str2) * 3600);   // hours
+      break;
+    case 'm':
+    case 'M':
+      *secs += (parse_integer(str2) * 60);     // minutes
+      break;
+    case 's':
+    case 'S':
+    case ' ':
+      *secs += parse_integer(str2);    // seconds
+      break;
+    default:
+      return 0;
+    }
+    str1++;                    // move past the time char
+  }
+  return 1;
+}
+
+
 /* ARGSUSED */
 FUNCTION(fun_timestring)
 {
index 5ce90fa718dbde34fc88f73074dc289de4c5bb0e..7d85ea0032dcfbe725670bf156ace7f57c219df5 100644 (file)
@@ -167,6 +167,29 @@ FUNCTION(fun_ufun)
   return;
 }
 
+/* ARGSUSED */
+FUNCTION(fun_ulambda)
+{
+  char rbuff[BUFFER_LEN];
+  ufun_attrib ufun;
+
+  BEGINOOREF_L
+
+  if (!fetch_ufun_attrib(args[0], executor, &ufun, 1)) {
+    safe_str(T(ufun.errmess), buff, bp);
+    ENDOOREF_L
+    return;
+  }
+
+  call_ufun(&ufun, args + 1, nargs - 1, rbuff, executor, enactor, pe_info);
+
+  safe_str(rbuff, buff, bp);
+
+  ENDOOREF_L
+  return;
+}
+
+
 /* ARGSUSED */
 FUNCTION(fun_ulocal)
 {
index 214306747651283509cb1d56ae17c091c8472bc2..591c3290485ee05c7ab07fa8015dff5c0dfbf0dc 100644 (file)
@@ -216,6 +216,7 @@ FUNCTION_PROTO(fun_munge);
 FUNCTION_PROTO(fun_name);
 FUNCTION_PROTO(fun_namegrab);
 FUNCTION_PROTO(fun_namegraball);
+FUNCTION_PROTO(fun_namelist);
 FUNCTION_PROTO(fun_nand);
 FUNCTION_PROTO(fun_nattr);
 FUNCTION_PROTO(fun_nearby);
@@ -310,6 +311,7 @@ FUNCTION_PROTO(fun_sortkey);
 FUNCTION_PROTO(fun_soundex);
 FUNCTION_PROTO(fun_soundlike);
 FUNCTION_PROTO(fun_space);
+FUNCTION_PROTO(fun_speak);
 FUNCTION_PROTO(fun_spellnum);
 FUNCTION_PROTO(fun_splice);
 FUNCTION_PROTO(fun_sql);
@@ -322,6 +324,7 @@ FUNCTION_PROTO(fun_starttime);
 FUNCTION_PROTO(fun_stddev);
 FUNCTION_PROTO(fun_step);
 FUNCTION_PROTO(fun_strcat);
+FUNCTION_PROTO(fun_stringsecs);
 FUNCTION_PROTO(fun_strinsert);
 FUNCTION_PROTO(fun_stripaccents);
 FUNCTION_PROTO(fun_stripansi);
@@ -350,6 +353,7 @@ FUNCTION_PROTO(fun_trunc);
 FUNCTION_PROTO(fun_type);
 FUNCTION_PROTO(fun_ucstr);
 FUNCTION_PROTO(fun_ufun);
+FUNCTION_PROTO(fun_ulambda);
 FUNCTION_PROTO(fun_uldefault);
 FUNCTION_PROTO(fun_ulocal);
 FUNCTION_PROTO(fun_unique);