Managing Your JanusNode's TextDNA

How is this JanusNode run?

You can configure your JanusNode's main function- text-generation- in two main ways: by adding or deleting words to the JanusNode's database of words (as described elsewhere), and by adding, deleting, or editing the TextDNA that the JanusNode uses to specify the dynamic structure of its CryptoPsyche (a virtual structure that is analogous to a mind, but stupider). The term 'TextDNA' is an acronym for 'Text-based Delimited Natural-Language Algorithms'. TextDNA is composed of the TextDemons and TextDNA that specifies the manner in which a JanusNode strings together words. In keeping with its simple but utterly idiosyncratic character, a JanusNode uses a simple but utterly idiosyncratic 'language' to specify its textDNA. Your JanusNode comes with tools to automatically generate its own textDNA. With your JanusNode to help guide you, you can have the pleasure of generating your own textDNA even if you know nothing about software control of the symbiot hardware on which a JanusNode runs.

A JanusNode stores its textDNA in text files in a folder named 'textDNA' insides the 'JanusNode Resources' folder. You can edit or create any textDNA file (or any other text file) from within your JanusNode, since your JanusNode can retrieve and display text files in any of its editable fields (that is, any of the eight built-in BrainFood fields, as well as the 'textDNA' field itself).


The textDNA used by your JanusNode can call textDemons, which are very similar to textDNA in their structure, but which must defined as part of one line of textDNA in order to be used. The main purpose of textDemons is to allow you, the user, to manipulate arbitrarily complex chunks of the textDNA as a single unit. For example, most single lines of textDNA will encode what human life forms refer to as 'a noun phrase'. It is convenient to define a noun-phrase textDemon which can be called in each line of the textDNA, rather than re-defining the noun phrase every time we want to use one. TextDemons are described in detail in the next section.


We begin here with a description of the textDNA itself.

Along with the any of the names from the text files in the 'BrainFood' folder, the following symbols are recognized in textDNA:

TextDNA may also contain the names of built-in functions and user-defined textDemons. The built-in functions are described below. The textDemons are totally user-configurable and so cannot be specified in this documentation, but their characteristics are defined below.

All of these symbols must always be separated from each other with a space.


Asterisks serve as comment markers. Any line containing an asterisk is simply ignored by a JanusNode. For this reason your JanusNode cannot produce asterisks directly. If you really need them. let me know: there is a way to do it indirectly, but it is too esoteric to get into here.


When it encounters one of the BrainFood file-names in a line of TextDNA, a JanusNode randomly chooses a word from that file. File names (and, indeed, any other item) must always be followed in textDNA by an integer between 1 and 100, indicating a percentage likelihood that a word from that file will be printed. For example, the phrase 's_nouns 90' appearing in textDNA will cause a singular noun to be printed with a 90% certainty. There is therefore a 10% chance that nothing will be printed.


The word 'punctuate' indicates that JanusNode should randomly punctuate. JanusNode punctuates with either a period or a comma 80% of the time. The remaining 20% of the time, it will use either a dash, an ellipsis, or a colon. The word 'punctuate' must also be followed by a number representing the likelihood of the punctuation being printed. You should be aware that there are several other ways of punctuating in JanusNode, which are explained later in this documentation.


The word 'return' indicates that JanusNode should insert a linefeed (a 'carriage return', to use an anachronism). It too must be followed by a percentage likelihood.


Words or phrases surrounded by quotes (and followed by the obligatory likelihood) are inserted verbatim (minus the quotes) by a JanusNode.

JanusNodes recognize most words that require special treatment when adding 'ing', 's', or 'ed'- so, for example, adding 'ed' after 'go' will be properly recognized as indicating 'went', and so on. However, English is extremely irregular and your JanusNode is certain to make the occasional mistake in dealing with these irregularities. After trying for some time to write a comprehensive function myself, I gave up and instead added user-configurable correction and extension resources to your JanusNode. You can correct any errors yourself, by adding them to the relevant files in the 'Irregulars' folder inside your 'JanusNode Resources' folder. There are three files, named 'Ing'.'S', and 'Ed'. When a JanusNode encounters any of the corresponding three key words, it checks inside the relevant files to see if there is a entry for the word under consideration. If there is, it uses that entry. If there is not, it proceeds to use its built-in functions, which will regularize irregulars. The entries in each 'Irregulars' folder are very simple: each entry (one per line) consists of the word to be dealt with, followed by a comma and its proper handling. For example, in the 'S' file there is an entry:

