This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
ABSTRACT Tcl was developed by John Ousterhout in the early 1980’s. He never expected it to grow as much as it has. Tcl continues to attract more uses each year and has received the ACM Software System Award for "a software system that has had a lasting influence". Being an embeddable language Tcl has many new extensions continually coming out every year. However no one can replace the easy to learn, general package, of Tcl. Tcl has all that you would need to develop full blown applications. Over the course of the paper I will provide a brief look into Tcl /Tk with an emphasis in Tcl so that we may understand the phenomenon that is Tcl.
1 1.0 Introduction Have you ever been so frustrated with your own, poorly written, code? If so did you develop your own language based off that aggravation? Tcl, pronounced “tickle,” was originally born out of frustration by programmers devising their own “poorly written” languages intended to be embedded into applications. 1 Tcl is a very simple scripting language that can be picked up in a matter of hours by anyone familiar with programming. Tcl (Tool Command Language) received its name based on its intended usage as an embeddable command language. 2 Tcl is considered a weak functional language. 3 This is because TCL has many features that could be considered imperative. With the right extensions, one could easily make it object oriented, or even logical, if the extension were written. Tcl has higher order functions and functional abstractions built into the language; however, Tcl is not widely used in this manner. Tcl started out as an experiment because someone was embarrassed by his code. Tcl has many similarities to LISP, C, and the UNIX shell. 4 2.0 Historical Background Professor John Ousterhout at the University of California at Berkley wrote Tcl in the early 1980’s. The idea for Tcl grew out of Ousterhout’s work designing tools for integrated circuits. At that time, there was not much invested in the command languages used during development; most were weak, quirky, nonreusable languages. Every tool ended up with its own uniquely written command language, which Ousterhout found embarrassing. 5 In 1987, Ousterhout got the idea for an embeddable command language. Thus began his work to create an interpreted language with library packages that could be reused for many different applications; he also envisioned generic facilities, such as variables, procedures and control structures. As Ousterhout explains: The notion of embeddability is one of the most unique aspects of Tcl, and it led me to the following three overall goals for the language: 1) The language must be extensible: it must be very easy for each application to add its own features to the basic features of the language, and the applicationspecific features should appear natural, as if they had been designed into the language from the start. 2) The language must be very simple and generic, so that it can work easily with many different applications and so that it doesn’t restrict the features that applications can provide. 3) Since most of the interesting functions will come from the application, the primary purpose of the language or to integrate or “glue together” the extensions. Thus the language must have good facilities for integration. 6 1
John Ousterhout Tcl Developer Xchange “History of Tcl” October 2005, 28 October 2006, ActiveState http://www.tcl.tk/about/history.html 2 Ibid. 3 Wiki.tcl.tk “Tcl Heritage” 16 July 2006. 28 October 2006 http://wiki.tcl.tk/985 4 Ibid. 5 Ousterhout http://www.tcl.tk/about/history.html 6 Ousterhout http://www.tcl.tk/about/history.html
2 After Ousterhout stopped working on designing tools, Tcl became a personal academic experiment. Little did he know how well it would expand. In 1990 Tcl became open to the public after Ousterhout presented a paper on Tcl at the UNENIX Conference and found many people were interested in getting a copy of Tcl. Shortly after the conference he began working on Tk, a graphical user interface extension to Tcl, which was also available to the public. From 1989 to 1993, Tcl/Tk popularity grew an order of magnitude each year, expanding a dozen users to several tens of thousands by 1993. This growth was due to the fact that Tk was the easiest way to create graphic user interfaces in UNIX (which at the time was the only thing Tcl/Tk ran on) and also because of Tcl’s embeddable nature. 7 A good portion of development on Tcl actually took place in 1994 when Ousterhout left his position at Berkeley and began working for Sun Microsystems in order to make Tcl a universal scripting language for the Internet. In 2005, Ousterhout recalled some of the advancements made during his time at Sun: The additional resources provided by Sun allowed us to make major improvements to Tcl and Tk. Scot Stanton and Ray Johnson ported Tcl and Tk to Windows and the Macintosh, so that Tcl became an outstanding crossplatform development environment; today, more than twothirds of Tcl downloads are for Windows. Jacob Levy and Scott Stanton overhauled the I/O system and added socket support, so that Tcl could easily be used for a variety of network application. Brian Lewis built a bytecode compiler for Tcl scripts, which provide speedups of as much as a factor of 10x. Jacob Levy implemented SafeTcl, a powerful security model that allows untrusted scripts to be evaluated safely. We added many other smaller improvements, such as dynamic loading, namespaces, time and date support, binary I/O, additional file manipulation commands and an improved font mechanism. 8
In 1998, Ousterhout left Sun and started his own business called Scriptics to focus entirely on Tcl; within a month, half of the Sun Tcl team had joined him and began development on TclPro, a retail version of Tcl. In the spring of 1998, Tcl won the ACM Software System Award; awarded each year for a software system that has had a lasting influence. 9 3.0 Syntactic Structure There are eleven important rules that define the syntax of the Tcl language. These eleven rules define the core of the language, if one masters these rules; he is on the way to mastering Tcl. The purpose of these rules is to help the programmer understand how the language works, variable declaration, control structures, and processes. 3.1 Words
7
John Ousterhout Tcl Developer Xchange “History of Tcl”, October 2005. 28 October 2006, ActiveState. http://www.tcl.tk/about/history.html 8 Ibid. 9 Ibid.
3 Tcl code consists of Strings; each word in the String is called a “word.” Words of a command are separated by white space, with the exception of newlines which indicate the conclusion of a command. 10 Each word may have an arbitrary string value, and the white space is not part of the word unless quoted. 11 Aword “This is a word” Multiple words 3.2 Commands Everything in Tcl is a string containing commands. Each command consists of one or more words with the first word being the name of the command, and each additional word being the arguments to the command. Commands are separated by semicolons and newlines unless the String is quoted. 3.3 Evaluation Tcl evaluates command in a twostep process, parsing and execution. 12 In the parsing step Tcl interprets the syntactic rules and divides every command into words and perform substitutions. Every command is parsed in exactly the same way; during the process the interpreter does not give any meaning to the value of the words. The parser simply replaces variables, such as $a; however the interpreter does not know or care what the variable is. The execution step of the evaluation assigns meaning to the words. As stated above, the first word in a command is the name of the command being executed. During the execution step the interpreter checks to see if a command by that name is defined. Tcl invokes a command, passing each additional word to the command procedure. Then the procedure interprets the words as it pleases. This process is shown in figure 1.
10
Develop Connection “Tcl(n): Tcl BuiltIn Commands,” November 6, 2006 http://developer.apple.com/documentation/Darwin/Reference/ManPages/mann/Tcl.ntcl.html 11 John Ousterhout, Tcl and Tk Toolkit (Partial Draft). AddisonWesley Publishing Company, Inc 1993, 26 12 Ibid. 26
4
Figure 3.1 Tcl command evaluation. 13
3.4 Substitution One of the most important aspects of Tcl is substitution. There are three types of substitutions: backslash substitution, command substitution, and variable substitution. Substitution simply replaces some word with some other value. Substitution can occur at any point in a command, including the name of the command; there can be an arbitrary number of substitutions within a single word or command. 14 3.4.1 Backslash Substitution Backslash (‘\’) substitution in Tcl is exactly like how Java/Ctype languages handle substitution. In most cases, the symbol following the backslash will appear as an ordinary character in a word. For example “\{“ appears as a literal “{“ in the word. There is nothing special about this type of substitution: it is mainly used for formatting words, e.g., when printing a dollar amount “\$” would be used for a $ to literally appear. 3.4.2 Command Substitution Command substitution causes a command to be executed by another command. set x [expr 1 + 3]
13 14
Ibid 27 Ibid 28
5 Is an example of this; the first command sets a variable x to the result of the expression [expr 1 + 3]. Any character between the brackets ([ ]) must constitute a valid Tcl command. Command substitutions may appear anywhere in a word and there may be more than one substitution in a single word. 15 The substitution of commands is done recursively by the Tcl interpreter. 16 An example of this can be found in the Random Text Generator discussed in Appendix A.3. 3.4.3 Variable Substitution Words that have a leading dollar sign (‘$’) are interpreted as variable after the set command has been issued. set a 3; puts “$a” Sets the variable ‘a’ to 3 and prints it out. Notice in the print statement how the ‘a’ has a leading $, therefore it replaces $a with 3 and prints 3. Similar to command substitution, variable substitution may appear anywhere in a word, and there may be more than one substitution in a single word. 17 3.4.4 Order of Substitution Substitution can be confusing if one is not used to it. However, Ousterhout discusses two rules that explain how substitution is performed: A typical scenario is for a users to be surprised at the behavior of a script because a substitution didn’t occur when the user expected it to happen, or a substitution occurred when it wasn’t expected. However, I think you’ll find Tcl’s substitution mechanism to be simple and predictable if you just remember two related rules: 1. Tcl parses a command and makes substitutions in a single pass from left to right. Each character is scanned exactly once. 2. At most, a single layer of substitution occurs for each character; the result of one substitution is not scanned for further substitution. Tcl substitutions are simpler and more regular than you may be used to if you’ve programmed with UNIX shells (particularly csh) 18
So each substitution occurs lefttoright and each one is evaluated before continuing. For example: set y [set x 0][incr x][incr x] (Note: incr. increases value of x by 1.) This example will always set the variable ‘y’ to the value 012. This tells us a lot about the language and its scope. 19 3.4.5 Substitution and Word Boundaries 15
Ibid. 29 Developer Connection “Tcl(n): Tcl BuiltIn Commands” November 6, 2006 17 Ibid. 18 John Ousterhout, Tcl and the Tk Toolkit (Partial Draft) AddisonWesley Publishing Company, Inc 1993, 34. 19 Developer Connection “Tcl(n): Tcl BuiltIn Commands” November 6,2006 http://develper.apple.com/documentation/Darwin/Reference/ManPages/mann/Tcl.ntcl.html 16
6
Substitutions do not affect the word boundaries of a command. During the substitution process, the value that is being substituted becomes part of the word it is next to, even if the substitution contains special characters, such as white space. 20 3.5 Double Quotes There are times when a programmer wants a word to contain white space, e.g. when trying to set a variable to a string of words. This is accomplished by placing double quotes around the words. Double quotes are not the only way to do this; it is important to recognize this since there are different interpretations depending on how words are connected, either with double quotes or with braces. If the first character of a word is a double quote (“) then the word is terminated by the next double quote character. If any special character appears in between the two double quotes (semicolons, closing brackets, white space, etc). It is treated as any other ordinary characters and literally included in the word. All three types of Tcl substitution also take place while in between double quotes. 21 3.6 Braces The alternative to double quotes is the brace (‘{‘). Similar to double quotes, the word will terminate by the matching closing brace. Unlike the double quotes, braces may be nested. The main difference between the double quote and the brace lies with substitution. No substitutions are performed on any characters in the word when using braces. Everything loses its special interpretation while in the braces, meaning the word typed between the braces is the literal word you get. 22 Braces are usually only used in special cases, such as the use of the eval command. 3.7 Comments A comment always begins with a hash mark (#) at the beginning of a line; note that the beginning of a line follows a new line character or a semicolon. The hash mark must be the very first nonblank character on the line. This is very similar to how comments are done in languages such as Python except that comments must be on a new empty line. 23 An interesting side effect of comments is if you have special characters inside a comment, the interpreter will not always behave as expected, thinking the special character is not inside of a comment. It is preferred to put comments in a particular form: # [this is a comment]
20
Ibid. Ibid. 22 Ibid. 23 John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 33 21
7 The reason for this is to eliminate problems caused by a special character within the comment. 4.0 Variables It is not uncommon for people to call Tcl and a string oriented language, and for good reason. In Tcl, every single data type is a string on the Command String layer; this includes: numbers, characters, lists, code, and dictionaries. 24 The interpreter reads these strings and depending on what kind of command is being executed, it interprets the string as a different type, such as an integer. Even though there is only one data type in Tcl there are two different kinds of variables: basic variables, such as traditional strings, and arrays and lists. 25 4.1 Basic Variables Basic variables are very simple; they consist of only two things, a name and a value. Both things can be arbitrary strings. Some examples of variable names and variable values are: “This is a Variable” !@# 22 All of these are treated as strings and any of them could be the variable name or variable value. 26 There are few constraints on either the variable name or variable value. This means the variable names may be as long as one likes and may contain nearly any character; an exception is that variables can not start with ‘{‘, unless the variable name is enclosed in quotes. To access a variable with a unique name, the $ is placed in front of the variable name. Like JavaScript or PHP, Tcl has dynamic type binding; this permits the programmer to not specify the type when declaring a variable. Tcl uses the command set to create, read, and modify variables. The set command takes one or two parameters, each yield a different result. The first argument is always the name of the variable. The second argument is an optional value for the variable. If no second argument is given, set reads the variable and returns the value it holds, if any. This is a key command to Tcl that is often used. Here is an example of the set command: set a {Eggs: $2.18/dozen} >> Eggs: $2.18/dozen set a >> Eggs: $2.18/dozen set a 44 24
Salvatore Sanfilippo Tcl Wise: Guide to the Tcl Programming Language (PreRelease) http://www.invece.org/tclwise/frontcover.html 25 John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 37 26 Ibid. 37
8 >> 44 In the above sample, the code sets ‘a’ to the string “Eggs: $2.18/dozen”, then reads the value of ‘a’ and finally modifies the value, changing it to the string “44”. 27 Tcl variables can represent anything: numbers, list, scripts, etc.; however, they are always stored as strings. Tcl is a typeless language, so there is no need to declare the variables before setting them. 28 This makes Tcl variables very easy to understand, because variable interpretation is done under the covers by Tcl. 4.2 Abstract Data Types Tcl has two abstract data types, the Array and the List. These two abstract data types are at the very core of the language, allowing the programmer to keep collections of strings in a certain format and use them in their respective ways. 4.2.1 Arrays The Array is a collection of elements with a name and a value. The name of an array element also has two parts, the name of the array and the name of the element within the array. Similar to basic variable array names and element names, the value may be any arbitrary string. Many programmers associate the Tcl array with an associative array, hash map, or dictionary which often appears in other languages. 29 Similar to the basic variable, you use the command set to assign a value to the array: array set “capital(New Mexico)” “Santa Fe” >> Santa Fe As expected, you may think this is exactly the same as a basic variable, the variable name is “capital(New Mexico)” and the variable value is “Santa Fe” and you would be absolutely correct. The difference between the two is the commands that can be used on the variable. For example there is a copy function that can be applied to arrays: array set B [array get A] Means get the array A and set it to B. This is just one of the many helpful commands that are associated with arrays. 30 Arrays are singularly dimensioned; however, there is a way to represent arrays as multidimensioned. The idea behind this is to create an array and set the name of the element to: set “matrix(index1,index2)”
27
John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 37 Ibid. 38 29 Ibid. 39 30 Wiki.tcl.tk “Tcl Heritage” 16 July 2006. 28 October 2006 http://wiki.tcl.tk/532 28
9 This is actually an array with the key being “index1,index2”. 31 4.2.2 Lists The other type of structure is the list, which is simply a collection of strings. This allows the user to collect any number of values in a single location. Lists are represented as strings with special structures, so one can store lists into a variable, nest them into another list, and even type them to commands. In other words, a list is simply a string separated by spaces or tabs. set myList [list this is hello world] >> this is hello world lsort myList >> hello is this world. There are many special features that can be only performed on lists, such as sorting, getting the length, or getting a certain element with the list. 32 Unlike most traditional list we can access them by index with the lindex command, for example: lindex “this is a list” 1 >> is lindex “this is a list” end >> list The above example grabs an element from a list. Notice that list can be set to a variable or indexed on the fly. Also, indexing starts at zero, similar to languages such as Java. We also have a keyword, “end,” which is the index to the end of the list. The Tcl list has many unique commands built in, making lists very powerful tools. 5.0 Flow of Control The flow of control statements in Tcl are very similar to the control structures found in C programming language and command line languages like csh. These statements include, if, while, for, foreach, switch, and eval. The major difference is that each one is a command that takes a different number and type of arguments. if {cond_1} {script_1} elseif {cond_2} {script_2} else {script_3} This is an example of the if command in Tcl, which takes an arbitrary number of arguments (words) that are parsed in a way that control flow similar to the if statement in C. Similar to the looping commands, they are commands and take arguments which the Tcl interpreter parser breaks up and executes like their C counterparts.
31 32
John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 41 Ibid. 5152
10 5.1 Eval Command One of the more unique control structures is the eval command. The purpose of the eval command is for creating and executing Tcl scripts on the fly. A Tcl script is multiple Tcl commands put together. The most important use of eval is to force another level of parsing. As mentioned earlier, Tcl parses and substitutes only once; however with the eval command multiple levels of parsing can be achieved. Suppose one wanted to remove a list of variables: set myList [list varA varB ...] foreach I $myList {unset $i} becomes: set myList [list varA varB ...] eval unset $myList with the eval command a command can be executed over an entire list, if set up properly. 33 Many Tcl’ers (a nickname for those that use Tcl) think that this is a dangerous or bad practice because of its double substitution; however, it does simplify some operations in the language. 34 6.0 SubProgramming Most usable Tcl programs use procedures. Procedures provide a quick and easy way to prototype new features in applications. Tcl procedures make for easy reuse. Procedures are similar to methods or functions as seen in other languages, with similar syntax as Java. proc name {argList} {script} Often times Tcler’s will rewrite procedures that run often in C to increase the performance of the given procedure. Custom made procedures with the eval command make a very powerful combination. The scope of a variable inside of a procedure is always local to the procedure including the arguments that are passed into the procedure, which means the value must be returned for the value to leave the procedure. This can be done in two ways, explicitly with the command return, or implicitly by defaulting to the result of the last line executed in the procedure. There are many unique things that a programmer can do with procedures and also many exceptions to the scoping of procedures. For example, one can create a global variable with the keyword “global”, but you must type “global varName in any procedure that will know of the value. Other keywords include: upvar or uplevel, which are used 33
John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 67 68 34 Wiki.tcl.tk “Tcl Heritage” 16 July 2006. 28 October 2006 http://wiki.tcl.tk/1017
11 for accessing variables outside of the scope of the procedure. These keywords can be used for writing custom control structures. 35 7.0 Personal Experience Tcl is an amazingly easy language to learn and develop software with, even more so then Python, which is often considered an easy language. Over the course of three months, I have learned a lot more about programming through writing some very simple programs in Tcl. I have enjoyed learning Tcl more than any other programming language because of its simplicity, and how easy it was to learn and understand commands. One of the more interesting things I learned about the language is that it is very expandable and easy to change the use statements to better fit my individual style. For example, if I do not like the “if” keyword for an 'if' control structure in Tcl, I can change it to the “hi” keyword by: rename if hi So, a programmer would be able to make the syntax more understandable to the user. This can be very good for writability but by doing so you sacrifice readability making it nearly impossible for others to read. Although it is very neat, I would suggest sticking to the command names that are built into the language. Active Tcl, the distribution I used, has a builtin Tcl interpreter for the Linux command line. This allows users to execute Tcl commands on the fly from the command line, making testing things in Tcl very simple. However it is not the only way to execute a Tcl program; the commandline interpreter has an optional argument, which is a name of a Tcl file that you want to execute. These are done using the tclsh in the Linux command line. There are Windows distributions of Tcl; however, for my experience I stuck to Linux. Tcl is an incredible language whose popularity has grown dramatically ever since its release. I have only scratched the surface of all the language can do. I would encourage anyone who is interested to advance their knowledge of Tcl/Tk, for you may see it in your future work place.
35
John Ousterhout, Tcl and Tk Toolkit (Partial Draft) AddisonWesley Publishing Company Inc. 1993, 69 75
A1
Appendix A: Example Tcl Code The following are some basic example of what code looks like in Tcl, using some very basic problems. Tcl code can be written on a single line if one wishes however for clarity I will try to break it up the best I can so you can clearly see some of the amazing features of Tcl.
A.1.1: n! Problem: A recursive factorial program that accepts a single integer n as input and outputs n! Input is passed to the program as a commandline parameter. A.1.2 Solution ################################################### # A procedure that find the factorial of a number # # INPUT: n, the number we are getting the factorial for # OUTPUT: the factorial of n ################################################### proc fact n { #if they put a negative number no #solution exist if { $n < 0 } { return "NonExistent" } #check if the number is the base case,1. if { $n == 1 || $n == 0 } {return 1} #call itself recursively multiplying the solution by #the current number return [expr $n * [fact [expr $n1]]] } #check if the user gave an argument via command line #if not: print a usage statement #otherwise: get the factorial of the number if {$argc != 1} { #prints a usage statement to the console puts "Usage: tclsh fractorial.tcl \$num" } else { #set the number to the user inputted value set num [expr [lindex $argv 0]] #print out to the console what the factorial of # n is. puts "The factorial of $num is [fact $num]" }
A.1.4 Sample Outputs Usage: tclsh fractorial.tcl $num The factorial of 5 is 120 The factorial of 5 is NonExistent
A.2.1 Structured Information Sorter Problem: A program that keeps track of information about Persons. A Person has a “character string” attribute called name and an “integer” attribute called age. The program should reads a commadelimited name/age pairs from a file and populate the program with a collection of Persons. It then sort the set by name, outputting the name of each Person in sorted order, and output the average age of all the Persons. A.2.2 Solution #create a list of people set people [list] #initialize variables set ageSum 0 set averageAge 0 set size 0 #Constants set _NAME 0 set _AGE 1 #Check that a file was given #if not: print usage statement if {$argc != 1} { puts "Usage: tclsh db.tcl \$filename" } else { #check to see if the file given exist, #if not: print "file does not exist" if {[file exists [lindex $argv 0]]} { #open a file (file name that is given) set filename [lindex $argv 0] set infile [open $filename r] #go through the file reading each line. while { [gets $infile line] >=0 } { #for each line trim all leading and trailing white #space and then split the line at the ','s and #add the data to the people list lappend people [split [string trim $line " "] ','] } #make a copy of people that is sorted (sort the people by name) set sortedPeople [lsort index $_NAME $people] #get the size of the list (# of people in list) set size [llength $sortedPeople]
A3
#add up all the peoples ages for {set i 0} {$i < $size} {incr i} { set ageSum [expr $ageSum + [lindex [lindex $sortedPeople $i] $_AGE]] } #get the averageAve #Note: * 1.0 on the bottom makes it not round set averageAge [expr ($ageSum) / ($size * 1.0)] #print out the names of the people in the list puts "PEOPLE IN LIST" puts "" for {set i 0} {$i < $size} {incr i} { puts [lindex [lindex $sortedPeople $i] $_NAME] } #print the average Age of the People in my Collection puts "The average age of the people in the list is: $averageAge" } else { #no file exist let user know puts "No such file exists" } }
A.2.4 Sample Output File PEOPLE IN LIST Doe Flintstone Hansen Jones Schlablotnik Smith Smith The average age of the people in the list is: 35.5714285714
A.3.1 Random Text Generator Problem: use grammars to generate somewhat random output (in contrast to a compiler which is a sentence recognizer). The idea is that we can take a grammar where each
A4 string of the form <string> is a nonterminal symbol that can be replaced using one of the productions in the grammar. The file is filled with 'productions' such as: { and ; but ; yet ; }
Where: · each production is bracketed with {} · the first line in each production holds the name of the nonterminal lefthandside of the production (i.e., in the example above) · each of the possible productions follows, terminated by a ';' (though shown on separate lines this is not a requirement) So the production above corresponds to ::= and | but | yet. Note that the first production in the grammar file will always be the 'start' symbol for the grammar defined by that file. The program reads one of these grammar file (provided as a commandline argument) and then generates text by starting with the start symbol and generating output by replacing each nonterminal with a randomly chosen production for that nonterminal. Obviously the production chosen may be a mixture of terminals and nonterminals so the process continues until there are no more nonterminals to process. In addition, we can weight the probability of a particular production being chosen by repeating it more than once in the list of productions for a particular nonterminal. Productions can be recursive, but must have at least one nonrecursive form to prevent infinite loops. A.3.2 Solution #imports the Stack namespace source Stack.tcl #return true if word is a nonterminal symbol proc isNonTerminal word { return [regexp \<*\> $word] } #return true if word is a nonterminal symbol proc isTerminal word { return [expr ![isNonTerminal $word]] } # [sets up our associative array] proc getMap {filename} { set map [list] set options [list] set _ON 1 set _OFF 0 set flag $_OFF set text "" set infile [open $filename r]
A5 # [go through the file reading each line.] while { [gets $infile line] >=0 } { # [starts the grammar parsing] if [expr ![string compare [string trim $line " "] "\{"]] { set flag $_ON set start $_ON set text "" set $options [list] } else { # [if its the first line after we begin this is a terminal] if {$flag && $start} { lappend map [string trim $line " "] set start $_OFF } elseif $flag { set text "" set options [list] set text [string trim $line " "] # [for each line after the starting line, add ] # [the line into a list] while {[gets $infile line] >=0 && $line != "\}"} { set text [concat $text [string trim $line " "]] } # [associate the nonterminal symbol with the list we created] set options [split $text ";"] set size [llength $options] lappend map [lrange $options 0 [expr $size 2]] set flag $_OFF } } } return $map } set generatedText "" #Check that a file was given #if not: print usage statement if {$argc != 1} { puts "Usage: tclsh db.tcl \$filename" } else { #check to see if the file given exist, #if not: print "file does not exist" if {[file exists [lindex $argv 0]]} { #open a file (file name that is given) set filename [lindex $argv 0] # [set up the associative array] set list [getMap $filename] set startSymbol [lindex $list 0] array set map $list } else { puts "No file exists by that name" } # [add the start symbol to the stack] Stack::push $startSymbol # [while the stack is not empty randomly generate text]
A6 while {![Stack::empty]} { # [if the next thing is a nonterminal] if [isNonTerminal [Stack::peek]] { # [get an item off the stack] set current $map([Stack::pop]) set currentSize [llength $current] # [randomly grab one of the expression associated to our ] # [nonterminal] set rndNum [expr int(rand() * $currentSize)] set innerSize [llength [lindex $current $rndNum]] # [add each item onto the stack] for {set i [expr $innerSize1]} {$i >= 0} {set i [expr $i 1]} { Stack::push [lindex [lindex $current $rndNum] $i] } } else { # [set up the generated text] set generatedText [concat $generatedText " "] set generatedText [concat $generatedText [Stack::pop]] } } # [print out generated text] puts $generatedText }
A.3.3 Sample Grammar Super Hero Acticle by Zachary A. Hayes ([email protected]) { <start> <details> . ; } { HEROS ARE BUSY THIS WEEK SAVING