Talk:Condit
Hello. In case anyone reads this wiki: I'm the creator of Condit, and I would not object to the language specification being reproduced here (to expand on this little stub page). Feel free to mail me via the Web site if that's of interest and you want formal written permission. 86.174.124.26 23:42, 20 July 2009 (UTC)
- Unfortunately, we cannot do this without you releasing the specification into the public domain. 91.104.254.222 17:28, 22 July 2009 (UTC)
Okay, since I've taken down my Web pages, I, Paul Collins, hereby release the following page on Condit into the public domain. Use it if you like! This wiki won't accept HTML tags so I have had to replace left-angle-bracket with @ and right-angle-bracket with % (which is safe, because those symbols did not appear in the original text). 86.186.146.195 13:35, 31 May 2011 (UTC)
Condit is an esolang (esoteric programming language) with a unique constraint: everything is done conditionally.
I invented Condit in 2001, and the specification has been considered stable since 2005, but I'm not happy with some of the unnecessary higher-level constructs. Almost any existing language could be approximately "Conditised" by creating a set of zero-valued variables, requiring each statement to be inside an if-block and looping over the whole program as described.
If you just want to see what Condit looks like, jump to the sample programs.
Overview
All statements take the form: when condition then actions
At run time, the interpreter checks the conditions in order, from first to last, and performs the appropriate actions for those that are true. This process is repeated until no condition is true, at which point the program terminates. (Actions can change variables, so the ordering of variable-based conditions is significant.)
For a Condit program to do anything, at least one of its conditions must be true on execution. You may include a condition that is always true, but this means that the program will run forever. It is more useful to use a condition involving a variable that can be changed to stop the actions from constantly repeating. For example:
when 1 then put "Hello, world!"
... prints "Hello, world!" an infinite number of times (if you are patient), whereas ...
when a=0 then put "Hello, world!" set a=1
... prints "Hello, world!" once and terminates.
Variables
Variable names must contain only alphabetic characters, with the first character determining data type. Variables beginning with a lower-case letter contain floating-point numeric values; those beginning with an upper-case letter contain strings. Initially, all numeric variables contain zero and all string variables contain the empty (zero-length) string.
Arrays
Numeric arrays and string arrays are permissible. The syntax [n]arr refers to the nth-indexed element of arr (array indexing starts at 0), and |arr| returns the number of elements. (Regular variables are actually single-element arrays, so arr means [0]arr.)
Assigning to [n]arr expands arr to n+1 elements, if necessary, preserving any existing elements and setting new ones to 0 (or the empty string). [-1]arr is the last element of arr, [-2]arr the second-to-last, and so on. [n]arr is regarded as non-existent when n>|arr|-1 or (n<0 and |arr|<-n). Non-existent array elements return 0 (or the empty string).
Expressions
Valid expressions are more or less a subset of those in other languages like C and BASIC. They are defined by the following recursive grammar. (The spaces are for readability only. Expressions must contain spaces only around and and or.)
@i%numeric-expression@/i% → @i%numeric-exp@/i% @br%@i%numeric-expression@/i% → @i%numeric-exp@/i% = @i%numeric-exp@/i% @br%@i%numeric-expression@/i% → @i%numeric-exp@/i% < @i%numeric-exp@/i% @br%@i%numeric-expression@/i% → @i%numeric-exp@/i% > @i%numeric-exp@/i% @br%@i%numeric-exp@/i% → @i%number@/i% @br%@i%numeric-exp@/i% → @i%numeric-var@/i% @br%@i%numeric-exp@/i% → @i%numeric-exp@/i% @i%operator@/i% @i%numeric-exp@/i% @br%@i%numeric-exp@/i% → ( @i%numeric-exp@/i% ) @br%@i%numeric-exp@/i% → chop( @i%string-var@/i% , @i%numeric-exp@/i% ) @br%@i%numeric-exp@/i% → eof( @i%string-exp@/i% ) @br%@i%numeric-exp@/i% → rnd( @i%numeric-exp@/i% ) @br%@i%numeric-exp@/i% → |@i%numeric-var@/i%| @br%@i%numeric-exp@/i% → |@i%string-var@/i%| @br%@i%operator@/i% → + @br%@i%operator@/i% → - @br%@i%operator@/i% → * @br%@i%operator@/i% → / @br%@i%operator@/i% → and @br%@i%operator@/i% → or @br%@i%numeric-var@/i% → @/tt%any numeric variable or numeric-array element@tt% @br%@i%number@/i% → @/tt%any number, numeric variable, or numeric-array element
The mathematical symbols + - * / = < > and the brackets ( ) have their canonical meanings, respectively add, subtract, multiply, divide, equals, less than, greater than, and evaluate first. Logical and and or can be used to test multiple conditions. The pipes | | return the number of elements in an array. rnd(n) provides a mechanism for obtaining a random integer between 0 and n. The chop and eof functions will be described later.
@tt% @i%string-expression@/i% → @i%string-exp@/i% @br%@i%string-expression@/i% → @i%string-exp@/i% = @i%string-exp@/i% @br%@i%string-expression@/i% → @i%string-exp@/i% < @i%string-exp@/i% @br%@i%string-expression@/i% → @i%string-exp@/i% > @i%string-exp@/i% @br%@i%string-exp@/i% → @i%string@/i% @br%@i%string-exp@/i% → @i%string-var@/i% @br%@i%string-exp@/i% → @i%string-exp@/i% + @i%string-exp@/i% @br%@i%string-exp@/i% → Chop( @i%string-var@/i% , @i%numeric-exp@/i% ) @br%@i%string-var@/i% → @/tt%any string variable or string-array element@tt% @br%@i%string@/i% → @/tt%any quoted string literal
String comparisons are performed in the canonical way. That is, the lesser string is whichever comes first in ASCIIbetical order ("Solstice"<"equinox" and "equinox"<"equinoxes"). The + operator denotes string concatenation ("equi"+"nox"="equinox").
The expression Chop(S,n) returns the first n characters of S (or the last -n characters, if n is negative) and removes them from S. If the value of n is not an integer, it is rounded down. If there are fewer than |n| characters in S, all of S is returned. This non-standard mechanism, whereby you cannot obtain a substring without removing it from its parent string, can actually be convenient. For example, you could output a string backwards like this:
when String>"" then put Chop(String,-1)
Also, you can use chop(S,n) in a numeric expression to extract a numeric value from a string. The resulting number is a decimal interpretation of the string. If S is the string "123", for example, then chop(S,3) returns 123 and chop(S,-1) returns 3. If S contains characters other than the digits and decimal point, evaluation stops at the first bad character, so "01.2+34.5" becomes 1.2 and "hello123" becomes 0. As with Chop, chop removes all the characters it was asked to evaluate, including any bad ones.
Note the difference between chop and Chop, following the convention of upper-case initial letters for strings.
Conditions
There must be exactly one condition between each corresponding @tt%when@/tt% and then.
A condition is an expression that leads to a true/false result when evaluated. In Condit, false is represented by zero and true by any non-zero value (1 by default), so in fact any numeric expression can be used as a condition. For example, the expression a>5 evaluates to 1 when true and 0 when false; and the expression a+8 is false only when a equals -8 so that a+8=0.
A string expression may be used as a condition only if the expression (not the string!) contains any of the comparison operators = < >. A string on its own has no numeric value and cannot be used as a condition.
The logical operators and and or are useful for creating complex conditions, like these:
when age<18 or Status="barred" then put "You can't drink here!"
when EyeCol="blue" and (height>180 or weight>70) then set suspect=0
There is no not operator, but you can write code like this:
when (EyeCol="blue")=0 then put "Not blue-eyed."
Actions
You may include as many actions as you like on a single line. Each action must be preceded and followed by a blank space to separate it from other code, though the space is not necessary at the end of a line.
set variable=expression
...assigns a value to a variable. If variable is a string variable, expression must be a string-exp (not a string-expression). If variable is a numeric variable, expression must be a numeric-exp (not a numeric-expression).
put expression
...prints a string or numeric expression to standard output. (There is no terminating newline.) String literals must be enclosed in quotes. Strings may include any of the special sequences \" (quote mark), \n (newline), \t (tab), \\ (backslash), and \xx (hex digits representing an ASCII code). Unrecognised sequences like \q are printed literally.
put #string-exp expression
...works just like put, but prints to a file instead of standard output. The target file is the one whose name is specified after the # symbol. Condit always writes to the end of the file. However, if the first two characters of the filename string are +> (not considered part of the filename), the file is deleted and replaced with only the new output. Be careful with this! Creating any kind of loop with +> will wipe the file at every iteration of the loop — not just the first.
get variable
...takes a line of input from the user (discarding the newline) and assigns it to a variable. If variable is a numeric variable, user input is interpreted as a numeric value. If variable is a string variable, user input is interpreted as a string (with implicit quotes).
get #string-exp variable
...works just like get, but reads from a file instead of standard input. The source file is the one whose name is specified after the # symbol. Condit always reads from the read pointer (current file position) up to the next newline. (The newline is ignored. For numeric variables, strings are converted using the same rules as chop.) However, if the first character of the filename string is < (not considered part of the filename), the read pointer is reset before the read takes place.
When you first read from a file, the read pointer is at the beginning, and it advances one line with each read operation. When there is no more data to be read, get # returns an empty string once and then the file is closed, i.e. the read pointer is reset to the beginning before the next read.
Note that blank lines are empty strings, so the empty string can occur before end of file. Use the eof function to check for end of file. It returns 1 for true or 0 for false. You could read the lines of a file into an empty array like this:
when eof("data")=0 then get #"data" [|MyData|]MyData
Interpreters
Steve "Stilvoid" Engledow wrote a Condit interpreter in Perl, called Tidnoc, but it was in rather evil and non-portable Perl (I can't get it running on Windows) and didn't implement some language features. It worked by converting Condit code into Perl code and executing the result.
In 2010, Jack Mudge wrote a Condit interpreter in Python, called Condit, which aims to support the full specification (with the addition of comments, and without the restriction that each when start a new line). It has a Web page here.
Sample programs
Here is a number guessing game in Condit. It doesn't use and or or !
when a=0 then set x=rnd(49)+1 put "Guess the number between 1 and 50.\n" set a=1 when a=1 then set tries=tries+1 get guess set R="it!" when guess<x then set R="too low." when guess>x then set R="too high." when a=1 then put "That's "+R+"\n" set j=guess-x+1 when j=1 then put "You got it in " put tries put ".\n" set a=2 set j=2
Here is the traditional "bottles of beer" program (untested).
when a=0 then set a=99 when a>0 then put a put " bottles of beer on the wall\n" put a put " bottles of beer\n" put "Take one down, pass it around\n" set a=a-1 put a put " bottles of beer on the wall\n\n" when a=1 then put "1 bottle of beer on the wall\n1 bottle of beer\nTake one down, pass it around\nNo more bottles of beer on the wall\n" set a=-1