platypus,platypi

This tells your JanusNode that the proper plural for 'platypus' is 'platypi'.

If the 'Not English' button in the Control Panel is checked, the three keywords 'ing', 's' and 'ed' are not treated specially.

Non-English users will note that they can use the 'Irregulars' files to add their own conjugations and pluralizations- though they will have to list every one they want to use, unless their language overlaps with English in its treatment of the various special categories.

 

The above constitutes the 'bare bones' of the structure of TextDNA. Other than lines containing asterisks, every line has the same format. Each line begins with a number (see the next paragraph), which is then followed by an optional subject marker (also described below) which must be the first element after the number. After this every line of TextDNA consists of pairs made up of a keyword (i.e., one of the word-list file names, or the word 'punctuate', or 'return', or any word or phrase within quote marks) and a the number representing a percentage likelihood. A line is terminated with a carriage return. Note that a single line of TextDNA may look like several lines because TextDNA is automatically wrapped at the end of the field. The paragraph that you are now reading, for example, is only a single line long, since it has only one carriage return in it, but (unless you are viewing it on an extraordinarily wide screen!) it has been wrapped to cover several lines.


The number which must appear at the beginning of each TextDNA specifies a percentage likelihood that the TextDNA as a whole will be used once it has been chosen. For example, if the first number of a line is '25', then that TextDNA will be used one quarter of the time that it is chosen by JanusNode. Each TextDNA is chosen with a likelihood equal to 1/(the number of lines of TextDNA)- that is, it is chosen randomly from among all possible lines of TextDNA. The first element of each line of TextDNA thus allows you to 'weight' certain lines of TextDNA less heavily, thereby favouring some lines of TextDNA over others. If you want to increase a line's likelihood of being randomly chosen, you can simply insert multiple copies of that line into the TextDNA file.


The optional subject marker which must be the second element of a TextDNA if it appears lets your JanusNode know which subjects the TextDNA is to be filed under. The syntax for this marker is the word 'subject', followed by a list, in parentheses, of all subjects, separated by commas. There may be no spaces anywhere in the marker; if there is, an error will be flagged. For example, the marker 'subject(love,life,My_Favourite_TextDNAs)' signals to a JanusNode that the current TextDNA is to be chosen if the current subject is 'love', 'life', or 'My_Favourite_TextDNAs'. There are no pre-set subjects and no limits on how many subjects a TextDNA can belong to. You may classify any line of TextDNA under any subjects you like. When a JanusNode activates its TextDNA, it automatically indexes every subject it finds, and puts them all in the subject menu, where you may choose them. The subject menu is just below the Janus icon. If you choose a subject from that menu, then JanusNode will choose only TextDNAs which belong to that subject when it generates poetry.

There are three 'special' choices in the subject menu- the 'Tell Fortune', 'Spell Poem', and 'Multiple TextDNA Files' options. The first two of these will be discussed below.

'Multiple TextDNA Files' mode

Let us consider the 'Multiple TextDNA Files' mode now. When this option is chosen from the 'Subject:' menu (the right-most choice on the window bar), then the user is presented with a list of all the available TextDNA files from the 'TextDNAs' folder. You can choose one or more of these files (hold down the command-key to make a discontinuous selection). Your JanusNode will henceforth choose TextDNA from all the files you have selected. It does so by weighting the files probabilistically according to the number of lines of TextDNA they contain. For example, if you choose three TextDNA files, one of which contains 90 lines and the other two of which contain 5 lines each, then TextDNA will be selected from the first file 90% of the time, and from each of the other two files 5% of the time. If you are using multiple TextDNA files, you cannot choose your subject- you will see 'No subjects available' in the subjects menu. Selecting that as your topic has no effect (i.e. it is the same as choosing 'Multiple TextDNA Files').


