Copyright Bruce Wilcox<EMAIL_ADDRESS>www.brilligunderstanding.com
Revision 6/13/2022 cs13.2
System functions are predefined and can be intermixed with direct
output. Generally they are used from the output side of a rule, but in
many cases nothing prevents you from invoking them from inside a
pattern. When used in a pattern, they do not write out any text output
to the user. But their output will be tested the same as it would from
an if
statement, meaning 0 and false are failures.
You can write them with or without a ^
in front of their
name in output. With is clearer, but you don’t have to. The only times
you must is if the first thing you want to do in a gambit is call a
function (unlikely) or you are in a pattern.
t: name(xxx)
u: ( [find (me go) ])
These are ambiguous. In the gambit is it function call or label. In the responder is find a function name or just a word?
The above gambit is treated as a label and pattern. You can force it to be a function call by one of these:
t: ^name(xxx) # explicilty say it is a function
t: () name(xxx) # explicitly add an empty pattern
Some functions out or take “rule tags”. All rules have an internal
label consisting of ~topic.toplevelindex.rejoinderindex
.
E.g.
~introductions.0.5
stands for the 0th rule in the ~introductions
topic,
rejoinder #5.
^addtopic ( topicname )
adds the named topic as a pending topic at the head of the list. Typically you don’t need to do this, because finding a reaction from a topic which is not a system, disabled, or nostay topic will automatically add the topic to the pending list. Never returns a fail code even if the topic name is bad.
^available ( ruletag optionalfail )
Sees if the named rule is available (1) or used up (0). If you supply the optional argument, the function will fail if the rule is not available.
^cleartopics()
Empty the pending topics list.
^counttopic ( topic what )
For the given topic, return how many rules match what.
What is gambits
, responders
,
availablegambits
,
availableresponders
,rules
,
used
.
That is, how many gambits exist, how many available gambits exist (not erased), how many responders exist, how many available responders exist (not erased), how many top level rules (gambits + responders) exist, and how many top level rules have been erased.
^gambit ( value value ... )
If value is a topic name, runs the topic in gambit mode to see if any
gambits arise. If none arise from the first value, it will try the
second, and so on. It does not fail unless a rule forces it to fail or
the named topic doesn’t exist or is disabled. You can supply an optional
last argument FAIL
, in which case it will return
FAILRULE_BIT
if it didn’t fail but it didn’t generate any
new output either.
The value may be ~
, which means use the current topic
you are within. It can also be PENDING
, which means pick a
topic from the pending topics stack (they are all pending being returned
to but not including the current topic). Or it can be any other word,
which will be a keyword of some topic to pick. E.g.,
^gambit(~ PENDING ~mygeneraltopic FAIL)
^findrule ( label )
On the assumption that you only have one occurence of a rule label in your script, if you provide that label to this function, it will find the corresponding rule anywhere in your script and return the rule tag corresponding to it. If you have more than one such labelled Rule it merely returns the first one it finds (which will be earliest rule in earliest compiled topic).
^getrule ( what label )
for the given rule label or tag, return some fragment of the rule.
what can be tag
,
type
,label
, pattern
,
output
,topic
, and usable
.
The type will be t
, ?
, s
,
a
, etc.
If a rule label is involved, optional third argument if given means
only find enabled rules with that label. For usable, returns 1 if is can
be used or null if it has been erased. The label ~
means
the current rule. The label 0
means the top level rule
above us (if we are a rejoinder, otherwise it is the same as
~
).
If ^getrule(label) is called from ^testpattern, then the label associated with the pattern will be used, if it has one.
^hasgambit ( topic )
fails if topic does not have any gambits left unexecuted.
Even it if does, they may not execute if they have patterns and they
don’t match. Optional second argument, if any
will return
normally if topic has any gambits (executed or not) and will failrule if
topic has no gambits (a reactor topic).
^keep()
do not erase this top level rule when it executes its output part (you could declare a topic to be this, although it wouldn’t affect gambits).
Doing keep()
on a gambit is quite risky since gambits
after it may not ever fire.
^lastused ( topic what )
given a topic name, get the volley of the last what, where
what is GAMBIT
, RESPONDER
,
REJOINDER
, ANY
. If it has never happened, the
value is 0
.
^next ( what {label} )
Given what of GAMBIT
or RESPONDER
or
REJOINDER
or RULE
and a rule label or tag,
find the next rule of that what. Fails if none is found.
REJOINDER
will fail if it reaches the next top level
rule.
If label is ~
, it will use the last call’s
answer as the starting point, enabling you to walk rules in
succession.
There is also ^next(FACT @xxx)
- see fact manual.
For ^next(INPUT)
the system will read the next sentence
and prep the system with it. This means that all patterns and code
executing thereafter will be in the context of the next input sentence.
That sentence is now used up, and will not be seen next when the current
revised sentence finishes.
Sample code might be:
t: Do you have any pets
a: ( ~yes ) refine()
b: ( %more ) ^next(input) refine()
c: ( ~pets ) ... # react to pet
c: () ^retry(SENTENCE) # return to try input from scratch
b: () What kind do you have?
c: ( ~pets ) ... # react to pet
If label is LOOP
, the system will stop
processing code in the current loop and return to the next iteration of
it, e.g. C++/Java continue, except that it will stop all code and return
to however high up the loop really is, exiting topics and functions
willy nilly if need be.
^poptopic ( topicname )
Removes the named topic as a pending topic. The intent is not to automatically return here in future conversation. If topicname is omitted, removes the current topic AND makes the current topic fail execution at this point.
^refine ( ? )
This is like a switch statement in C language. It executes in order the rejoinders attached to its rule in sequence.
When the pattern of one matches, it executes that output and is done, regardless of whether or not the output fails or generates nothing. It does not “fail”, unless you add an optional FAIL argument. You can also provide a rule tag. Normally it uses the rule the refine is executing from, but you can direct it to refine from any rule.
^rejoinder ( {tag/label} )
Without argument, see if the prior input ended with a potential rejoinder rule, and if so test it on the current sentence. If we match and dont fail on a rejoinder, the rejoinder is satisfied. If we fail to match on the 1st input sentence, the rejoinder remains in place for a second sentence. If that doesn’t match, it is canceled. It is also canceled if output matching the first sentence sets a rejoinder.
You can give an optional tag or label to pretend the named rule had been the one to set a rejoinder and so therefore execute its rejoinders explicitly.
^respond ( value value ... )
Tests the sentence against the named value topic in responder mode to see if any rule matches (executes the rule when matched). It does not fail (though it may not generate any output), unless a rule forces it to fail or the topic requested does not exist or is disabled.
This rule will not erase but the responding rule might. If the first
value fails to generate an answer, it tries the second, and so on. You
can supply an optional last argument FAIL
, in which case it
will return FAILRULE_BIT
if it didn’t fail but it didn’t
generate any new output either. You could instead supply an optional
last argument TEST
, in which case a topic is executed to
see if a rule will match. If so, the tag is returned and no output is
made from the topic (and no rule is used up).
If a value designates a labelled or tagged rule (e.g.,
~mytopic.mylabel
or ~mytopic.1.0
) then the
system will skip over all rules until it reaches that rule, then begin
linear scanning, even if the topic is designated random.
The value may be ~
, which means use the current
topic you are within.
It can also be PENDING
, which means pick a topic from
the pending topics stack (they are all pending being returned to but not
including the current topic). Or it can be any other word, which will be
a keyword of some topic to pick.
^retry ( item )
If item is RULE
reexecute the current rule. It
will automatically try to match one word later than its first match
previously.
If item is TOPIC
it will try the topic over
again.
If item is SENTENCE
it will retry doing the
sentence again. To prevent infinite loops, it will not perform more than
5 retries during a volley. SENTENCE
is particularly useful
with changing the tokenflags to get input processing done differently.
If item is INPUT it will retry all input again.
^retry(TOPRULE)
will return back to the top level rule
(not of the topic but of a rejoinder set) and retry.
It’s the same if the current rule was a top level rule, but if the
current rule is from ^refine()
, then it returns to the
outermost rule to restart. If the current rule is not from
^refine()
, then TOPRULE
means the lexically
placed toprule above the current rule and a ^reuse()
will
be performed to go to it.
^reuse ( rule label optional-enable optional-FAIL )
Uses the output script of another rule. The label can either be a simple rule label within the current topic, or it can be a dotted pair of a topic name and a label within that topic or it can be a rule tag.
^reuse
stops at the first correctly labeled rule it can
find and issues a RULE fail if it cannot find one. Assuming nothing
fails, it will return 0 regardless of whether or not any output was
generated.
When it executes the output of the other rule, that rule is credited with matching and is disabled if it is allowed. If not allowed, the calling rule will be disabled if it can be.
t: NAME () My name is Bob.
?: ( << what you name >> )
^reuse(NAME)
?: ( << what you girlfriend name >> )
^reuse(~SARAH.NAME)
Normally reuse will use the output of a rule whether or not the rule
has been disabled. But, if you supply a 2nd argument (whatever it is),
then it will ignore disabled ones and try to find one with the same
label that is not disabled. You can also supply a FAIL
argument (as either 2nd or 3rd) which indicates the system should issue
a RULE FAIL if it doesn’t generate any output.
If you want to use a common rule to hold an answer and ONLY fire when reused, perhaps with rejoinders, the most efficient way to do that is with a rule whose pattern can never match. E.g. like this:
s: COMMON (?) some answer
a: () some rejoinder...
You make ^reuses
go to COMMON (or whatever you name it)
or even ^setrejoinder
on it. The rule itself can never
trigger because it only considers its pattern when the input is a
statement, but the pattern says the input must be a question. So this
rule never matches on its own.
There are also a variety of functions that return facts about a topic, but you have to read the facts manual to learn about them.
^sequence ( ? )
This is like ^refine
, except instead of only executing
the first rejoinder that matches, it executes all matching rejoinders in
order. If one of the rule outputs fails, it stops by failing the calling
rule.
Normally ^sequence
uses the rejoinders of the rule that
it is executing from, but you can direct it to ^sequence
the rejoinders of any rule.
^setrejoinder ( {kind} tag )
Force the output rejoinder to be set to the given tag or rule label. It’s as though that rule had just executed, so the rules beneath it will be the rejoinders to try.
If kind is input
then the input rejoinder is
set.
If kind is output
or is omitted, then it sets
the output rejoinder.
^setrejoinder
does not jump anywhere. It establishes the
context for ^rejoinder
.
When you do:
t: what is your name
a: ATX(_~propernoun) Hi, '_0
the outputrejoinder is set to ATX
. You can change that
if you want. When the next volley comes in, the outputrejoinder is now
the inputrejoinder and used for ^rejoinder
. You can modify
that as well. Both can exist simultaneously, you have the input context
and you set an output context before having used up the
inputrejoinder.
Setting a rejoinder on a rule means starting with the rejoinder
immediately after it. If kind is output and the tag is
null
, the output rejoinder is cleared (analogous to
^disable).
If the kind is input and the tag is
null
, the input rejoinder is cleared.
To kill a set outputrejoinder, use ^disable(OUTPUTREJOINDER).
^topicflags ( topic )
Given a topic name, return the control bits for that topic. The bits
are mapped in dictionary_system.h as TOPIC_*
.
^sleep ( milliseconds )
This stalls the engine for that many milliseconds. If this is a server, the server is unavailable until sleep is done. Use with care. A good use is when starting up a server instance and the boot process involves reading from an API. If your machine runs 30 instances of ChatScript launched at once (to use max CPU), then all of them hitting the same API at once may be bad for the API and forcing a randomized sleep based on processid is a good use.
^mark ( word location {ONE ALL})
Marking and unmarking words and concepts is fundamental to the
pattern matching mechanism, so the system provides both an automatic
marking mechanism and manual override abilities. You can manually mark
or unmark something. Automatic system marking marks all concepts implied
by chasing up membership in other concepts, as does this call
^mark
. word
can be any word, which also means
you can mark something with a concept name whether or not the concept
actually is defined anywhere.
There are two mechanisms supported using ^mark
and
^unmark
: specific and generic.
With specific
, you name words or concepts to mark or
unmark, either at a particular point in the sentence or throughout the
sentence. By default, or using the optional third argument
ALL
, not only is what you name marked, but anything it in
turn is a part of is marked. The optional third argument
ONE
will only mark that named word/concept and none of the
hierarchy implied by its membership in yet some other concept.
With generic
you disable or reenable all existing marks
on a word or words in the sentence. In fact, you go beyond that because
during patttern matching words you disable are invisble entirely, and
matching proceeds as if they do not exist.
Specific
: effects are permanent for the volley and cross
over to other rules. In documentation below, use of _0
symbolizes use of any match variable.
^mark and ^unmark can mark/unmark a single thing, or the hierarchy of
things.
Mark takes a possible 3rd argument ONE or ALL (defaulted) which makes it
mark either just the thing you gave or traverse the hierarchy and mark
all of it.
^mark ( ~meat _0 )
This marks ~meat
as though it has been seen at whereever
sentence location _0
is bound to (start and end)
^mark ( ~meat n )
Assuming n
is within 1 and sentence word limit, this
marks meat at nth word location. If n
was gotten from
^position of a match variable, it is the range of that match
variable.
^mark ( tomboy _0 )
This marks the word tomboy as visible at the location designated,
even though this word is not actually in the sentence. While patterns
will react to its presence, it will not show up in any memorizations
using _
.
While usually you mark a concept, you can also mark a word (though you should generally use the canonical form of the word to trigger all its normal concept hierarchy markings as well).
Although ^conceptlist
(see Facts manual) normally only
reports concepts marked at a word, if you explicitly mark using a word
and not a concept, that will also be reported in
^conceptlist
.
^mark ( ~meat )
With location omitted, this marks ~meat
as though it has
been seen at sentence start (location 1).
^mark()
Memorizes the set of all * unmarks (generic unmarks) and then turns them off so normal matching will occur.
^unmark()
Restores the set of generic unmarks that were flipped off using
^mark()
.
^unmark ( word _0 )
The inverse of specific ^mark
, this takes a
matchvariable that was filled at the position in the sentence you want
erased and removes the mark on the word or concept set or topic name
given. Pattern matching for it in that position will now fail. If the
word was a phrase, then all words in that phrase have the mark removed.
Thus South Georgia
which has Georgia
embedded
within it, and both might be ~geographic_area, will have both words
unmarked if you unmark ~geographic_area. But it is not symmetric to
^mark
because it does not remove all implied marks that
mark may have set.
If you end up calling ^unmark with a 2nd argument of null, it will just return without failure. This can happen if you pass null to an outputmacro: myfn(var) and then ^unmark(xxx ^var).
^Unmark can remove either the single thing, or ^unmark(* _0) removes ALL marks and makes the word completely invisible (not seen in a wildcard match), whereas ^unmark(@ _0) removes all marks but leaves the word occupying space in the sentence and will be seen in a wildcard match.
When you call unmark, the analysis of the sentence HAS ALREADY
HAPPENED.
If a ~noun was detected, so has ~noun_and_some_concept. So if you then
erase ~noun mark, it has NO impact on other marks made.
^unmark ( * n )
Assuming n
is within 1
and sentence word
limit, this unmarks all concepts at nth word location. If n was gotten
from ^position
of a match variable, it is the range of that
match variable.
^unmark ( word all )
All references to word (or ~concept
if you named one)
are removed from anywhere in the sentence.
Generic:
effects are transient if done inside a pattern,
last the volley if done in output. When you are trying to analyze pieces
of a sentence, you may want to have a pattern that finds a kind of word,
notes information, then hides that kind of word and reanalyzes the input
again looking for another of that ilk.
Being able to temporarily hide marks can be quite useful, and this
means typically you use ^unmark
of some flavor to hide
words, and then ^mark
later to reenable access to those
hidden words.
^unmark ( * _0 )
Turns off ALL matches on this location temporarily. The word becomes
invisible and takes up no space in the sentence. It disables matching at
any of the words spanned by the match variable. This unmark will also
block subsequent specific marking using ^mark
at their
locations.
^unmark ( @ _0 )
Turns off ALL matches on this location range but keep the word
visible. It cannot be matched by anything but a wildcard and takes up
its space in the sentence. Often this is used before something like
mark(~city _0)
where you are taking something with
ambiguous meanings, like “nice”, and removing all wrong meanings (and
right ones) and putting back just right ones. This is a way for the rest
of processing to only see the correct interpretation of a word.
Turns off matching on all words of the sentence.
^mark ( * _0 )
To restore all marks to some location after having used ^unmark(* _0)
^unmark ( * )
Turn off all words of the sentence. Probably not that useful.
^mark ( * )
Restores all marks of the sentence, for words that had ^unmark(* _0) performed.
Reminder: If you do a generic unmark from within a pattern, it is transient and will be turned off when the pattern match finishes (so you don’t ruin later rules), whereas when you do it from output, then the change persists for the rest of the volley. Furthermore it is handy to flip specific collections of generic unmarks on an off.
^replaceword(word _0)
You can change the word itself at the location just by providing the word you want used and the location in the sentence (as a match variable or position index). Replacing a word does not make it visible to pattern matching. It is merely what will be retrieved (for both original and canonical).
^addwordAt(word canonical _0)
Inserts word into sentence at the position of given match variable, moving what was there one farther over. The second argument designates the canonical to use and the third is a a position, and can be a match variable name or something that evaluates into a word index. Does not alter pos-tagging or parsing and does not mark itself. You can use ^mark to cause marking to happen. Fails if sentence is already full to brim with words.
^position ( how matchvariable )
This returns the integer representing where the named match variable is located.
how can be START
, END
, or
BOTH
. Both means an encoding of where the start and end of
the the match was. See @_n
in pattern matching to set a
position or the ^setposition
function.
^marked ( word )
returns 1
if word is marked, returns
FAILRULE_BIT
if the given word is not currently marked from
the current sentence.
^setposition ( _var start end )
Sets the match location data of a match var to the number values given.
Alternatively you can do ^setposition ( _var _var1 )
,
which is redundant with just doing _var = _var1
.
Another alternative is
^setposition ( _var1 _var2 original )
, which will set the
position of _var1
to the actual positions that the original
word _var2
was expanded to. This is equivalent to using
$_start = ^position( START _var2 )
$_end = ^position( END _var2 )
$_original = ^originalinputrange( $_start $_end )
$_originalStart = $_original >> 8
$_originalEnd = $_original & 255
$_actual = ^actualinputrange( $_originalStart $_originalEnd )
$_actualStart = $_actual >> 8
$_actualEnd = $_actual & 255
^setposition( _var1 $_actualStart $_actualEnd )
^setcanon ( wordindex value )
Changes the canonical value for this word.
^settag ( wordindex value )
Changes the pos tag for the word.
^setoriginal ( wordindex value )
Changes the original value for this word.
^setrole ( wordindex value )
Changes the parse role for this word. These are used in conjunction
with $cs_externaltag
to replace the CS inbuilt English
postagger and parser with one from outside. See end of ChatScript
PosParser manual.
^savesentence ( label )
/ ^restoresentence ( label )
These two functions save and restore the current entire sentence
preparation context. That means everything that pattern matching depends
upon from the current sentence can be saved, you can go on to a new
sentence (either via ^next(INPUT)
or
^analyze()
or whatever), and then rapidly flip back to some
previous sentence analysis. Label is a value used to label the saved
analysis. This only works during the current volley.
Cannot be used in document mode. ^savesentence
returns
the number of 4-byte words the save took.
^analyze ( stream )
The stream generates output (not printed to user) and then prepares the content as though it were current input sentence. This means the current sentence flagging and marking are all replaced by this one’s. It does not affect any pending input still to be processed. If the stream is quoted string, the quotes are removed. This would be common, for example, when analyzing output from the chatbot gotten via grabbing facts with “chatoutput” as the verb.
Note that the stream is considered a single sentence. If you want to
supply multiple sentences, you need to call ^tokenize
and
then loop on the facts created.
Note that ^analyze
does not call any prepass topic you
may have, but you can invoke that topic directly aterwards yourself.
^tokenize ( {WORD SENTENCE} stream )
WORD
or SENTENCE
are optional parameters
(SENTENCE
is default).
If SENTENCE
, then splits the stream into sentences and
creates facts of each like this:
(sentence ^tokenize ^tokenize)
.
If WORD
, then splits it entirely into words paying no
attention to sentence boundaries.
^capitalized ( n )
Returns 1
if the nth word of the sentences starts with a
capital letter in user input, else returns 0
.
If n is alphabetic, it returns whether or not it starts with a capital letter. Illegal values of n return failrule.
^input ( … )
The arguments, separated by spaces, are injected back into the input
stream as the next input, processed before any pending additional input.
Typically this command is then followed by ^fail(SENTENCE)
to cancel current processing and move onto the revised input.
Since the sentence is fed in immediately after the current input, if
you want to feed in multiple sentences, you must reverse the order so
the last sentence to be processed is submitted via input first. You can
detect that the current sentence comes from ^input
and not
from the user by %revisedInput
(bool) being true (1).
Note: the input sentence is not what the user originally typed, so don’t expect it to reflect appropriately in %originalInput. Also it is tokenized on entry, so things like commas may already have been separated.
^original ( _n ) or ^original(rawuser)
The argument is the name of a match variable. Whatever it has memorized will be used to locate the corresponding series of words in the original raw input from the user that led to this match. That is, the value is prior to any spell correction done by ChatScript.
u: (my _life) ^original(_0)
For input “my lif” spell correction will change the input to “life”, which matches here, but ^original will return “lif”.
^original(rawuser)
returns exactly what user sent in,
before any adjustments are made to it. (Some adjustments are made even
before spellcheck).
^position ( which _var )
If which is start
this returns the starting
index of the word matched in the named _var
.
If which is end
this returns the ending index.
E.g.,
if the value of _1
was the fox, it might be
that start was 3 and end was 4 in the sentence it was the fox
.
If the match var matched a fundmental meaning, then you can use which
values of subject
, verb
, and
object
. The verb is where start and end both yield.
^removetokenflags ( value )
Rremoves these flags from the tokenflags returned from the preprocessing stage.
^settokenflags ( value )
Adds these flags to the tokenflags return from the preprocessing
stage. Particularly useful for setting the #QUESTIONMARK
flag indicating the input was perceived to be a question.
For example, I treat tell me about cars sentences as questions by marking them as such from script (equivalent to what do you know about cars?).
Note that the change will not impact rule matching within the topic you have just done the change, because it has commited the set of rules it will try to match. So it only applies to later topics.
^setwildcardindex ( value )
Tells the system to start at value
for future
allocations of wildcard slots. This is only useful inside some pattern
where you are trying to protect data from some previous match. Eg.
u: (_~animals) refine()
a: ( ^setwildcardindex(_1) _~color)
_0
is set to an animal. Normally the rejoinder would set
a color onto _0
and clobber it, but the call to
^setwildcardindex
forces it to use _1
instead,
so both _0
and _1
have values.
^isnormalword ({letteronly} value)
Fails if value has a character that is not alphabetic, numberic, a hyphen, an underscore, or an apostrophe. If the optional letteronly is given, then only alphabetic is allowed.
^compute ( number operator number )
Performs arithmetic and puts the result into the output stream.
Numbers can be integer or float and will convert appropriately. There
are a range of operators that have synonyms, so you can pass in directly
what the user wrote. The answer will be ?
if the operation
makes no sense and infinity if you divide by 0
.
~numberOperator
recognizes these operations:
| operator symbol | description |
|---|---|
+ |
plus add and (addition) |
- |
minus subtract deduct (subtraction) |
* |
x time multiply (multiplication) |
/ |
divide quotient (float division) |
% |
remainder modulo mod (integer only- modulo) |
root |
square_root (square root) |
^^ |
power exponent (exponent ) |
<< and >> |
shift (limited to shifting 31 bits or less) |
random |
( 0 random 7 means 0,1,2,3,4,5,6 - integer only) |
Basic operations can be done directly in assignment statements like:
$var = $x + 43 - 28
The random function is only pseudo-random. A specific username is assigned a seed based on their name. Thereafter the seed evolves by the dialog but it is repeatable when the same user starts over again. If you want truly random, use %fullmstime % $howmany to get range 0 .. $howmany-1
^timefromseconds ( seconds {offset} )
This converts time in seconds (Unix epoch time) from the given time
in whatever timezone, to a string like %time
returns. You
can compute a difference in times by merely doing a subtraction of the
two times. %fulltime
will give you the current time that
you could plug in here. The optional second argument will displace that
time by the hours offset (can be plus or minus).
^timeinfofromseconds ( seconds )
This converts time in seconds (Unix epoch time) into its component
bits, spread across 7 match variables. Starting by default at
_0
, if you assign it like this:
_3 = ^timeinfofromseconds(%fulltime)
it will start at _3
. The items you get are:
| value | offset |
|---|---|
| seconds | 0 |
| minutes | 1 |
| hours | 2 |
| date in month | 3 |
| month name | 4 |
| year | 5 |
| day name of week | 6 |
| month index (jan==0) | 7 |
| dayofweek index (sun==0) | 8 |
^timetoseconds ( seconds minutes hours date-of-month month year )
This converts time data since 1970 (Unix epoch time). Analogous to
%fulltime
, which returns the current time in seconds. Month
can be number 1-12 or name of month or abbreviation of month.
Date-of-month must be 1 or more. Year must be on or 1970 and less than
2100. Optional 7th argument indicates whether time is within daylight
savings or not , values can be 1 or 0, t or f, T or F. Default is
false.
^isnumber ( value )
Fails if value is not an integer, float, or currency,
The following functions cannot be used during postprocessing since output has been finished in theory and you can now analyze it.
^flushoutput()
Takes any current pending output stream data and sends it out. If the rule later fails, the output has been protected and will still go out (though the rule will not erase itself).
^insertprint ( where stream )
The stream will be put into output, but it will be placed before output number where or before output issued by the topic named by where. The output is safe in that even if the rule later fails, this output will go out. Before the where, you may put in output control flags as either a simple value or a value list in parens.
^keephistory ( who count )
The history of either BOT
or USER
(values
of who) will be cut back to the count give. This affects detecting
repeated input on the part of the user or detecting repeating output by
the chatbot.
^lastsaid ()
Returns what the bot said last volley.
^print ( {stdout, log} stream )
Sends the results of outputing that stream to the user. It is
isolated from the normal output stream, and goes to the user whether or
not one later generates a failure code from the rule. Before the output
you may put in output control flags as either a simple value without a
#
(e.g., OUTPUT_EVALCODE
) or a value list in
parens.
By default it goes to the user log (or if log is specified) but if stdout is specified it goes to the console.
Flags include:
| Flag | description |
|---|---|
OUTPUT_EVALCODE |
is automatic, so not particularly useful. Useful ones would control how print decides to space things |
OUTPUT_RAW |
does not attempt to interpret ( or { or [
or " |
OUTPUT_RETURNVALUE_ONLY |
does not go to the user, is merely return as an answer. Print normally stores directly into the response system, meaning failing the rule later has no effect. Print normally does not return a value so you can’t store it into a variable. And print has a number of flags that can affect its formatting that dont exist with normal output. This flag converts print into an ordinary function returning a value, reversing all those differences |
OUTPUT_NOCOMMANUMBER |
dont add commas to numbers |
OUTPUT_NOQUOTES |
remove quotes from strings |
OUTPUT_NOUNDERSCORE |
convert underscores to blanks |
These flags apply to output as it is sent to the user:
| Flag | description |
|---|---|
RESPONSE_NONE |
turn off all default response conversions |
RESPONSE_UPPERSTART |
force 1st character of output to be uppercase |
RESPONSE_REMOVESPACEBEFORECOMMA |
as the name says |
RESPONSE_ALTERUNDERSCORES |
convert underscores to spaces |
RESPONSE_REMOVETILDE |
remove leading ~ on class names |
RESPONSE_NOCONVERTSPECIAL |
don’t convert ecaped n, r, and t into ascii direct characters |
RESPONSE_CURLYQUOTES |
change simple quotes to curly quotes (starting and ending) |
RESPONSE_NOFACTUALIZE |
suppresses building bot output facts (for postprocessing) |
^preprint ( stream )
The stream will be put into output, but it will be placed before all previously generated outputs instead of after, which is what usually happens. The output is safe in that even if the rule later fails, this output will go out. Before the output you may put in output control flags as either a simple value or a value list in parens.
^repeat ()
Allows this rule to generate output that may repeat what has been said recently by the chatbot.
^reviseOutput ( n value )
^setresponse(ID message) Allows you to replace a generated response with the given value.
n is one based and must be within range of given responses.
One can use this, for example, alter output to create accents. Using
^response
to get an output, you can then use
^substitute
to generate a revised one and put it back using
this function.
^setresponse ( n value )
Another name for ^reviseoutput.
These functions allow you to find out what the chatbot has said and why.
^response ( id )
What the chatbot said for this response. Id 1
will be the first output.
^responsequestion ( id )
Boolean 1 if response ended in ?
, null otherwise.
^responseruleid ( id )
The rule tag generating this response from which you can get the
topic. May be joined pair of rule tags if rule was relayed (reuse) from
a different rule. The final rule will be first and the relay second, eg
~keywordless.30.0.~control.3.4
.
If the id is -1
, then all output generated will
be included, analogous to what happens in the log file for
why
in the entries.
^responsepattern(responseid)
Returns the pattern that matched inside [] of rule generating output (if it is matched that way) For a rule like: u: ([ (pattern 1) (pattern 2) ([try 3])]) It will tell you which piece of the pattern matched. Handy for debugging why a pattern matches incorrectly w/o having to read a trace log and analyzing each + and -.
These functions are only available during postprocessing.
^postprintbefore ( stream )
It prints the stream prepended to the existing output. You will not
be able to analyze or retrieve information about this, like you would
from a normal print because it generates no facts representing it. This
is useful for adding outofband messages [ ]
to the front of
input for controlling avatars and such. Or for adding transitional
phrases or other personality coloring before the main output.
^postprintafter ( stream )
It prints the stream appended to the existing output. You will not be able to analyze or retrieve information about this, like you would from a normal print because it generates no facts representing it. This is useful for adding summarizing data after output, e.g., when running the document reader.
^argument ( n )
Retrieves the nth argument of the calling outputmacro (1-based).
^argument ( n ^fn )
Looks backward in the callstack for the named outputmacro, and if
found returns the nth argument passed to it. Failure will be reported
for n out of range or ^fn
not in the call path.
This is an alterative access to function variable arguments, useful in a loop instead of having to access by variable name.
If n is 0
, the system merely tests whether the
caller exists and fails if the caller is not in the path of this
call.
^Bug(msg)
Generates a run-time bug. If you are compiling script at the time, it generates a std script bug trap. If you are not, it forces an error message into the bug log.
^callstack ( @n )
Generates a list of transient facts into the named factset. The facts
represent the callstack and have as subject the critical value (the verb
is callstack
and the object is the rule tag responsible for
this entry). Items include function calls (^xxxx) and topic calls
(~xxxx) and internal calls (no prefix).
^command ( args )
Execute this stream of arguments through the :
command
processor. You can execute debugging commands through here. E.g.,
^command(:execute ^print("Hello") )
Note that it is hard to turn on :trace this way, because the system
resets it internally at various points. The correct way to manipulate
trace is to do $cs_trace = -1
in regular script, outside of
^command.
^end ( code )
Takes 1 argument and returns a code which will stop processing. Any
data pending in the output stream will be shipped to the user. If
^end
is contained within the condition of an if, it merely
stops it. An end rule inside a loop merely stops the loop. All other
codes propagate past the loop. The codes are:
| code | description |
|---|---|
CALL |
stops the current outputmacro w/o failing it. See also ^return |
RULE |
stops the current rule. Whether the next rule triggers depends upon whether or not output was generated |
TOPRULE |
stops the current rule (top level if in rejoinder). Whether the next rule triggers depends upon whether or not output was generated |
LOOP |
stops the current loop but not the rule containing it. Can pass up through topics to find the loop. If there is no loop, it will fail you all the way to the top |
TOPIC |
stops the current topic |
SENTENCE |
stops the current rule, topic, and sentence |
INPUT |
stops all the way through all sentences of the current input |
PLAN |
succeeds a plan - (only usable within a plan) |
^eval ( flags stream )
To evaluate a stream as though it were output (like to assign a
variable). Can be used to execute :
commands from script as
well.
Flags are optional and match the flag capabilities of
^print
.
One common flag would be OUTPUT_NOQUOTES
if you wanted
to remove the enclosing “” from a user variable value. E.g.,
$$tmp = ^eval(OUTPUT_NOQUOTES $$arg1)
^eval
is also particularly used with variables, when you
know the value of a variable is itself a variable name and you want its
actual value, e.g.
$nox = 1
$$tmp = join($ no x)
$$val = eval($$tmp) # $$val = 1
^fail ( code )
Takes 1 argument and returns a failure code which will stop
processing. How extensive that stop is, depends on the code. If
^fail
is contained within the condition of an if, it merely
stops that and not anything broader.  The failure
codes are:
| code | description |
|---|---|
RULE |
stops the current rule and cancels pending output |
TOPRULE |
stops the toplevel rule given we are executing in a rejoinder |
LOOP |
stops a containing loop and fails the rule calling it. If you have no containing loop, this can crawl up through all enclosing topics and make no output |
TOPIC |
stops not only the current rule also the current topic and cancels pending output. Rule processing stops for the topic, but as it exits, it passes up to the caller a downgraded fail(rule), so the caller can just continue executing other rules |
SENTENCE |
stops the current rule, the current topic, and the current sentence and cancels pending output |
INPUT |
stops processing anything more from this user’s volley. Does not cancel pending output. It’s the same as END(INPUT) |
Output that has been recorded via ^print
,
^preprint
, etc is never canceled. Only pending output.
^clearmatch()
This clears all match variables to empty.
^match ( what )
This does a pattern match using the contents of what (usually a
variable reference). It fails if the match against current input fails.
It operates on the current analyzed sentence which is usually the
current input, but since you can call ^next(input)
or
^analyze()
it is whatever the current analysis data is.
if (%more AND ^match(^"(< ![~emocurse ~emothanks] ~interjections >)" ) )
{FAIL(SENTENCE)}
or
$$newrule = GetRule(pattern $$newtag)
$$newtype = GetRule(type $$newtag)
if ($$newtype == $$type AND match($$newrule)) # we would match this rule
^match
can also take a rule tag for what, in which case
it uses the pattern of the rule given it. ^match
will
normally take your pattern and compile it with the script compiler
during execution.
If you have discarded the script compiler in your build, it will run your pattern directly and pray. In that case every token should be separated by a space: eg not this:
[my you]
but this
[ my you ]
and relational tests won’t work so you can’t do _0>5
or _0?
or things like that. If you know your pattern in
advance, you can put it on a rule and then execute that since it will
have been compiled. E.g.
s: TEST (some fancy pattern)
and later
^match(~mytopic.test)
You can also just say ^match(~someconcept)
and it will
test the current input for that concept.
$$csmatch_start
and $$csmatch_end
are
assigned to provide the range of words that ^match
used.
^matches ()
Returns a string of indices of words that matched the most recent pattern match. The indices are in order, so you can know the range of the match or the specific word indices that were seen. Currently matches only include the words/concepts that were matched, not things like
(sag*)
where the word is not fully named.
^nofail ( code … script … )
The antithesis of ^fail()
. It takes a code and and
number of script elements, executes the script and removes all failure
codes through the listed code.
This is important when calling ^respond
and
^gambit
from a control script. You would want a control
script to pass along codes at the sentence level, but if the respond
call generated a fail-rule return, you don’t want that to stop all the
code of a control script responder.
The nofail codes are:
| code | description |
|---|---|
RULE |
a rule failure within the script does not propagate outside of nofail |
TOPRULE |
a rule failure within the script does not propagate outside of nofail |
LOOP |
a loop failure or end within the script does not propagate outside of nofail |
TOPIC |
a topic or rule failure within the script does not propagate outside of nofail |
SENTENCE |
a topic or rule or sentence failure within the script does not propogate outside of nofail |
INPUT |
no failure propagates outside of the script |
notnull ( stream )
Execute the stream and if it returns no text value whatsoever, fail this code. The text value is not used anywhere, just tested for existence. Useful in IF conditions.
^norejoinder ()
Prevents this rule from assigning a rejoinder.
^notrace ( ... )
Suppresses normal tracing if if :trace all is on, for the duration of evaluation of the contents of the parens. It does not block explicit traces of functions or topics.
^trace (...)
Enables trace for the following, even if exterior is controlled by a ^notrace directive.
^return ( ... )
Evaluates it data and returns any output from the most recent calling outputmacro. It is nominally equivalent to:
here is some outputting
^end(CALL)
My personal coding convention is to use ^return
when the
function is supposed to return a value to a caller who will assign it
somewhere. And not to use it if the function is directly creating output
to the user or is just being executed for side effects.
You can return the contents of a variable
^return($$myvar)
or the name of a factset
^return(@19)
or just some literal value
^return(test)
. Returning a factset just returns its name.
But if you have
@0 = ^myfunc()
and ^myfunc
returns a factset name, you have done the
equivalent of
@0 = @19
which means copy the elements of set 19 into set 0.
Note that ^return()
and ^return(null)
are
treated the same. An empty string is returned. This is similar to
assigning a variable by saying $var = null
which assigns
the empty string.
^addcontext ( topic label )
Sets a topic and context name for use by ^incontext
.
The label doesn’t have to corrrespond to any real label.
The topic can be a topic name or ~
meaning
current topic.
^authorized ()
Use same authorizedIP.txt file and rules that debug commands use, to validate current user.
^clearcontext ()
Erases all context data (see ^addcontext
).
^incontext ( label )
This function is only used inside a pattern.
label can be a simple text label or a
topicname.textlabel
. The system tracks rule labels that
generated output to the user or rules starting with the label
CX_
whether or not the rule generates output as long as it
didn’t fail during output.
^inContext
will return how many volleys have happened
since the referenced rule (normal return) if the label has output within
the 5 prior volleys and will fail if not. It’s like an extension of
rejoinders. Rejoinders have a 1 volley context and must be placed
immediately after a rule. This has a 5 volley context and are used in
normal rule patterns.
You can give an optional 2nd argument number, which is how many volleys later are acceptable. If the volley count is higher, you fail even within the 5 prior volleys.
u: (^incontext(PLAYTENNIS) why) because it was fun.
^stats ( FACTS / DICT / TEXT / HASHBUCKETS / UNUSEDHASHBUCKETS )
^stats ( PATTERNEVALUATION )
^stats ( TIMESUMMARY )
All ^stats calls can take an
optional 2nd argument “ECHO” which sends the answers to the user log and
echo on terminal.
FACTS: Returns how many free facts remain. DICT: Returns how many empty dictionary entries remain. TEXT: Returns how much space for both stack and heap remain. HASHBUCKETS: shows how many words are in each bucket size UNUSEDHASHBUCKETS: how many buckets are wasted (empty) PATTERNEVALUATION: how many calls to pattern matcher happened TIMESUMMARY: create facts of how many ms cpu spend in functions called
^environment ( variablename )
Access environment variables of the operating system. E.g.
^environment(path)
^system ( any number of arguments )
The arguments, separated by spaces, are passed as a text string to
the operating system for execution as a command. The function always
succeeds, returning the return code of the call. You can transfer data
back and forth via files by using ^import
and
^export
of facts.
^popen ( commandstring 'function )
The command string is a string to pass the os shell to execute. That
will return output strings (some number of them) which will have any
\r
or \n
changed to blanks and then the string
stripped of leading and trailing blanks.
The string is then wrapped in double quotes so it looks like a standard ChatScript single argument string, and sent to the declared function, which must be an output macro or system function name, preceded by a quote.
The function can do whatever it wants. Any output it prints to the output buffer will be concatenated together to be the output from ChatScript. If you need a doublequote in the command string, use a backslash in front of each one. They will be removed prior to sending the command. E.g.,
outputmacro: ^myfunc(^arg)
^arg \n
topic: ~test( testing )
u: () popen( "dir *.* /on" '^myfunc)
output this:
Volume in drive C is OS
Volume Serial Number is 24CB-C5FC
Directory of C:ChatScript
06/15/2013 12:50 PM <DIR> .
06/15/2013 12:50 PM <DIR> ..
12/30/2010 02:50 PM 5 authorizedIP.txt
06/15/2013 12:19 PM 10,744 changes.txt
05/08/2013 03:29 PM <DIR> DICT
...( additional lines omitted)
49 File(s) 29,813,641 bytes
24 Dir(s) 566,354,685,952 bytes free
'Function
can be null if you are not needing to look at
output.
^tcpopen ( kind url data 'function )
Analogous in spirit to popen.
You name the kind
of service (POST
,
GET
), the url
(not including
http://
) but including any subdirectory, the text string to
send as data, and the quoted function in ChatScript you want to receive
the answer.
The answer will be read as strings of text (newlines separate and are stripped off with carriage returns) and each string is passed in turn to your function which takes a single argument (that text).
:trace TRACE_TCP
can be enabled to log what happens during the call.
Likely you will prefer ^jsonopen
which can deal with
more complex web communication scenarios and returns structured data so
you don’t have to write script yourself to parse the text.
'function
can be null
if you are not
needing to look at output.
The system will set $$tcpopen_error
with error
information if this function fails.
When you look at a webpage you often see it’s url looking like this:
http://xml.weather.com/weather/local/4f33?cc=*&unit ="+vunit+"&dayf=7"
There are three components to it.
The host: xml.weather.com
.
The service or directory: /weather/local/4f33
.
The arguments: everything AFTER the ?
.
The arguments are URLencoded, so spaces have been replaced by
+
, special characters will be converted to %xx
hex numbers.
If there are multiple values, they will be separated by
&
and the left side of an =
is the
argument name and the right side is the value.
When you call ^tcpopen
, normally you provide the host
and service as a single argument (everything to the left of
?
) and the data as another argument (everything to the
right of ?
).
Since ChatScript URL encodes, you don’t. If you don’t know the
unencoded form of the data or you don’t think CS will get it right, you
can provide URL-encoded data yourself, in which case make your first
argument either POSTU
or GETU
, meaning you are
supplying url-encoded data so CS should not do anything to your
arguments.
Below is sample code to find current conditions and temperature in
san francisco if you have an api key to the service. It calls the
service, gets back all the JSON formatted data from the request, and
line by line passes it to ^myfunc
.
This, in turn, calls a topic to hunt selectively for fragments and
save them, and when all the fragments we want have been found,
^myfunc
outputs a message and stops further processing by
calling ^END(RULE)
.
Note that in this example there is no data to pass, everything is in the service named, so the data value is ““.
outputmacro: ^myfunc (^value)
$$tmp = ^value
nofail(RULE respond(~tempinfo))
if ($$currentCondition AND $$currentTEMP)
{
print( It is $$currentCondition. )
print(The temperature is $$currentTemp. )
^END(RULE)
}
topic: ~tempinfo system repeat keep()
u: (!$$currentCondition)
$$start = findtext($$tmp $$pattern1 0)
$$findtext_start = findtext($$tmp ^"\"" $$start)
$$currentCondition = extract($$tmp $$start $$findtext_start )
u: ($$currentCondition)
$$start = findtext($$tmp $$pattern2 0)
$$findtext_start = findtext($$tmp , $$start)
$$currentTemp = extract($$tmp $$start $$findtext_start)
topic: ~INTRODUCTIONS repeat keep (~emogoodbye ~emohello ~emohowzit name )
t: ^keep() Ready. Type "weather" to see the data.
u: (weather)
$$pattern1 = ^"\"weather\":\""
$$pattern2 = ^"\"temp_f\":"
if ( tcpopen(GET api.wunderground.com/api/yourkey/conditions/q/CA/San_Francisco.json "" '^myfunc))
{ hi }
else
{ $$tcpopen_error }
There is a subtlety in the ^myfunc
code in that it uses
^print to put out the result. Just writing:
if ($$currentCondition AND $$currentTEMP)
{
It is $$currentCondition.
The temperature is $$currentTemp.
^END(RULE)
}
will not work, because that output is being generated by the call to
^tcpopen
, which is in the test part of the if, so
everything it does is purely for effect of testing a condition. The
generated output is dicarded.
If you moved the output generation to the { }
of the
if
, things would be fine. E.g.,
if ( tcpopen(GET api.wunderground.com/api/yourkey/conditions/q/CA/San_Francisco.json "" '^myfunc) )
{
It is $$currentCondition.
The temperature is $$currentTemp.
}
else { $$tcpopen_error }
Doing the output without using ^print
is my preferred
style; it is easier to see what is going on for output if it is not
hidden deep inside some if test.
^export ( name from )
From must be a fact set to export. Name is the file to write them to.
An optional 3rd argument append
means to add to the file at
the end, rather than recreate the file from scratch.
Obviously, you must first have done something like
^query
to populate the fact set. Eg.
^query(direct_sv item label ? -1 ? @3)
^export(myfacts.txt @3)
If the name includes the substring “ltm”, then the file will not be appendable, but will be encryptable and routes to databases if the filesystem has been overridden by Mongo, Postgres, or MySQL.
^import ( name set erase transient )
name is the file to read from. Set is where to put the read facts.
erase can be erase
meaning delete the file
after use, or keep
meaning leave the file alone, or
cache
to mean that the file can be read and stored in the
file cache.
transient can be transient
meaning mark facts
as temporary (to self erase at end of volley) or permanent
meaning keep the facts as part of user data. Eg
^import(myfacts.txt @3).
If set is null, then facts are created but not stored into any fact-set and the subject of the first fact is returned as the answer (presumed to be a json structure).
If the name includes the substring “ltm”, then the file will be decryptable and routes to databases if the filesystem has been overridden by Mongo, Postgres, or MySQL.
The external API functions allow execution of rule behaviors from outside of ChatScript. This function takes in a pattern string like
( [ ~bottle test] * me)
and compiles it to internal format. Alternatively it accepts a std CS ‘if’ condition like
if ($var >= 1 AND !$var2)
It returns JSON object notation in ordinary user output, e.g.
{ "code": "(....)" }
You can take that code value and save it away somewhere and later pass it into ^testpattern. Should compilation fail, then there will be no CODE field and instead you will have an errors field.
{"errors": [ xxx yyy ] }
In either case you may see a warnings field
{"warnings": [ xxx yyy ] }
where xxx and yyy are warning messages.
The code returned may have additional data at the start. This is used to inform spelling correction to protect words occurring in the pattern. For best results you should compile patterns in the same bot environment you expect to execute them in. The pattern won’t explictly protect words it thinks the bot will already be protecting.
You can create script function definitions in compilepattern can be passed around and used in ^testpattern and ^testoutput. This is done by assigning a function definition to a variable appropriately named with a $^ prefix.
$^myvar:=^"^dp_myfun($_a $_b) ..... "
The above variable assignment will compile the assignment into an appropriate representation as a function definition. That variable value can be passed into ^testpattern and ^testouput in the variables list and used. The function name must start with the prefix ^dp_ . The ….. are a set of normal output code, but omit any { } around it.
The external API functions allow execution of rule behaviors from outside of ChatScript. ^testpattern will execute a list of patterns and tell you which one (if any) matched and possibly return match variables. The argument is a JSON object as follows
{
"input": "range leak", -- required
"patterns": [ -- at least one required
string-returned-from-compilepattern,
string-returned-from-compilepattern,
],
"style": "earliest", -- optional
"trace": "1", -- optional
"variables": { -- optional
"$faucet": "testing",
"$x": 1 },
"trace": 1, -- optional, returns a field "trace" which is CS trace pattern of the patterns for remote debugging
"concepts": [ -- optional
{ "name": "~stove",
"values": [ "oven", "range", "cooktop" ]
},
{ "name": "~leaky",
"values": [ 'leak", "drip", "spill" ] } ]
}
Input is the user input (one or more sentences) to be matched against.
Patterns is an array of pattern strings as returned by
CompilePattern. Alternatively, patterns is an array of json pattern
objects, which has a field called pattern
and optionally a
field called label
that will be displayed when tracing
execution and is retrievable using ^getrule(~ label)
Patterns can use memorization and they can assign values. If they use memorization, you can assign those onto normal variables, which if permanent variables will be returned as “newglobals”. You can perform an assignment inside the pattern using something like $answer:=_0 (see Variable assignment in Advanced patterns). The “newglobals will be omitted if there are no changes.
Patterns can also define functions that can be used there or in later invoked patterns. They should be compiled using ^compilepattern
Use of %trace_on and %trace_off in a pattern can also be used to
trigger and return tracing data. %trace_on normally just does pattern
tracing but %trace_on all
turns on full tracing.
Use of “cheat cs info” as part of the user input will save onto the newglobal $cs_info the datestamps of the engine and scripts, and they will be appended to any output from ^testoutput that occurs subsequently.
Use of bwinfo as part of the user input will return a changed
variable bwinfo
which lists all words and the concepts
marked for them A concept ~bwinfo_ignore if you define it will have
concepts NOT to show here.
If you pass in a variable { $language : xxxx } then cs will change to that language for the call. This only works for languages not requiring their own dictionaries (like Japanese and ideographic).
If the user input you pass in is “null”, then the current input is not altered and will be used. This is only useful if you are calling this function from a normal bot and not an external one.
$$cs_sentencecount
will be set as the number of the
current sentence being used and can be used with ^restoresentence.
%originalinput
is the input field.
%testpattern
will be the index number in the array of
patterns of current pattern being matched in ^testpattern
Normally all patterns are executed in sequence against a sentence, then all again against the next sentence, and so on. If the initial patterns have %testpattern-prescan in them (usually at the start), then those will all be executed in sequence against all sentences in sequence. Then the remaining patterns will revert to the usual all patterns against each sentence in succession.
The variables and concepts fields are optional and provide context. A
concept named “~replace_spelling” is treated not as a concept but as a
list of paired words analogous to “replace:”, but is only a transient
replace series for this call. A concept can contain values that look
like: "(...)"
or (xxx)
which are CS patterns.
These patterns can match user input just like an ordinary concept word
can.
A concept can contain entries that are pairs, separated by |. This is
preferred over supplying separate pairs of words. The left side is
stored as the concept member and a fact is created ( left-side api-remap
right-side) which allows you to write scripts that remap found entries
on output to the right side value. ^testoutput accepts concepts so that
the remap ability can be instantiated by script.
Style is also optional and defaults to earliest
. Other
choices are all, best, latest and tfidf. Earliest means stop running
patterns and sentences as soon as you get a match. This makes the call
faster and if your patterns are ordered best first, there is no
incentive to try lower priority patterns or more sentences (except
against multiple sentence inputs). Latest is the opposite, presumes that
the patterns are ordered worst first. In addition, last will not execute
any patterns earlier than the already matching one once one has been
found (since it cannot improve the result). All runs all patterns on all
sentences. It is good for gleaning data. Best presumes patterns are
ordered best first, but once a match has been found it will move on to
additional sentences to see if a better match can be found.
Result of ^testpattern is ordinary user output JSON object:
{
"match": 1, -- index of matching pattern
"newglobals": { "$stovetype": "range", "$leaktype": "leak" }
}
If nothing matched, the value of match
is false.
Otherwise it is the index of the matching pattern (0-based). If there
are return values from matching one or more patterns, those will be
listed in newglobals
, which is omitted if there are none.
Values of null are never returned.
You can force ^testpattern to trace user regardless of whether
tracing is on or not or whether nouserlog is set.
Just prepend to your input “:tracepattern 1”. Or in a pattern or
^testoutput code use %tracepattern_on and %tracepattern_off and that
reference will turn on and off tracing within that call to CS. They can
be used in any pattern or output without damaging it. And you can block
this behavior on a server by using the command line parameter
blockapitrace
.
If you add cheat cs info
to the user input, it will
return a newglobal $cs_info which tells when scripts and engine were
compiled.
If you call ^jsonopen from a pattern and it times out or has a bad
url, ^testpattern will return an extra field
jsonopen-status
with value of timeout.
The optional trace
field if set, will have the patterns
involved traced (:trace pattern) and the results returned in a field
named trace
as a text string with crlf. Or a pattern can
set the variable $$cs_testpattern_style
to be a value and
the system will enable trace until it is set off or the call
completes.
tfidf
calculates a similarity score for each pattern
using a TF/IDF statistical process (term frequency/interdocument
frequency) . The scores are returned in a scores
array in
the result object. The input JSON structure requires an idf
object where the property names represent tokens, typically concept
names, used in the patterns and a value representing an inverse document
frequency score (between 0 and 1) for that token - rarer and hence more
selective tokens will have a higher score than common terms. It is
preferred that the members of each concept are mutually exclusive. For a
pattern, the similarity score is calculated from the idf
terms that were actually matched from the input.
{
"patterns" : [
"( ~concept1 { ~concept2 } )",
"( ~concept3 ~concept1 { ~concept4 } )",
"( << { ~concept1 } { ~concept2 } { ~concept5 } >> )",
...
],
"idf" : {
"~concept1" : 0.43723,
"~concept2" : 0.66096,
"~concept3" : 0.99923
...
}
}
An extended example is this:
For reference, here is a sample input document we create where we are trying to pick the best choice from the following:
1 GB 3G for 28 days @ 155 Rs.
1 GB 3G for 28 days 147 Rs
1 GB 2G for 28 days 147 Rs
Special Packs
The current sentence is "2G @ 147 Rs."
^testpattern data is:
{ # jo-t4484
"style": "tfidf",
"patterns": [ # ja-t4485
"^( ( [ ( ~choicelists000 ~choicelists001 ~choicelists002 ~choicelists003 ~choicelists004 ~choicelists005 ~choicelists006 ~choicelists007 ~choicelists008 ) ( << { ~choicelists000 } { ~choicelists001 } { ~choicelists002 } { ~choicelists003 } { ~choicelists004 } { ~choicelists005 } { ~choicelists006 } { ~choicelists007 } { ~choicelists008 } >> :m$$lovvariablepattern:=%testpattern ) ] ) ^recordlovpatternmatch ( ) ) ",
"^( ( [ ( ~choicelists000 ~choicelists001 ~choicelists002 ~choicelists003 ~choicelists004 ~choicelists005 ~choicelists009 ~choicelists008 ) ( << { ~choicelists000 } { ~choicelists001 } { ~choicelists002 } { ~choicelists003 } { ~choicelists004 } { ~choicelists005 } { ~choicelists009 } { ~choicelists008 } >> :m$$lovvariablepattern:=%testpattern ) ] ) ^recordlovpatternmatch ( ) ) ",
"^( ( [ ( ~choicelists000 ~choicelists001 ~choicelists010 ~choicelists003 ~choicelists004 ~choicelists005 ~choicelists009 ~choicelists008 ) ( << { ~choicelists000 } { ~choicelists001 } { ~choicelists010 } { ~choicelists003 } { ~choicelists004 } { ~choicelists005 } { ~choicelists009 } { ~choicelists008 } >> :m$$lovvariablepattern:=%testpattern ) ] ) ^recordlovpatternmatch ( ) ) ",
"^( ( [ ( ~choicelists011 ~choicelists012 ) ( << { ~choicelists011 } { ~choicelists012 } >> :m$$lovvariablepattern:=%testpattern ) ] ) ^recordlovpatternmatch ( ) ) "
],
"concepts": [ # ja-t4486
{ # jo-t4497
"name": "~choicelists000",
"values": [ # ja-t4498
1
]
},
{ # jo-t4499
"name": "~choicelists001",
"values": [ # ja-t4500
"GB"
]
},
{ # jo-t4501
"name": "~choicelists002",
"values": [ # ja-t4502
"3g",
"3G"
]
},
{ # jo-t4503
"name": "~choicelists003",
"values": [ # ja-t4504
"for"
]
},
{ # jo-t4505
"name": "~choicelists004",
"values": [ # ja-t4506
28
]
},
{ # jo-t4507
"name": "~choicelists005",
"values": [ # ja-t4508
"day",
"days"
]
},
{ # jo-t4509
"name": "~choicelists006",
"values": [ # ja-t4510
"at",
"@"
]
},
{ # jo-t4511
"name": "~choicelists007",
"values": [ # ja-t4512
155
]
},
{ # jo-t4513
"name": "~choicelists008",
"values": [ # ja-t4514
"rs",
"Rs."
]
},
{ # jo-t4524
"name": "~choicelists009",
"values": [ # ja-t4525
147
]
},
{ # jo-t4532
"name": "~choicelists010",
"values": [ # ja-t4533
"2g",
"2G"
]
},
{ # jo-t4540
"name": "~choicelists011",
"values": [ # ja-t4541
"special",
"Special"
]
},
{ # jo-t4542
"name": "~choicelists012",
"values": [ # ja-t4543
"pack",
"Packs"
]
},
{ # jo-t4551
"name": "~choicelistsall",
"values": [ # ja-t4552
"( ^testLoVSpell( ) )"
]
}
],
"idf": { # jo-t4487
"~choicelists000": 0.4372345589580705,
"~choicelists001": 0.4372345589580705,
"~choicelists002": 0.6609640474436812,
"~choicelists003": 0.0017079474959299629187314817357901119976304471492767,
"~choicelists004": 0.4372345589580705,
"~choicelists005": 0.4372345589580705,
"~choicelists006": 0.34328891624380336589084095066937152296304702758789,
"~choicelists007": 1.0849625007211563,
"~choicelists008": 0.4372345589580705,
"~choicelists009": 0.6609640474436812,
"~choicelists010": 1.0849625007211563,
"~choicelists011": 1.0849625007211563,
"~choicelists012": 1.0849625007211563
}
}
And this produces an output of
{ # jo-t4559
"match": 2,
"scores": [ # ja-t4560
0.400867,
0.571482,
0.968875,
0.000000
]
}
The external API functions allow execution of rule behaviors from outside of ChatScript. This function takes in an output string like
if ($test) {test me}
^callmycode($test)
and compiles it to internal format. It returns JSON object notation in ordinary user output, e.g.
{ "code": "(....)" }
You can take that code value and save it away somewhere and later pass it into ^testpattern. Should compilation fail, then there will be no CODE field and instead you will have an errors field.
{"errors": [ xxx yyy ] }
In either case you may see a warnings field
{"warnings": [ xxx yyy ] }
where xxx and yyy are warning messages.
The external API functions allow execution of rule behaviors from outside of ChatScript. The argument is a JSON object as follows
{
"output": code-returned-from-compile-output,
"variables": { -- optional
"$faucet": "testing",
"$x": 1 },
}
"concepts": [ -- optional
{ "name": "~illness",
"values": [ "red spots|redness", " ]
} ]
}
If a variable value above looks like a JSON arry or object, will be transformed into the corresponding internal JSON name.
The result is a JSON object
{
output: "message to user",
newglobals: {"$x": "1", "$y": "testing"} -- optional
}
Output may be blank if nothing is intended to be generated or the code fails in execution. Newglobals will be present if the code changes permanent global variables. Changes to transient variables $$ and local variables $_ will not be returned. If nothing is returned, the field is omitted. If a permanent variable is set to a JSON structure, the entire JSON string will BE send (and not the JSON name).
Concepts, if supplied, are only useful for remapping concept value to a different value for output. If you add the following function to your scripts:
outputmacro: ^remap VARIABLE ($_word )
$_remap = ^query(direct_sv $_word api-remap ? 1 ? @0object)
if (!$_remap) {$_remap = $_word}
^return($_remap)
then you could accept an input like “I have red spots” and write an output message like “how long have you had this redness?”
if (PATTERN _~illness) { How long have you had this ^remap('_0) ?}
^debug ()
As a last ditch, you can add this function call into a pattern or the
output and it will call DebugCode in functionExecute.cpp
so
you know exactly where you are and can use a debugger to follow code
thereafter if you can debug c code.
^log ( ... )
This allows you to print something directly to the users log file. If
you want it echoed to the console as well, you can do
^log(OUTPUT_ECHO This is my message)
.
You can actually append to any file by putting at the front of your output the word FILE in capital letters followed by the name of the file. E.g.,
^log(FILE TMP/mylog.txt This is my log output.)
Logging appends to the file. If you want to clear it first, issue a log command like this:
^log(FILE TMP/mylog.txt NEW This is my log output)
The new
tells it to initialize the file to empty.
Additionally you can optimize log file behavior. If you expect to write to a file a lot during a volley (eg during :document mode), you can leave the file open by using
^log(OPEN TMP/mylog.txt This is my log output.)
which caches the file ptr. After which you can write with OPEN or FILE equivalently. To close the file use
^log(CLOSE TMP/mylog.txt)
By default, ^log acts like output to user, converting escaped n, r, and t into their actual ascii characters. The flag RESPONSE_NOCONVERTSPECIAL passed in will block this. You pass various flags as:
^logcode( flag OPEN ...) or
^logcode( (flag flag) OPEN ...)
^memorymark ()
Reading a document consists of performing a single volley of the
entire document. This can tie up a lot of memory in keeping facts,
dictionary entries, user variables, etc. If you are careful in what you
do, you can make the memory burden go away. ^memoryMark()
notes where memory is currently at, and is best done within the
document_pre topic. Then you can release memory after every sentence of
the document, so it doesn’t accumulate.
Multiple calls to memory mark stack up the data, so each call to ^memoryfree will undo one mark.
^memoryfree ( {$data})
This releases memory back to the most recent
^memorymark()
. It is best done after your main control of
the document bot has finished processing a sentence. Partly because the
analysis of the sentence is lost and so no later rules can pattern match
to it (though you can call ^analyze to reacquire your sentence).
E.g.,
topic: ~document_pre system repeat()
t: ^memorymark() # note start
Log(OUTPUT_ECHO \n Begin $$document ) # instant display
topic: ~main_control system repeat () # executed each sentence of document
u: (%document)
respond(~filter)
^memoryfree()
The caveats and warnings about how this works. Whenenver you free memory, the system will clear all fact sets. It will clear all user variables set after the memory mark (leaving the ones before alone). It will then release facts, text, and dictionary nodes created after the mark.
The only data you can pass out from a memoryMark/memoryfree zone is data stored on match variables (which have size limitations) or on the count field of a dictionary word of a preexisting word or in the argument (if supplied) of $data (hence a nice JSON structure you can pass).
^memorygc ()
This can function in either document mode or chat mode. It does what it can to release unused memory. It has restrictions in it does not work if you have facts with facts as fields or are in planning mode. It also discards saved sentence data, and all of your analysis data for the current sentence. It also discards all data in factsets.
JSON functions and JSON are described in the ChatScript JSON manual. Type is either array or object and a json composite with no content is created and its name returned.
^readfile ( TAB filepath )
^readfile ( TAB filepath )
reads a tsv (tab delimited
spreadsheet file) and returns a JSON array representing it. The lines
are all objects in an array. The line is an object where non-empty
fields are given as field indexes. The first field is 0. Empty fields
are skipped over and their number omitted.
^readfile ( TAB filepath 'function)
reads tsv and spreads
fields onto arguments of ’function, which it calls once per line
^readfile ( LINE filepath 'function)
reads any file and
passes each line untouched as the sole argument to the function.
Fomerly called ^jsonreadcsv.
Input lines can include:
:trace all or :trace none - manages tracing :quit or :exit - acts like
end of file :pause or :resume - ignores lines while paused
^burst ( {count once} data-source burst-character-string )
Takes the data source text and hunts within it for instances of the burst-character-string. If it is being dumped to the output stream then only the first piece is dumped.
If it is being assigned to a fact set (like @2
) then a
series of transient facts are created for the pieces, with the piece as
the subject and ^burst
^burst
as the verb and
object.
If it is being assigned to a match variable, then pieces are assigned starting at that variable and moving on to successively higher ones.
If burst does not find a separator, it puts out the original value. For assignment to match variables, it also clears the next match variable so the end of the list will be a null match variable.
If burst_character is omitted, it is presumed to be BOTH
_
(which joins composite words and names) and ” “, which
separates words.
If burst_character is the null string ““, it means burst into characters.
^burst
takes an optional first parameter
count
, which tells it to return how many items it would
return if you burst, but not to do the burst.
^burst
takes an optional first parameter once
which says split only into the first burst and then the leftover
rest.
^burst
has a special burst value digitsplit
which will split a number-text thing or a text-number thing into two
pieces (text thing and number thing). This is good for splitting a
currency thing lik USD25 or 25$.
^words ( someword )
Looks up the given word and returns all words matching it. Matching
includes the lower case form of it and any number of uppercase forms of
it. E.g, you might say ^words(ted)
and get back facts for
ted, Ted, TED.
The answers are a series of facts of the form (someword words words).
In addition to case switching, the system will automatically switch
words with underscores or blanks into words with changes in them to the
other (since CS stores phrases with underscores). So
^words("I love you")
can match phrases already in the
dictionary of:
I_love you
I_love_you
I
love you
I LOVE You
etc. Depending on which words are actually there (for example because they are parts of a fact).
^canon ( word canonicalform )
Same as :canon
during a :build
from a
table. Fails during normal execution not involving compiling.
^explode ( word )
Convert a word into a series of facts of its letters.
^extract ( source start end )
Return the substring with the designated offset range (exclusive of
end location). Useful for data extraction using ^popen and ^tcpopen when
combined with ^findtext
.
In addition to absolute unsigned values, start and end can take on offsets or relative values. A signed end is a length to extract plus a direction or shift in start:
^extract($$source 5 +2) # to extract 2 characters beginning at position 5
^extract($$source 5 -2) # to extract 2 characters ending at position 5
A negative start is a backwards offset from end.
^extract($$source -1 +1) # from end, 1 character before and get 1 character
^extract($$source -5 -1) # from end, 5 characters before and get 1 character before. i.e. the 6th char from end.
^extract($$source -5 -1000) # all characters until 5 from end
Note start offset is 0-based indexing, but if you used ^findtext, it was 1-based index so you probably need to subtract 1.
^findtext ( source substring offset {insensitive} )
Find case sensitive substring within source+offset and return offset
starting immediately after match. Useful for data extraction using
^popen
and ^tcpopen
when combined with
^extract
. $$findtext_start
is bound to the
actual start of the match. $$findtext_word
is bound to the
word index in which the match was found where one or more blanks
separate words. Indexing starts at 1 (same as sentence positional
notation).
An optional fourth argument insensitive
will match
insensitively.
Failing to match will generate a rule failure. If the source or
substring contains an _
, these will be converted to blanks
before execution, to allow that or the space notation to be considered
equivalent (unless your source or substring is literally an underscore
only).
If you want to find a newline or tab character, then pass in the
string \n
or \t
. That will find an actual
ascii character of such. If you want to find the ascii string
\n
, then use \n and \t to find them. Normal scripts don’t
have ascii newline or tab in them. You wrote the backslashed characters
and they are converted to the appropriate ascii characters on output to
the user. But if you have read data from an external source, it will
likely be the actual ascii characters.
You can use this with ^extract to retrieve text before or after the found data.
^flags ( word )
get the 64bit systemflags of a word.
^intersectwords ( arg1 arg2 optional )
Given two “sentences”, finds words in common in both of them. Output
facts will go to the set assigned to, or @0
if not an
assignment statement. The optional third argument, if it’s
canonical
, it will match the canonical forms of each
word.
^join ( any number of arguments )
Concatenates them all together, putting the result into the output
stream. If the first argument is AUTOSPACE
, it will put a
single space between each of the joined arguments automatically.
^actualinputrange ( start end )
Given the starting and ending word positions of an original input (what CS had after tokenization but before adjustments), this returns the range of where the words arose in the actual input. The return is a range whose start is shifted 8 bits left and ORed with the end position.
^originalinputrange ( start end )
Given the starting and ending word positions of an actual input (what CS sees after adjustments and what you normally pattern match on), this returns the range of where the words came from in the original input. The return is a range whose start is shifted 8 bits left and ORed with the end position.
^properties ( word {NOT_NEW})
Returns the 64bit properties of a word. An optional NOT_NEW 2nd argument will fail-rule if the word is not already in the dictionary.
^pos( part-of-speech word supplemental-data )
Generates a particular form of a word in any form and puts it in the
output stream.
Or tests a word to see if it is of a particular form. If it cannot
generate the request, it issues a RULE failure. Most combinations of
arguments are obvious. Here are the 1st & 3rd choices. For verbs
with irregular pronoun conjugation, supply 4th argument of pronoun to
use.
| part-of-speech | word/verb/number (+ supplement-data argument) |
action |
|---|---|---|
conjugate |
pos-integer (as returned from ^partofspeech ) |
returns the word with that part of speech (eg conjugate go #VERB_PAST_PARTICIPLE) |
raw |
integer 1 .. %length |
(returns the original word in sentence) |
syllable |
word | tells you how many syllables a word has |
hex64 |
integer-word | converts a number to 64bit hex |
hex32 |
integer-word | converts a number to 32 bit hex |
ismodelnumber |
word | return 1 if it is (mixed alpha/numeric). Fails otherwise. |
isinteger |
word | return 1 if it is all digits, fails otherwise |
isfloat |
word | return 1 if it is float, fails otherwise |
isuppercase |
word | return 1 if it begins with an uppercase letter, fails otherwise |
isalluppercase |
word | return 1 if it starts uppercase, and consists of entirely uppercase letters, hyphen, underscore and ampersand, fails otherwise |
ismixed case |
word | return 1 if it has both upper and lowercase letters, fails otherwise |
type |
word | returns concept, number, word, or unknown |
common |
word | returns level of commonness of the word |
grade |
word | returns grade level word learned - 0=kindergarten 2=1-2 4=3-4 6=5-6 8=beyond 6 |
verb |
verb | given verb in any form, return requested form |
present_participle |
verb | |
past_participle |
verb | |
infinitive |
verb | |
past |
verb | |
present3ps |
verb | |
present |
verb | |
verb |
match noun |
returns noun form matching verb (sing./plural). e.g. (walk match dog) -> walks |
aux |
auxverb pronoun |
returns verb form matching pronoun supplied. for do,have, be |
pronoun |
word flip |
changes person form for 1st and 2nd person |
adjective |
word more |
writes the adjective in its comparative form: fast -> faster |
most |
word | the superlative form. beautiful -> most beautiful |
adverb |
word more |
writes comparative form: strong -> strongly |
noun |
word proper |
return word as a proper noun (appropriately cased) |
lowercaseexist |
word | |
uppercaseexist |
word | |
singular |
word or a number | == 1 |
plural |
word or a number | > 1 |
irregular |
word | return value only for irregular nouns |
determiner |
word noun |
add a determiner “a/an” if it needs one |
place |
integer | return place number of integer |
capitalize |
word - same as uppercase- converts phrase to uppercase following rules for titles | |
uppercase |
word - same as capitalize- converts phrase to uppercase following rules for titles | |
mixcase |
word - lowercase all letters except first one and first ones after space, underscore, hyphen | |
lowercase |
word - convert all letters to lowercase | |
allupper |
word - convert all letters to uppercase | |
canonical |
word | see notes |
integer |
floatnumber | generate integer if float is exact integer |
preexists |
word | return 1 if word in any casing was already in the dictionary before this volley, fail otherwise. |
xref |
wordindex | Kind |
isfunction |
word | returns 1 is word is a defined function, or fails |
layer |
word | When was this word entered into the dictionary. Answers are:
wordnet , 0 , 1 , 2 ,
user . |
substitute |
word | returns the word this word gets substituted into. |
Rules for title capitalization are no longer capitalizingall in a phrase. Follows title capitalization rules regarding short conjunctions, prepositions, determiners.
Ordinary non-title Example:
# get first name (in a not English language), and capitalize
u: what's your first name?
#! giuditta
a: ( _* )
$_name = ^original(_0)
Nice to meet you, ^pos(capitalize $_name)
# if user enter giuditta, the rejoinder output: Nice to meet you, Giuditta
For ^pos(canonical), there is an optional third argument which is the concept name of the pos-tag. Foreign words may have multiple lemma forms based on part of speech. E.g., in the German dictionary you can find this entry:
Informationstechnische ( NOUN ADJECTIVE NOUN_SINGULAR NOUN_PLURAL ) lemma=`informationstechnisch`Informationstechnische` ADJA NN
which says there are two forms of canonical, one for ADJA (adjective)
and one for NN (noun). If you don’t specify a 3rd argument, you get the
first one (ADJA). If you specify ~ADJA
you get the first
and if you specify ~NN
you get the second.
If your third argumernt is all
then the list of all
canonical forms is returned with | separating the entries.
Parsing links things like prepositional phrases to nouns they describe, indirect objects to verb, etc. The xref returns the index of the word in sentence cross-referenced by some parser data found at the designated in wordindex. Kind includes: crossreference, indirectobject, object, complement, phrase, verbal, clause. Returns 0 if no xref found.
^decodeInputtoken ( number )
Display the text values of tokenflag bits. You can pass it
%token
to see the meanings of the current sentence analysis
or $cs_token
to see what you have current set as token
controls.
^decodepos ( pos location )
Translates into text the 64bit pos data at given location.
location
can be a position in the sentence (1… number of
words) or a match variable found from some location in the sentence).
See dictionary.h for meanings of bits. Type word will classify
word as concept, word, number, or unknown.
^decodepos ( role location )
Returns the text of the role data of the given location.
^partofspeech ( location )
Gets the 64-bit part-of-speech information about a word at
location
, resulting from parsing. Location can be a
position in the sentence (1… number of words) or a match variable found
from some location in the sentence). See dictionary.h for
meanings of bits.
^phrase ( type matchvar )
Can be used to retrieve all of a prepositional phrase or a noun
phrase. type
is noun
,
prepositional
, verbal
, adjective
.
Optional 3rd argument canonical
will return the canonical
phrase rather than the original phrase. E.g.,for input:
u: (I ~verb _~directobject) $tmp = ^phrase(noun _0)
with input I love red herring $tmp
is set to
red herring
^role ( location )
Gets the 32-bit role information about a word at location, resulting from parsing. Location can be a position in the sentence (1… number of words) or a match variable found from some location in the sentence). See dictionary.h for meanings of bits.
^tally ( word {value} )
Only valid during current volley. You can associate a 32-bit number
with a word by ^tally(test 35)
and retrieve it via
^tally(test)
.
^rhyme ( word )
Finds a word in the dictionary which is the same except for the first letter (a cheap rhyme).
^substitute ( mode find oldtext newtext)
Outputs the result of substitution. Mode can be character or word or
insensitive. In the text given by find, the system will search for
oldtext and replace it with newtext, for all occurrences. This is
non-recursive, so it does not also substitute within replaced text.
Since find is a single argument, you pass a phrase or sentence by using
underscores instead of spaces. ^substitute
will convert all
underscores to spaces before beginning substitution and will output the
spaced results.
In character mode, the system finds oldtext as characters anywhere in newtext. In word mode it only finds it as whole words in newtext. Finding is case sensitive, unless you use the argument insensitive, which will do character mode insensitive match. You can select insensitive word match by making the first argument be a text string containing the normal 1st argument values, e.g. insensitive word
^substitute(word "I love lovely flowers" love hate)
outputs I hate lovely flowers
^substitute(character "I love lovely flowers" love hate)
outputs I hate hately flowers
^spell ( pattern fact-set )
Given a pattern, find words from the dictionary that meets it and create facts for them that get stored in the referenced fact set. The facts are created with subject 1, verb word, and object the found word. The pattern is a text string describing possibly the length and letter constraints.
If there is an exact length of word, it must be first in the pattern. After which the system matches the letters you provide against the start of the word up until your pattern either ends or has an asterisk or a period. A period means match any letter.
An asterisk matches any number of letters and would normally be
followed by more letters. The *
will swallow letters in the
dictionary word until it can match the rest of your given pattern. It
will keep trying as needed. Eg.
^spell(4the @1) will find them but not their
^spell(am*ic @1) will find American
^spell(a*ent @1) will find abasement
^spell(h.l.o @1) will find hello
^spellcheck ( input dictionary {tolerance} )
Input is tokenized words separated by spaces, dictionary is json array of words. Outputs the input words adjusted by any spelling correction.
Useful if you read dynamic menus from an API endpoint and then want to match user input against that menu, whose elements may not be in the main CS dictionary.
By default up to two characters in a word can be changed. The tolerance argument can be either an integer specifying the number of characters that can be changed, or a floating point percentage to indicate the proportion of characters in each word that can be changed.
^sexed ( word he-choice she-choice it-choice )
Given a word, depending on its sex the system outputs one of the three sex choices given. An unrecognized word uses it.
^sexed(Georgina he she it)
would return she
^uppercase ( word )
Is the given word starting with an uppercase letter? Match variable binds usually reflect how the user entered the word. This allows you to see what case they entered it in. Returns 1 if yes and 0 otherwise.
^format( formatstring value)
This is a thin wrapper over sprintf. The first argument is a string which is the format string for sprintf. The second argument is the number to convert. For integer, if you use a %d format, you will be using a 32-bit value. For ll formats you will be using 64-bit but it won’t work well on Windows output because Windows uses their own sprintf notation.
^addproperty ( word flag1 … flagn )
given the word, the dictionary entry for it is marked with additional properties, the flags given which must match property flags or system flags in dictionarySystem.h. Typically used to mark up titles of books and things when building world data.
In particular, however, if you are adding phrases or words not in the
dictionary which will be used as patterns in match, you should mark them
with PATTERN_WORD
. To create a dynamic concept, mark the
set name as CONCEPT.
You can also add fact properties to all members of a set of facts via
^addproperty(@4 flag1 … flagn).
These flags are also predefined in dictionarysystem.h and you can use
some of the predefined but meaningless ones to do what you want. These
are User_flag4
, User_flag3
,
User_flag2
, User_flag1
.
See also ^recordDictionaryChanges.
^recordDictionaryChanges(0 or 1)
WIth 1, starts tracking changes to properties and systemflags on words being modified. This will allow you to undo those changes on the fly using ^revertDictionaryChanges. With 0, saving changes is suspended until you turn it on again. The function will return a number that represents this list of changes, so that you can choose to revert them or restore them in the future (on this volley only). Normally when you start recording changes, it starts a new list. You can tell it to resume accumulating on an old list by passing its number as an optional second argument.
Note that you don’t have to use recordDictionaryChanges to protract against changes you make to words already in the dictionary prior to the volley because the engine will automatically restore them to their prior state when the volley ends. But if ^recordDictionaryChanges is on, it will also record those changes.
^revertDictionaryChanges
This undoes those changes(turning recording off). Even if you never revert changes yourself, at the end of the volley all new words disappear from the dictionary and any changes to old words are automatically reverted without your tracking them. If you give it no arguments, it reverts the current accumlation of changes from the latest ^recordDictionaryChanges. But you can give it a number returned from a previous ^recordDictionaryChanges(0) to replace that collection of changes that had been accumulated.
This function returns a number representing the list it just reverted, which can be used with ^recordDictionaryChanges or ^replayDictionaryChanges
^replayDictionaryChanges
Restores changes done by a record and later reverted (allowing you to insert changes not recorded). It takes a number argument as returned by ^recordDictionaryChanges when you turn off changes.
^define( word )
Output the definition of the word. An optional second argument is the part of speech: noun verb adjective adverb, which will limit the definition to just that part of speech. Never fails but may return null.
The second argument can also be all
which means list all
definitions per part of speech, not just the first. And it can be the
third optional argument so you can get all meanings of a word as a noun,
for example.
If you name a meaning, like (dog~1) then the definition will be for that specific meaning.
^synset
Words in the Wordnet dictionary have multiple meanings and each meaning is linked together as ‘synsets’ with meanings of other words. To name a meaning is to give the word with an index after it. For example, the word dog has 7 meanings. Six are nouns and 1 is a verb. Of the nouns, one meaning is dog is dog~1, your classic canine dog. Its synset includes Canis_familiaris~1 and domestic_dog~1.
In addition words have a parent, wherein the word is a refinement of the parent. CS considers some meaning to be the ‘master’ of the synset, and only that is hooked to its parent.
^synset(word~n) - return a list of facts naming synonyms of the meaning
^synset(synsetmaster word~n) - returns the synset master of word ^synset(synsetup word~n) - if word is a master, returns the word it is a refinement of ^synset(word) - return a list of facts naming all the meanings this word has.
^hasanyproperty ( word value )
Does this word have any of these property or systemflag bits? You can have up to 5 values as arguments, e.g.,
^hasproperty(dog NOUN VERB ADJECTIVE ADVERB PREPOSITION)
If the word is not in the dictionary, it will infer it, allowing it
to handle things like verb tenses. If you want to insure the word
already exists first, you should do ^properties(dog) AND
^hasproperty(dog xxx)
since property fails if the word is
not found.
^hasallproperty( word value )
Does this word have all property or systemflag bits mentioned? You can have up to 5 values as arguments, e.g.,
^hasallproperties(dog NOUN VERB ADJECTIVE ADVERB PREPOSTION)
Values should be all upper case. If the word is not in the dictionary, it will infer it, allowing it to handle things like verb tenses. If you want to insure the word already exists first, you should do
^properties(dog) AND ^hasproperty(dog xxx)
since property fails if the word is not found.
^removeproperty ( word value )
Remove this property bit from this word.
This effect lasts until the system is reloaded. Value should be all
upper case. Value is normally a system flag value or a property value
from dictionarysystem.h
which does not need a hash in front
of it (system will look up the name).
word can be in doublequotes. And there are two internal bits
that are also allowed to be removed: CONCEPT
and
HAS_SUBSITUTE
.
You can use HAS_SUBSTITUTE
to disable some standard
substitution in LIVEDATA, but you can’t apply this at build time because
the system won’t remember. Instead call it from ^csboot
during startup. For example, in LIVEDATA interjections file, there is an
entry:
<surprise ~emosurprise
But if you didn’t want surpise at the start of a sentence declared
the interjection ~emosurprise
, you could do
^removeproperty("<surprise" HAS_SUBSTITUTE)
And the dictionary has some words which are composites, like iced_coffee and it will automatically convert the two words into a single token. If you wanted to stop this behavior, you could disable this composite word via
^removeproperty(iced_coffee NOUN)
since it is declared as a NOUN (you can see with
:word iced_coffee
). You can do this to nouns, adjectives,
adverbs.
^walkdictionary ( '^function )
calls the named output macro from every word in the dictionary. The function should have 1 argument, the word.
^walktopics ( '^function )
calls the named output macro for every topic the current bot can access. The function should have 1 argument, the topic name.
^walkvariables ( '^function )
calls the named output macro for every global permanent user variable currently non-null. The function should have 1 argument, the topic name. It does not involve $$ or $_ variables, or bot variables.
^Iterator ( ? member ~concept )
An iterator is a repeatable fact query that allows you to walk
through each member of a concept, either at top level or recursively.
Useful in conjunction with a loop()
, the function is
defined in the planning manual but can be used outside of planning. You
can have one iterator in progress per rule.
loop () # unload every resource on board
{
$$resource = ^iterator(? member ~resources)
...
}
^wordAtIndex ( ({original, canonical} n))
^wordAtIndex
retrieves the word from the current
sentence at the index given, as either the original word or as a
canonical word (as a match variable sees it)
^wordAtIndex ( canonical n n1)
gathers a range from n
thru n1.
^wordAtIndex ( original "_0")
gathers a range from that
which _0 represents (but uses the original data so it is not like merely
saying _0, which may not have real data if you did an arbitrary
assignment to it setting its position).
^disable ( what ? )
What can be topic
or rule
or
inputrejoinder
or outputrejoinder
or
save
or write @set
.
If topic
, the next argument can be a topic name (with or
without ~
or just ~
meaning the current
topic). It means to disable (BLOCK) that topic.
If a rule
, you erase (disable) the labeled rule (or rule
tag and ~
means the current rule).
If outputrejoinder
, it cancels the current output
rejoinder mark, allowing a new rule to set a rejoinder.
If inputrejoinder
then it cancels any pending rejoinder
on input.
If save
, then the user data will not be saved. It’s as
though this interchange didn’t happen.
If ^disable(write @1)
then factset @1
will
not be written out into user data and is junk at the next volley
(normally this is true).
You can also disable the inputrejoinder
with
^setrejoinder(input)
and the output rejoinder with
^setrejoinder(output)
.
^enable ( what ? )
What can be topic
or rule
or
save
or write @set
.
If topic
, thenext argument can be a topic name or the
word all
for all topics . Designated topics will be enabld
(unBlocked).
If a rule
, the label (or rule tag) will be enabled,
allowing the rule to function again. If save, then it reenables saving
user context if you had disabled it.
If ^enable(write @1)
then factset @1
will
be written out into user data and is restored at the next volley
(normally this is not true).
^length ( what )
If what is a fact set like @1
, length returns how many
facts are in the set. If what is a word, length counts its characters.
If what is a concept set, length returns a count of the top level
(nonrecursive) members. If the name of a json array or object, returns
how many top level elements it has.
^pick ( what )
Retrieve a random member of the concept if what is a concept. Pick is also used with factsets to pick a random fact (see FACTS MANUAL).
For a concept, if the member chosen is itself a concept, the system will recurse to pick randomly from that concept. If the argument to pick is a $var or _var, it will be evaluated and then pick will be tried on the result (but it won’t recurse to try that again).
If the argument is a JSON object it randomly picks a fact whose verb/object is a key-value pair. If the argument is a JSON array, it randomly picks a fact whose verb is the index and whose object is the value. The fact id returned can be used with ^field or you can use something like $result.object to get the specific object.
^reset ( what ? )
What can be user or topic or factset or VARIABLES or FACTS or HISTORY.
If what is user, the system drops all history and starts the user afresh from first meeting (launching a new conversation), having erased the user topic file.
If what is a factset, the “next” pointer for walking the set is reset
back to the beginning.  If what is VARIABLES then it
sets all global user variables to NULL, leaving alone
$$, $_ and bot variables. If what is FACTS, it kills all permanent user
facts If what is HISTORY it forgets what was said previously.
^findfact ( subject verb object )
The simplest fact find involves knowing all the components (meanings)
and asking if the fact already exists. If it does, it returns the index
of the fact. If it doesn’t it returns FAILRULE_BIT
.
^query ( kind subject verb object)
The simplest query names the kind of query and gives some or all of
the field values that you want to find. Any field value can be replaced
with ? which means either you don’t care or you don’t know and want to
find it. The kinds of queries are programmable and are defined in
LIVEDATA/queries.txt
(but you need to be really advanced to
add to it). The simplest query kinds are:
| query kind | description |
|---|---|
| direct_s | find all facts with the given subject |
| direct_v | find all facts with the given verb |
| direct_o | find all facts with the given object |
| direct_sv | find all facts with the given subject and verb |
| direct_so | find all facts with the given subject and object |
| direct_vo | find all facts with the given object and verb |
| direct_svo | find all facts given all fields (prove that this fact exists) |
| Unipropogate | find how subject joins into the object set |
If no matching facts are found, the query function returns the RULE fail code.
?: (do you have a dog) ^query( direct_svo I own dog) Yes.
If the above query finds a fact (I own dog) then the rule says yes. If not, the rule fails during output. This query could have been put inside the pattern instead.
^query( kind subject verb object count fromset toset propagate match )
query can actually take up to 9 arguments. Default values are
?
.
The count argument defaults to -1
and indicates
how many answers to limit to. When you just want or expect a single one,
use 1 as the value.
fromset specifies that the set of initial values should come from the designated factset.
Special values of fromset are user
and
system
which do not name where the facts come from but
specify that matching facts should only come from the named domain of
facts.
toset names where to store the answers. Commonly you don’t name it because you did an assignment like
@3 = ^query(...)
and if you didn’t do that, toset defaults to @0
so
if (^query(direct_s you ? ?))
puts its answers in @0
. It is equivalent to:
if (^query(direct_s you ? ? -1 ? @0))
You can also simultaneously query and unpack a single field from the first matching fact by naming the field on the to argument.
$$tmp = ^query(direct_sv you eat ? 1 ? @5object)
Typically you would do this if you expected only a single fact and
were trying to find a specific field value. So you’d set the limit to 1.
If a fact is found, it is stored in @5
, and the object
field is pulled off and stored onto $$tmp
. If no fact is
found, the query does not fail, $$tmp is merely set to null.
The final two arguments only make sense with specific query types that use those arguments.
For unipropogate
, if you have these concepts;
concept: ~things (~animals ~vegetables ~minerals)
concept: ~animals (~canine ~feline)
concept: ~canine (dog)
Then ^query(unipropogate dog ? ~things 1)
would return
(~animals member ~things)
.
Note that the set to be found (~things
) is not expanded.
Normal queries expand any reference to a set into all of its members and
expand simple words to the entire wordnet hierarchy above it. You can
block this expansion behavior by putting a single quote in front. Note
for the idiom '_0
which means the original form of the
match variable, you have to use two quotes: ''_0
.
^query(direct_svo 'bomb ''_0 '$$tmp)
unipropogate
expects a set as its object argument, so it
does not need to be quoted. A query can also be part of an assignment
statement, in which case the destination set argument (if supplied) is
ignored in favor of the left side of the assignment, and the query
doesn’t fail even if it finds no values. E.g.,
@2 = ^query(direct_sv I love you)
The above query will store its results (including no facts found) in
@2
. Queries can also be used as test conditions in patterns
and if constructs. A query that finds nothing fails, so you can do:
u: ( dog ^query(direct_sv dog wants ?)) A dog wants @0object.
You can also do !^query
. Or
if (^query(direct_vo ? want toy)) {@0subject wants a toy.}
^first ( fact-set )
Retrieve the first fact, e.g.
_1 = ^first(@1all)
^last ( fact-set )
Retrieve the last fact.
^pick ( fact-set )
Retrieve a random fact. Removing the fact is the default, but you can
suppress it with the optional second argument KEEP
, e.g.,
_1 = ^last(@1all)
gets the last value but leaves it in the
set.
You can erase the contents of a fact-set merely by assigning null into it.
@1 = null
This does not destroy the facts; merely the collection of them.
^sort ( {alpha alphabetic age} @0 … )
You can sort a fact set which has number values as a field.
^sort ( fact-set {more fact sets} )
The fact set is sorted from highest first. By default, the subject is
treated as a float for sorting. You can say something like
@2object
to sort on the object field. You can add
additional factsets after the first, which will move their contents
slaved to how the first one was rearranged. Eg.
^sort(@1subject @2 @3)
will perform the sort using the subject field of @1
, and
then rearrange @2
and @3
in the same way
(assuming they have the same counts). Instead of sorting by numeric
value, can do an alpha sort or an oldest fact first sort similar to the
normal sort.
^delete ( factset or factid or jsonid )
If you actually want to destroy facts, you can query them into a fact-set and then do this:
^delete(@1)
And all facts in @1
will be
deleted and the set erased You can also delete an individual fact who’s
id is sitting on some variable
^delete($$f)
And you can delete a json array or object,
including all of its substructure the same way.
If you pass something that is not deleteable, the system will do nothing and does not fail.
^length ( factset or ~set or jsonid or word )
If you want to know how many facts a fact-set has, you can do this:
^length(@1)
- outputs the count of facts
Likewise how many top level members of a concept (not recursive). Or how many fields in a json object or elements in a json array. Or how many characters in a word. A Null value for the argument is legal, and is of length 0.
Note: if you do length of a name that starts with ~
and
is not a defined concept set, the function fails rather than return
0.
^nth ({factset,concept,jsonstruct} count )
If you want to retrieve a particular set fact w/o erasing it, you can use
^nth(@1 5)
where the first argument is like ^first
because you also
specify how to interpret the answer and the second is the index you want
to retrieve, eg.,
^nth(@0object 5)
An index out of bounds will fail. Factsets are always numbered 1…n,
so the first element is, in fact, ^nth(@0object 1)
would
correspond to @0object
or ^first(@0object)
Similarly you can do nth(~concept 2)
to retrieve the
third member of a concept (numbering starts at 0).
You can also do nth
of a JSON object (returns the factid
of the nth key/value pair) or JSON array (returns the factid of the nth
index/value pair).
^unpackfactref
examines facts in a set and generates all fact references from it. That is, it lists all the fields that are themselves facts.
@1 = ^unpackfactref( @2)
All facts which are field values in @2
go to
@1
. You can limit this:
@1 = ^unpackfactref(@2object)
only lists object field facts, etc
^save ( factset boolean )
Unlike variables, which by default are saved across inputs, fact sets are by default discarded across inputs. You can force a set to be saved by saying:
^save(@9 true) # force set to save thereafter
^save(@9 false) # turn off saving thereafter
^makereal ( {set , factid} )
If you give this a factset, it will convert any transient facts in
that set into permanent. If you give this a factid, it will convert all
transient facts created after that id into permanent. This might allow
you, for example, to call ^jsonopen
and get back a
transient JSON structure and after inspection you could convert it to
permanent if you wanted to.
^setFactOwner ( {set , factid} idbits )
In multibot deploys, allows you to change which bot owns this set of facts. Useful if you want to allow a bot to share facts owned by another bot.
^addproperty ( set flag )
Add this flag onto all facts in named set. Typically you would be
adding private marker flags of yours. If set has a field marker (like
@2subject
) then the property is added to all values of that
field of facts of that set.
^conceptlist ( kind location )
^conceptlist(kind location {filter})
generates a list of
transient facts for the designated word position in the sentence of the
concepts (or topics or both) referenced by that word, based on kind
being CONCEPT or TOPIC or BOTH.
Facts are (~concept ^conceptlist location)
where
location is the range location in the sentence (start <<8 +
end).
^conceptlist( CONCEPT 3) # absolute sentence word index
^conceptlist( TOPIC _3) # whereever _3 is bound
Otherwise, if you don’t use an assignment, it stores into set 0 and
fails if no facts are found. Any set already marked
^Addproperty(~setname NOCONCEPTLIST)
will not be returned
from ^conceptlist
.
Special preexisting lists you might use the members of to exclude
include: ~pos
(all bits of word properties)
~sys
(all bits of system properties) and ~role
(all role bits from pos-tagging). Only one instance of a concept or
topic will be returned as a fact.
If a concept reference covers multiple words (like an unmerged New
York City), the concept is indexed at the first word. The location
returned is: (start <<8) | end
.
If you omit the 2nd argument (location), then it generates the set of
all such in the sentence, iterating over every one but only doing the
first found reference of some kind. If you use ^mark
to
mark a position, both the word and all triggered concepts will be
reported via ^conceptlist
.
But if the mark is a non-canonical word, mark does not do anything about the canonical form, and so there may be no triggered concepts as well. (Best to use a canonical word as mark).
If you add the optional 3rd argument, it will filter concepts to be only those that start with the filter characters.
@0 = ^conceptlist(CONCEPT _0 ^"~bot-")
retrieves only concepts that start with ~bot-
.
^wordinconcept ( word conceptname )
Takes any casing of a word and finds which casing in the dictionary is a member of the concept. When you memorize a word, you will get how the user spelled it. Eg., Ebay, when the dictionary actually has eBay. The correct spelling of the word can be found this way as a member of a concept. And composite words using either spaces or underscores can be found as well and returns the correct notation.
^createattribute ( subject verb object flags )
This is just like ^createfact
, except that it only
allows one fact with this subject and verb to exist.
 If, for example, you had a
fact (car1 cost $1500)
and executed
^createattribute(car1 cost $1000)
then after this the $1500 fact would no longer exist and only the new price fact would exist.
Note
if you have facts that reference facts that would be killed
off, the createattribute call will decline to create a new fact and fail
instead. Also, don’t have those old facts as values of variables or
factsets because those values will become erroneous. The system will not
stop you, but you cannot guarantee the results after that. BE CAREFUL
you don’t create facts where the verb and object are intended to be
constant and the subject varies. It won’t work correctly.
(car space 10)
- fine if 10 can vary
(10 space car)
- wrong if 10 can vary
See also ^revisefact
which is probably easier to use for
most cases.
^createfact ( subject verb object flags )
The arguments are a stream, so flags
is optional.
Creates a fact of the listed data if it doesn’t exist (unless flags
allows duplicates). Or ^createfact($$tmp)
or some other
variable that evaluates to a fact stream will also create/find a fact.
$$tmp
might have been written previously using
WriteFact.
^writefact ( F )
Given a fact index such as might be returned by
first(@1fact)
, writes out the fact in std text notation
(such as done by ^export or written into user files). (see
^createfact
).
^revisefact ( factid subject verb object )
The existing non-dead user fact will have fields replaced when
arguments are not null
. You cannot change type of field, so
a fact subject will require a factid as subject, etc.
^delete(set)
erase all facts in this set. This is the same as
^addfactproperty(set FACTDEAD)
.
^field ( fact fieldname )
given a reference to a fact, pull out a named field. If the fieldname is in lower case and the field is a fact reference, you get that number. If the fieldname starts uppercase, the system gives you the printout of that fact. Eg for a fact:
$$f = createfact (I eat (he eats beer))
^field( $$f object)
returns a number
(the fact index)
and ^field($$f Object)
returns (he eats beer)
as the translation of the fact into
text.
Fields include: subject
, verb
,
object
, flagsv
, all
(spread onto
3 match variables, raw (spread onto 3 match variables).
all
just displays a human normal dictionary word, so if
the value were actually plants~1
you’d get just plants
whereas raw would return what was actually there
plants~1
.
You can also retrieve a field via $$f.subject
or
$$f.verb
or $$f.object
or
$$f.flags
.
^find ( setname itemname )
given a concept set, find the ordered position of the 2nd argument within it. ^Output that index (0-based). Used, for example, to compare two poker hands.
^findmarkedfact ( subject verb mark )
given the arguments, start at subject, follow all facts having the verb, and stop if you can find a fact with the mark given.
^first ( fact-set-annotated )
Retrieve the first fact. You must qualify with what you want from it.
Retrieve means the fact is removed from the set.
^first(@0subject)
retrieves the subject field of the first
fact. Other obvious qualifications are verb, object, fact (return the
index of the fact itself), all (spread all 3 fields onto a match
variable triple, raw (like all but all displays just a normal
human-readable word like plant whereas raw displays what was actually
there, which might have been plant~1
).
^flushfacts ( factid )
kills all facts created after this one. To use effectively, you need
to create an initial dead fact e.g,
$$marker = ^createfact(junk marker data FACTDEAD)
and then
if you want to cancel sentence processing because, for example, you
intend to replace this sentence with a new one (like with pronoun
resolution), you can erase any facts you created while doing this
sentence by doing ^flushfacts($$marker)
.
^gambittopics ()
finds user topics (not system topics) with gambits remaining. If you
use it in a fact-set assignment statement, it stores all topics found as
facts (topicname ^gambittopics topicname
). You can then
display them or use them as you wish E.g.
@1 = ^gambittopics()
^gambit( ^pick(@1)) # randomly issue a gambit
Otherwise, if you don’t use an assignment, it stores into set 0 and fails if no facts are found.
^intersectfacts ( from to )
Sees what facts in the from set are in common with the to set. You specify what field to intersect on by naming a field of the to set (or none). Eg.,
^intersectfacts(@0 @1object)
will find facts in set 0 whose objects match any in set 1. If you don’t name a field, you have to find exact matches on the entire fact. You need to assign the result to a new fact set, which will contain all matching facts from the from set.
@2 = ^intersectfact(@0 @1object)
^keywordtopics ()
Lists topics and priority values for matching keywords in input. An
optional argument if gambit
, will ignore topics without
available gambits. The verb used is: ^keywordtopics
.
Note: it does not attempt to match the topic you are currently in, as the normal control scripts should already have tried that topic before coming to the more random thrashing of ^keywordtopics.
In a multiple-bot build with bots with variations of the same code, a
topic name in one bot may be ~xxx and in another its xxx2
(2nd copy). Things work fine unless you explicitly test the name like
if ($_topic == ~introductions)
because the $_topic name may
have a suffix. You should eliminate the suffix for the comparison. Or do
something like:
if (^substitute(character $_topic ~introductions x FAIL)) {...}
^last ( fact-set-annotated )
Retrieve the last fact - see ^first
for a more complete
explanation.
^length ( word )
puts the length of the word into the output stream. If word is actually a fact set reference (e.g., @2 ), it returns the count of facts in the set.
^makereal ()
Convert all user facts that are transient into non-transient facts. Probably only useful when using plans, which generate transient facts representing the state of the world and you want those planned world facts to become the current real facts.
^next ( FACT fact-set-annotated )
Allows you to walk a set w/o erasing anything. See
^first
for more complete description of annotation, the
distinction between next and ^first
is that next does NOT
remove the fact from the set, but moves on to each fact in turn. You can
reset a set with
^reset(@1)
then loop thru it looking at the subject field with
loop() { _0 = next(FACT @1subject) }
^pendingtopics ()
List of currently pendings topics (interesting)
^pick ( ~concept )
Retrieve a random member of the concept. Pick is also used with
factsets to pick a random fact (analogous to ^first
with
its more complete description).
^querytopics ( word )
Get topics of which word is a keyword and which are not system topics and which have gambits (not necessarily unused), returns as fact triples of word, “a”, topicname. If used in an assignment to a set, it will not fail, but it may return 0 elements. If not used in an assignment, then it will use set @0 and will FAIL if no topics are found.
^removeproperty ( set flag )
remove this flag from all facts in named set. Typically you would be
removing private marker flags of yours or making transient facts
permanent.If set has a field marker (like @2subject
) then
the property is added to all values of that field of facts of that
set.
^reset ( @1 )
reset a fact set for browsing using ^next.
^query ( kind subject verb object )
see writeup earlier.
^save ( set )
mark set to be saved with user data from here on
^sort ( set )
sort the set.. doc unfinished.
^unduplicate ( set )
remove duplicate facts from this set. The destination set will be named in an assignment statement like:
@1 = ^unduplicate(@0)
Normally this merely removes duplicate facts. If you specify a field as well, no facts having that field duplicated will be kept either. Eg
@1 = ^unduplicate(@0object)
^uniquefacts ( from to )
Sees what facts in the from set are not in common with the to set. You specify what field to intersect on by naming a field of the to set (or none). Eg.,
^uniquefacts(@0 @1object)
will find facts in set 0 whose objects do not match any in set 1. If you dont name a field, you have to find exact matches on the entire fact not in the 2nd set.
^unpackfactref ( set )
Find all facts in set which have facts as fields and then make THOSE facts be the facts of the set. The destination set will be named in an assignment statement like:
@1 = ^unpackfactref(@0)
^changebot(botname botid) allows a bot to pretend to be another bot and access its data, functions, and topics. Variables are not affected by this. The user topic file will remain as the user came into the server.
This dumps the message into the LOGS/bugs.txt file.