When you first turn on the 'Multiple TextDNA files' mode (or every time you leave the TextDNA field if the mode is selected) JanusNode needs to 'compile' the TextDNA files, which means it needs to read through every available TextDNA file once. This can be a time-consuming operation, depending on how many files there are, how big they are, how complex the TextDNA is, and how fast your hardware symbiot is.


Note that, in multiple-TextDNA-files mode, JanusNode loads in the TextDNA files to its TextDNA field. This means that there is no guarantee that JanusNode will be in the same state when you turn the 'Multiple TextDNA files' mode off as it was when you turned it on. To use a single specific TextDNA file, you must go to the TextDNAsfile and use the 'Open file' menu command to read in the file you want, and then make sure that the 'Multiple TextDNA files' mode is not selected from the 'Subject:' menu. Alternatively, just put that file in its own folder within the 'TextDNA' folder.

A simple examples of a TextDNA

An example of a TextDNA will make the simple syntax clear. Consider the following line of TextDNA:

100 subject(simple_TextDNA,MyTextDNA) s_articles 20 adjectives 5 s_nouns 100 s_verbsnob 100 punctuate 70

This line will be classified under two subjects: 'simple_TextDNA' and 'MyTextDNA', which will both appear in the subject menu. This means that the TextDNA will only be considered if the subject is either one of these, or if it is the default, 'None'. The TextDNA will fire 100% of the time that it is randomly chosen, since the first element of the line is 100. It will then insert a singular article 20% of the time, followed 5% of the time by an adjective. The next two elements of the TextDNA, a singular noun and a verb which needs no object, will always be inserted when the TextDNA is chosen, since they are both given a 100% chance of being inserted. Finally, this sample sentence will be punctuated, as described earlier, 70% of the time. Thus one possible outcome of this TextDNA is the creation of the sentence:

The laughing eye twinkles.


Note that it is almost always necessary to have some elements of a line of TextDNA inserted with 100% probability, in order to guarantee that there will be a basic syntactically-sensible structure underlying the sentence which is generated. One might, on the other hand, might go in for really 'modern' poetry that does not even follow the rules of syntax.

 

TextDNA control characters

The rest of the characters which are allowed in TextDNAs allow for greater flexibility in TextDNA writing.

Repetition Brackets [ ]

The square brackets are for repeating certain parts of the TextDNA. Everything contained within square brackets will be repeated a random number of times (but not less than once and not more than five times).
For example, consider the following line of TextDNA:

100 "You" 100 [ s_verbsnon 100 adverbs 100 "," 100 ]

This TextDNA will always fire when chosen, because the first element is 100. The first printable item- the word 'You'- will be printed every time the TextDNA fires. The phrase within the square brackets- a verb followed by an adverb and a comma- will be printed at least once, and up to five times. One possible outcome of this TextDNA is the creation of the phrase:

You run madly, dream crazily, hope lovingly,

Choice brackets {}

The curly brackets allow for alternative choices to be inserted in TextDNAs. Alternatives are separated with the '|' character. When a TextDNA containing alternatives fires, JanusNode will randomly choose one the alternatives. You can put as many alternatives as you like.
Once again, an example will make the simple idea clear. Consider this line of TextDNA:

100 "You" 100 { "dream" 100 adverbs 100 | s_verbs_to 100 "me" 100 }

This TextDNA will always fire when chosen, because the first element is 100. The first printable item- the word 'You'- will be printed every time the TextDNA fires. After that, one of the two choices between the curly brackets will be randomly chosen. Either the word 'dream' will be printed, followed by an adverb, or else a verb taking 'to' will be printed, followed by the word 'me'.
Thus, two possible outcomes of this TextDNA are equally likely. The TextDNA will either print a sentence like:

You dream quietly

or it will print a sentence like

You give to me.

Function brackets and built-in functions <>

The triangular brackets ('<' and '>') mark off function calls. JanusNode expects to see a function call between the brackets, followed by percent probability of calling that function. If the function is called, JanusNode will print whatever the function call returns.
For example, you can make a call to a random number generator in the middle of the TextDNA. A TextDNA which does so might look like this:

100 "Love me" 100 < random(20) 100 > "times" 100

This TextDNA would improve upon Jim Morrison's famous line "Love me two times" by substituting a random number between 0 and 20 for the word 'two'. One possible outcome would be:

Love me 15 times


There are no constraints on the number of arguments the function can take, but the function call must be printed without any spaces in it. You can access any HyperTalk function with a function call (though it is hard for me to see which ones other than 'random' might be useful!). The main use of the function call feature, however, is to give you access to a number of useful functions which have been built in to your JanusNode. Most of these functions do not currently error-check their arguments very well, so pay close attention to the syntax.

The 'Assign' & 'Get' Functions

Two important functions built in to JanusNode are the 'assign' and 'get' functions. These allow for the setting and accessing of global variables within a string of TextDNA.


The 'assign' function usually takes two arguments: a name for the global variable and one of the BrainFood file names. It names a randomly chosen word from the file with the name. In doing so, it returns nothing. However, the second argument of the 'assign' function need nor necessarily be the name of a BrainFood field. If it is a quoted string of words separated by commas (but containing no spaces), 'assign' will randomly assign one of the words in that string to the global variable. For example, the following is legal:

< assign(AnAnimal,"dog,cat,kitten,cow") 100 >

 

The 'get' function allows your JanusNode to access varianbls which have been assigned a value using the 'assign' function. It takes a single argument, which should be the name of a previously assigned variable. It returns the value of that variable. If the name is not the name of a previously assigned variable, then the function does not return anything.


Consider the following rather inane example:

100 < assign(MyNoun,s_nouns) 100 > < assign(MyArticle,s_articles) 100 > < get(MyArticle) 100 > < get(MyNoun) 100 > "is not" 100 < get(MyArticle) 100 > < get(MyNoun) 100 >

The first element is the global probability that the the textDNA will be used if it is chosen, as described above. The two function calls after the global probability are assignment statements, which will not result in anything being printed. The first assignment (< assign(MyNoun,s_nouns) 100 >) assigns a singular noun to the variable named 'MyNoun'. The second assignment (< assign(MyArticle,s_articles) 100 >) assigns a singular article to the variable named 'MyArticle'. These two variables are then accessed twice in the body of the TextDNA, sandwiching the phrase "is not". One possible outcome of this TextDNA is the creation of the sentence:

my poem is not my poem


A JanusNode ships with four pre-defined global variables which are randomly initialized at start-up. These are 'MyNoun' (a singular noun), 'MyNouns' (a plural noun), 'MySVerb' (a singular verb which doesn't need an object), and 'MyPVerb' (a plural verb which doesn't need an object). Since these are given a value at start up, you can access them in TextDNAs without having to use an assign statement first. However, you are free re-assign them in the usual way (that is, by making a call to the 'assign' function) or by clicking on the 'Initialize Globals' button in the Control Panel, which will re-set the four globals.


You can also define global variables by clicking on the 'Define Constant' button in the Control Panel, which will prompt you for the name and type of the variable you want to create. Variables created in this way can be used within TextDNA without assigning them a value first, since they are given a value when they are created. However, they must be re-created each time you re-start your JanusNode robot.

The LoadTextDNAFile and UseTextDNA Functions: Power to structure text

Two powerful functions in your JanusNode are the 'LoadTextDNAFile' and 'UseTextDNA' functions. They extremely simple,but extremely useful. Warning: these are 'power user' functions. which require that you have some idea of what you are doing. If you are just getting started with your JanusNode, you may want to leave these two functions until you understand how everything else works. Things will get very hairy if you try to use these functions without having a good idea of how your JanusNode works.


'LoadTextDNAFile' takes a single argument: the name (in quotation marks to be safe, although they are not necessary if your TextDNAs files contain only alphabetic and numeric characters) of a TextDNA file in the TextDNAs folder. It loads that file in to the JanusNode.

Example:


< LoadTextDNAFile("paragraph.DNA") 100 >


This will load and prepare the TextDNA file called "paragraph.DNA". If a JanusNode encounters a call to the 'LoadTextDNAFile' function while it is in 'Multiple TextDNA Files' mode, it will change mode to single TextDNA file mode. This means that after a function call to 'LoadTextDNAFile', JanusNode will only know about the current TextDNA file, not any other TextDNA files which may happen to be in the TextDNA folder. This a deliberate feature, not a bug or technical limitation. The function exists to allow users to have control of how the TextDNA is used, whereas 'Multiple TextDNA Files' mode does not allow such control (since you cannot set a subject or method in that mode).


In concert with the 'UseTextDNA' function (described next), the 'LoadTextDNAFile' function is an extremely powerful tool whose implementation makes it possible to make your JanusNode generate long, highly structured, coherent texts, which may use literally thousands of TextDNAs and BrainFood files in a systematic way.


The 'UseTextDNA' function takes a single argument, which is the name of a subject by which at least one line of TextDNA in the current TextDNA set is classified. It simply sets the current subject to the specified subject, thereby ensuring that next TextDNA chosen will come from that set. Having this capability makes it possible to string together sets of TextDNAs (or a single line of TextDNAs, since a set can consist of just one line) one after another, thereby gaining complete control over the order on which your TextDNA fires. This makes it possible for your JanusNode to do many things that would otherwise be tricky or impossible, such as writing rhyming verses with a repetitive structure, writing long coherent narratives, and much more.


An example of the syntax of this function is


< UseTextDNA(MyTextDNA) 100 >


Whenever this function call is encountered in TextDNA, the next line of TextDNA will be drawn from all the lines of TextDNA that are classified under the subject 'MyTextDNA'.


Note that you can also use this function to make probabilistic jumps between TextDNA sets. For example, consider the following:


< UseTextDNA(MyTextDNA1) 100 > < UseTextDNA(MyTextDNA2) 50 >


Here there are two function calls, one after another, of which the first will certainly fire (since it is specified as having a 100% chance of firing) and the second of which will fire 50% of the time. After encountering this in a line of TextDNA, a JanusNode will use the TextDNA set named 'MyTextDNA2' 50% of the time, and the TextDNA set named 'MyTextDNA1' the other 50% of the time.

The RepriseTextDNA Function

RepriseTextDNA is another powerful meta-level control function, which allows the 'UseTextDNA' function to use indirect reference. The function takes a single argument which is the name of a variable, and fires a line of TextDNA which has the same name as _the value_ of that variable. You will usually set the value of the argument which is passed to RepriseTextDNA using the 'Assign' function.

For example:

100 < assign(CurrentDNA,"MyTextDNA") 100 >

*** The above line will assign the value 'MyTextDNA' to the variable 'CurrentDNA'

100 < RepriseTextDNA(CurrentDNA) 100 >

*** The above line will fire a (randomly-selected) line of TextDNA which has the subject

*** 'MyTextDNA', which is the _value_ of the variable 'CurrentDNA'.

RepriseTextDNA makes it possible to have repeated elements in an output text (for example, choruses in a song- see the RobotJohnson files for many examples) and to have a logical flow to texts, since you can set the values of future text in TextDNA which has fired. For example, you might have two rules with the same name which lead to different paths using the RepriseTextDNA function, as follows:

100 Subject(HeroineEnd) "And then she died." 100 < RepriseTextDNA(DeadHeroineDNA) 100 >

***

100 Subject(HeroineEnd) "And then she had a baby." 100 < RepriseTextDNA(PrinceIsBornDNA) 100 >

In this case, prior rules might call either one of these two rules using the UseTextDNA function, since they both have the same subject. However, clearly things are likely to develop differently if the heroine dies than if she has a child. The DeadHeroineDNA variable contains the name of one set of TextDNA which continues (or completes) the text with a dead heroine, while the PrinceIsBornDNA contains the names of a set of TextDNA which continues the text with a child. Note that if there were only a single relevant rule-set in either case you could just use the UseTextDNA function- the RepriseTextDNA function would be used in this case if (as in real life!) there were multiple possible paths through birth and death.

The GetRhyme function

The GetRhyme function takes two arguments: a variable name, and a suffix. It returns a randomly-selected entry from a BrainFood file that has the name of the _value_ of the variable, plus the suffix. Although it may prove useful in many different circumstances, its main purpose is to make it possible to write rhyming verse which uses any one of a number of rhymes. If you define a number of files 'x.verb', 'x.noun', 'x.exclamation' (where 'x' takes multiple values- i.e. 'dog', 'cat', 'pig') then you can always call a word which rhymes in the proper place, without always writing poems which use the same rhyme. For example:

100 Subject(AnimalPoemSetUp) < assign(Cur,"dog,cat,pig") 100 >

100 Subject(ShortAnimalPoem) "A" 100 < GetRhyme(Cur,noun) 100 > "likes to" 100 < GetRhyme(Cur,verb) 100 > "!" 100

The first line of TextDNA sets the value of the variable 'cur' to either 'dog', 'cat', or 'pig'. Assuming the existence of the appropriate BrainFood files (which, incidentally, do not actually exist- you'd have to make them to run this example), the second line writes a short poem alleging that the animal in question likes to do something that rhymes with its name. One possible outcome of firing these two lines of TextDNA might be:

A pig likes to jig!

However, it is equally possible that the identical lines might produce:

A cat likes to bat!


The 'GetSubject' Function

The 'GetSubject' function can be used to personalize your JanusNode's creations with a single name and matching pronoun and possessive. The function usually takes one of four arguments: 'name','pronoun','possessive', or 'object'. It will return the correct name, pronoun, possessive, or object as determined by its question at start-up or by the information garnered when you click on the 'Set Subject' button in the Control Panel. The 'GetSubject' function can also be used to change the name or sex of the current subject from within a line of TextDNA. If the argument is 'he', then the gender of the subject will be set to male. If the argument is 'she', then the gender if the argument will be set to female. If the argument is anything else, then the name of the subject will be set to the argument. In all three of these latter cases, nothing is returned.


Consider the following example:

100 < getSubject(Jane) 100 > < getSubject(name) 100 > s_verbs_from 100 < getSubject(possessive) 100 > s_nouns 100

This TextDNA will always change the subject's name to 'Jane', since the first call to the 'getSubject' function has 'Jane' as an argument, and is guaranteed to fire. After that, it will print the new name, 'Jane', since the second call has 'name' as an argument, and is also guaranteed to fire. This will be followed by a verb which takes the word 'from', a possessive, and a noun. On possible outcome of thus TextDNA is the production of the phrase:

Jane steals from her dog

However, note that this snippet of TextDNA might also print:

Jane steals from his dog

It will do so if the last subject was a male, since there is nothing in this TextDNA which sets the sex of 'Jane' - and a JanusNode is far too dumb to figure things like that out for itself. We could have remedied this by adding the function call '< getSubject(she) 100 >' anywhere before the call '< getSubject(possessive) 100 >'. This would set the subject.

 

The 'backspace' function

The 'backspace' function exists to allow for the suppression of the space that a JanusNode normally inserts between words: i.e. it deletes the last character that was output, and prints from the new end. There are many situations in which this might be useful- for example, if you want to write a string of TextDNA which uses parentheses, or which creates compound words, or if you want to keep sentences in paragraphs. The function takes no arguments and returns nothing.

Example:

100 "Imagine a" 100 s_nouns 100 < backspace() 100 > "-" 100 < backspace() 100 > s_nouns 100

This string of TextDNA will ask you to imagine a new compound noun. For example, it might print:

Imagine a computer-coffin

You may certainly feel free to develop this idea commercially.

'Backspace' has one counter-intuitive (but rather useful) behavior, which is that it will erase as many spaces and returns as it finds (It will never erase more than one single non-whitespace character, and will erase even a single one only if the last character printed happens to be non-whitespace.) This is useful because your JanusNode automatically inserts a linefeed after firing any line of textDNA, even lines that have no actually printing in them. If you want that textDNA to be 'transparent' (have no effect at all on output), then terminate the line with a call to the backspace function. If you want to see an example of why this might be useful, look at the vocabulary-morphing rule in, for example, file '0RJ.I' in the RobotJohnson textDNA folder.

The 'quotation' function

The quotation function allows the user to use (straight or curly) quotation marks within a string of TextDNA, so that your random texts can cite other random texts. It takes one of two arguments, "o" or "c", or no argument. If the argument is "o" the function will return an opening curly quote. If it is "c" the argument will return a closing curly quote. If there is no argument, the function return a straight quote.

Example:

100 "Try to understand this sentence: " 100 < quotation() 100 > TextDemonSPVP 100 TextDemonPNP 100 < quotation() 100 >

This TextDNA makes use of TextDemons, which are defined in the following section of this documentation. This string of TextDNA will print quote a verb phrase and a noun phrase following a verbatim introductory phrase. One possible outcome of the TextDNA is the creation of the phrase:

Try to understand this sentence: "too many of us accommodate temptations".

Profound, huh?

The 'UseFont', 'UseStyle', and 'UseSize' Functions

The 'Font-o-matic' feature (described elsewhere in this documentation) allows you to get JanusNode to randomize the font type, font size, and font style. However, you can also use built-in functions to control (or randomize) these aspects of text presentation from with TextDNAs, using the 'Usefont', 'UseStyle', and 'UseSize' functions. They all have the same syntax: each one takes either a valid argument for what it does (e.g. a font name, a font style, or a font size respectively) and sets the current display font to that size, style, and font. All three arguments may also take the argument 'random', in which case the relevant value is set randomly.
Legal font styles are 'bold', 'italic', 'extend', 'underline', 'outline' and 'plain'. Legal font names and sizes are system-dependent; in general, you can usually use font sizes between about 10 and 127 if your system uses true type fonts.
Example: An example of a TextDNA using these functions is:

100 < UseFont(chicago) 100 > <UseSize(14) 100 > "This is" 100 < UseSize(24) 100 > < UseStyle(italic) 100 > "really" 100 < UseSize(14) 100 > < UseStyle(plain) 100 > "dumb TextDNA." 100

This self-referential string of TextDNA will print the sentence 'This is really dumb TextDNA.' in plain 14-point Chicago type, except for the word 'really', which will appear in italic 24-point Chicago.

The 'CapitalizeNext' Function

The 'CapitalizeNext' function takes no arguments. It simply ensures that the item which follows it will start with a capital letter.

Example:

100 "This is a name:" 100 < CapitalizeNext() 100 > syllables 100 < backspace() 100 > syllables 70

This string of TextDNA will print out "This is a name:" followed by a one or two-syllable nonsense word which starts with a capital letter. One possible outcome is:

This is a name: Tishyag.

The 'BecomePassive' Function

The 'BecomePassive' function takes no arguments. When it is called, processing stops and your JanusNode enters a passive, receptive state. Please note that a JanusNode must fire all functions before it can print a line of TextDNA, since most functions are intended to return printable components of that line. For this reason, the 'BecomePassive' function should not appear with any text that you wish to print. If it does, that text will never get printed, since your JanusNode wil become passive before it gets to printing it.

Here is an example of a full line of textDNA:

100 Subject(End) < BecomePassive() 100 >

When this line fires, processing will halt.

 

Limitations

There is no practical limit to the quantity of TextDNA you can define, since you can have approximately as many TextDNA files as you wish. Since TextDNA can call specific lines of TextDNA, even when they are in another file, there is no limit to how complex your production's connections and structure can be.

Error-checking

A JanusNode dynamically checks the syntax of each line of TextDNA as it is processed. Some kinds of major syntax errors errors are flagged with a description. The description includes the line number, the offending element in the TextDNA, and a copy of the text of the offending TextDNA. Click on the error message field to make it disappear when you have corrected the error in the TextDNA field. JanusNodes do not stop processing for most errors (i.e. the most common errors of a mis-named BrainFood file-name or a missing probability afer an element in TextDNA). If it encounters an error of this type, a JanusNode will print an error message in its output field and will attempt to keep processing. It can often recover from such errors, but sometimes it will print a very large number of error messages before it recovers. If your JanusNode prints nothing but error messages, the most likely cause is either that your 'BrainFood' folder is missing or mis-named, or that you have an extraneous element (or a missing element) early on in your TextDNA (for example: a missing probability after a BrainFood file name).

Nesting TextDNA Elements

Nesting of repeats, functions, and alternatives is not currently allowed: that is, you cannot put square brackets within square brackets, curly brackets within curly brackets, or function calls within function calls. This wil be remedied in a future version of Janus. You can nest repeats, functions, and alternatives within each other. For example, the following is a perfectly legal line of TextDNA:

100 < getSubject(name) 100 > [ { s_verbs_from 100 < getsubject(possessive) 100 > s_nouns 100 | s_verbs_to 100 s_objects 100 } "," 100 ] return 100

One outcome of this TextDNA might be the creation of the phrase "Linda runs from her playmate, accepts from her estimation, receives from her community, steals from her era,", while another might be "Linda takes to everything, shows to her, takes to him, gives to me,".
The inability to nest repeats, functions, and alternatives within themselves (and the limitations imposed by the order in which JanusNode parses each of these) are not hard-wired, however. You can use TextDemons to nest anything within anything, and to gain complete control over how your TextDNAs are parsed. We now turn our attention to TextDemons.

 

TextDemons

TextDemons are very similar to TextDNA. The main difference is that TextDemons are not directly chosen by a JanusNode from the field where they are defined, but must rather be part of a TextDNA string. TextDemons are defined in the 'TextDemons' field. They have a similar format to TextDNA, except that they don't begin with a global likelihood of being chosen, but rather with a name which must always begin with the string 'TextDemon'. Examples of acceptable names are 'TextDemonAdverbPhrase' or 'TextDemonMyTextDemon'. After the name a TextDemon can contain anything that TextDNA can contain: indeed, their structure is identical except for the first item.


Once a TextDemon has been defined and 'compiled' (which is taken care of automatically) then you can use TextDemons in any string of TextDNA (or in any TextDemon) in just the same way you use any other item: by writing the name of the TextDemon followed by a percentage likelihood of it being chosen.


For example, we might define a TextDemon in the TextDemon field as follows:

TextDemonMyFirstTextDemon p_nouns 100 p_verbsnob 100 [ adverbs 50 ]

We can then use this TextDemon in a line of TextDNA, defined in the TextDNA field, as follows:

100 TextDemonMyFirstTextDemon 100 "and" 100 p_verbsnob 100

The TextDemon defines a noun-verb phrase (possibly modified with one or more adverbs). The above TextDNA might generate the sentence:

time changes warmly nightly and distorts


TextDemons provide great flexibility for your TextDNA. You can use them to define constants, to define commonly-used text chunks, and to gain finer control over the way units are repeated or chosen.


One word of warning: Although JanusNode does correctly parse TextDemons which contain quotes, it must, for technical reasons, parse such TextDemons character-by-character rather than word-by-word. For this reason, TextDemons containing quotes are MUCH slower to compile than TextDemons which contain no quotes. In order to get around this, two special symbols have been defined for use in the TextDemon field: 'Q1' and 'Q2'. These can be used to substitute for opening and closing quotes respectively in TextDemons which contain only one pair of quotes. I recommend that you use this method of quoting in all TextDemons. If you want to quote more than one element in a TextDemon, define each of the quoted elements as a separate TextDemon.


Every JanusNode ships with many pre-written TextDemons in the TextDemon field. Since they are subject to change and open for tinkering by any user, they are not documented officially. You will have to take a look at them if you want to see what they do. However, you should be careful, since theTextDNA which is distributed with your JanusNode depends on the TextDemons which came with it. You are welcome to delete all the TextDemons that come with your JanusNode, but do not do so unless you understand what you are doing, as much of your TextDNA will be non-functional without the appropriate TextDemons.

Limitations

The 'TextDemons' field is currently limited by an internal limit on the size of a text field (32K). In the future (maybe when I retire in about thirty years) Janus will be re-written to avoid this limit. If it gets to be a problem for you before that time, let Janus know- you're a power-user with whom Janus would like to be in contact!