├── .gitignore ├── .hgignore ├── eg ├── test.t ├── edgcase3.t ├── hello.t ├── edgcase1.t ├── edgcase2.t ├── add_bin.t ├── truth-machine.t ├── dec.t ├── inc.t ├── incany.t └── quine.t ├── .hgtags ├── doc ├── license.txt └── thue.txt ├── README.markdown ├── src ├── thue.rb ├── thue.c └── thue.py └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | 3 | bin 4 | -------------------------------------------------------------------------------- /eg/test.t: -------------------------------------------------------------------------------- 1 | ZZZ::=... 2 | ::= 3 | ZZZZZZZZZZZZ 4 | -------------------------------------------------------------------------------- /eg/edgcase3.t: -------------------------------------------------------------------------------- 1 | YYY::=... 2 | ::=G 3 | ::= 4 | ZZZZZZZZZZZZ 5 | -------------------------------------------------------------------------------- /eg/hello.t: -------------------------------------------------------------------------------- 1 | __::=~Hello, World! 2 | 3 | ::= 4 | 5 | __ 6 | -------------------------------------------------------------------------------- /eg/edgcase1.t: -------------------------------------------------------------------------------- 1 | Z ::=Y 2 | Z::=Y 3 | ::= 4 | ZZZZZZZZZZZZ 5 | -------------------------------------------------------------------------------- /eg/edgcase2.t: -------------------------------------------------------------------------------- 1 | YYY::=... 2 | ::=G 3 | ::= 4 | ZZZZZZZZZZZZ 5 | -------------------------------------------------------------------------------- /eg/add_bin.t: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shinh/Thue/master/eg/add_bin.t -------------------------------------------------------------------------------- /eg/truth-machine.t: -------------------------------------------------------------------------------- 1 | a::=::: 2 | -0-::=~0 3 | 1::=~1 4 | --::=-1- 5 | ::= 6 | -a- 7 | -------------------------------------------------------------------------------- /eg/dec.t: -------------------------------------------------------------------------------- 1 | 0_::=0-- 2 | 1_::=0 3 | 10--::=01 4 | 00--::=0--1 5 | _1--::=@ 6 | _0--::=1 7 | _0::= 8 | ::= 9 | _1000000000000_ 10 | -------------------------------------------------------------------------------- /eg/inc.t: -------------------------------------------------------------------------------- 1 | 1_::=1++ 2 | 0_::=1 3 | 4 | 01++::=10 5 | 11++::=1++0 6 | 7 | _0::=_ 8 | _1++::=10 9 | 10 | ::= 11 | 12 | _1111111111_ 13 | -------------------------------------------------------------------------------- /eg/incany.t: -------------------------------------------------------------------------------- 1 | _0::=_ 2 | 3 | 1_::=1++ 4 | 0_::=0++ 5 | 6 | 0++::=1 7 | 01++::=10 8 | 11++::=1++0 9 | _1++::=10 10 | 11 | __::=_//_ 12 | //::=::: 13 | 14 | ::= 15 | 16 | __ 17 | -------------------------------------------------------------------------------- /.hgtags: -------------------------------------------------------------------------------- 1 | 3f3acd9208c4412cf4f249ff70c6037ed1834d33 rel_1_4_2004_0301 2 | 935f97d5bb1d1c4d2b935c12c6d9ef9377abfb37 rel_1_5_2010_1218 3 | 22d25e868d9256cd7584f3c8641a526fc872647c rel_1_5_2012_0916 4 | 1ca49ba200b7ea47517053bf072fdb26a2917937 rel_1_5_2012_1028 5 | bd7299e8b3006ba6afa3411d893bc48e4a17bb08 rel_1_5_2015_0827 6 | -------------------------------------------------------------------------------- /eg/quine.t: -------------------------------------------------------------------------------- 1 | :=~~~~~=::=::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::===~~==~==~==~==~==~==~~======~~==~~~~==~~~====~~==~==~==~==~==~~======~~==~==~~~~==~~~====~~==~==~==~==~~======~~==~==~~~====~~==~==~==~~======~~==~==~~==~~~====~~==~==~~======~~==~==~==~~~====~~==~~======~~==~====~~~======~~==~~~====~~==~==~==~==~==~==~~==~~~= 2 | :=~~~~=::=~::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::===~~==~==~==~==~==~==~~======~~==~~~~==~~~====~~==~==~==~==~==~~======~~==~==~~~~==~~~====~~==~==~==~==~~======~~==~==~~~====~~==~==~==~~======~~==~==~~==~~~====~~==~==~~======~~==~==~==~~~====~~==~~======~~==~====~~~======~~==~~~====~~==~==~==~==~==~==~~==~~~= 3 | :=~~~=::=~ 4 | :=~~=::=~= 5 | :=~=::=~~ 6 | :==::=~: 7 | ::= 8 | :=~~~~~= 9 | -------------------------------------------------------------------------------- /doc/license.txt: -------------------------------------------------------------------------------- 1 | Boring Licensing Information: 2 | 3 | The contents of this file are essentially in the Public Domain, with the 4 | exception that anyone who does anything interesting with it (including, but 5 | not limited to, such pursuits as modifications, sales, professional usage, 6 | and integration with other products) is required to notify the author (the 7 | forwarding e-mail address jcolag@bigfoot.com should be stable and will be 8 | kept current) as to what that will be. 9 | 10 | This is only for the purposes of getting an feel for the direction that this 11 | work should take in the future, and also to see if I have any more recent 12 | suggestions (or newer version) that might be of use to you. 13 | 14 | In exchange for such a liberal policy, this comes with absolutely no 15 | guarantees, express or implied, on the suitability or stability of this 16 | work. As an example, don't come crying if it transforms your baby sister 17 | into a marauding supervillain. You've been warned, now, so it's your own 18 | darn fault. 19 | 20 | Licensing the License: Go ahead and use it. I would greatly appreciate it 21 | if the liability example were different for every product, however. 22 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | The Thue Programming Language 2 | ============================= 3 | 4 | This is Cat's Eye Technologies' distribution of **Thue**, an esoteric 5 | programming language designed by John Colagioia. Thue is a non-deterministic 6 | string-rewriting language, based on a formalism called a *semi-Thue system*, 7 | but also including some programming-oriented features, like input and output. 8 | 9 | The specification can be found in the file `thue.txt` in the `doc` directory. 10 | 11 | John's implementation of the language, in C, is in the file `thue.c` in the 12 | `src` directory, and can, for all intents and purposes, be considered the 13 | reference implementation. There is no `Makefile` but an executable can be 14 | built by running the included `build.sh` script, which is trivial. 15 | 16 | In the `src` directory, there are also two other implementation of Thue: 17 | 18 | * `thue.py`, in Python, written by Frédéric van der Plancke 19 | * `thue.rb`, in Ruby, written by Chris Pressey 20 | 21 | There is an assortment of example Thue programs in the `eg` directory. The 22 | credits for these are as follows: 23 | 24 | * `add_bin.t`: Frédéric van der Plancke 25 | * `edgcase?.t`: Chris Pressey 26 | * `truth-machine.t`: Keymaker 27 | * `quine.t`: TSUYUSATO Kitsune 28 | * all others: John Colagioia 29 | 30 | More information on Thue can be found on the esolangs.org wiki 31 | [entry for Thue](http://esolangs.org/wiki/Thue). 32 | 33 | Contents in this distribution are "essentially in the public domain" (scare 34 | quotes intentional.) See the file LICENSE for more information. 35 | -------------------------------------------------------------------------------- /src/thue.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # An implementation of Thue in Ruby. 4 | # Sept 10 2012, Chris Pressey, Cat's Eye Technologies. 5 | # This program is in the public domain. 6 | 7 | rules = [] 8 | state = 'rules' 9 | body = '' 10 | debug = false 11 | if ARGV[0] == '-d' then 12 | debug = true 13 | ARGV.shift 14 | end 15 | File.open(ARGV[0], "r") do |infile| 16 | while (line = infile.gets) 17 | line.chomp! 18 | if state == 'rules' then 19 | match = line.match /^(.*?)::=(.*?)$/ 20 | if match then 21 | if match[1] =~ /^\s*$/ then 22 | state = 'body' 23 | else 24 | rules.push({'lhs'=>match[1],'rhs'=>match[2]}) 25 | end 26 | end 27 | else 28 | body += line 29 | end 30 | end 31 | end 32 | 33 | while true 34 | if debug then 35 | puts body 36 | end 37 | cands = [] 38 | rules.each do |rule| 39 | start = 0 40 | while (pos = body.index(rule['lhs'], start)) 41 | cands.push({'repl'=>rule['rhs'],'pos'=>pos,'len'=>rule['lhs'].length}) 42 | start = pos + 1 43 | end 44 | end 45 | if (l = cands.length) == 0 then 46 | break 47 | end 48 | pick = cands[rand(l)] 49 | pos = pick['pos'] 50 | len = pick['len'] 51 | repl = pick['repl'] 52 | if repl == ':::' then 53 | repl = STDIN.gets 54 | repl.chomp! 55 | elsif repl =~ /^\~/ then 56 | if repl.length == 1 then 57 | puts "" 58 | else 59 | print repl[1..-1] 60 | end 61 | repl = "" 62 | end 63 | if pos == 0 then 64 | front = '' 65 | else 66 | front = body[0..pos-1] 67 | end 68 | back = body[pos+len..-1] 69 | body = front + repl + back 70 | end 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The contents of this distribution are "essentially in the public domain". 2 | The scare quotes there are intentional. For a more precise description, 3 | continue reading. 4 | 5 | John Colagioia's original license text can be found in `doc/license.txt`. 6 | (In it, the phrase "this file" referred to the archive in which all the Thue 7 | files were distributed.) He placed the files he wrote (implementation, spec, 8 | and examples) into the public domain *except* with the proviso that he be 9 | contacted by anyone who wanted to do "anything interesting" with the files, 10 | including modifying them. 11 | 12 | Unfortunately, the email address he left for this purpose, with the claim 13 | that it would be kept current, is no longer active. Thus, one can probably 14 | reasonably assume that he is no longer interested in what happens to his 15 | Thue sources, and that they are unconditionally in the public domain; 16 | however, I am not a lawyer, etc. etc. 17 | 18 | Frédéric van der Plancke's implementation, and example source, are much less 19 | questionably in the public domain; his only proviso is that he be given 20 | credit for what he wrote (and not be given credit for any changes or 21 | additions that he did not write.) Actually, as I understand it (but again, 22 | I am not a lawyer etc etc), putting something into the public domain does 23 | not take away one's moral rights to be recognized as author and to not be 24 | miscredited, so this isn't very much (if at all) different from just being 25 | in the public domain. 26 | 27 | I, Chris Pressey, hereby place my implementation in Ruby, my example Thue 28 | programs in `eg`, and the contents of the README, into the public domain, 29 | under the Creative Commons CC0 1.0 Public Domain Dedication, the details 30 | of which can be found here: 31 | 32 | http://creativecommons.org/publicdomain/zero/1.0/ 33 | 34 | The following example files were taken from the esolangs.org wiki, and 35 | are thus also under the CC0 1.0 Public Domain Dedication: 36 | 37 | truth-machine.t 38 | -------------------------------------------------------------------------------- /doc/thue.txt: -------------------------------------------------------------------------------- 1 | Welcome to the Thue Reference Manual 2 | 3 | > What is a Thue? 4 | 5 | Thue is...uhm...well... 6 | 7 | OK, I got it. Thue is a language based on the concept of the semi-Thue 8 | grammar/process, which is named for (and possibly created by) the 9 | Norwegian mathematician Axel Thue (pronounced "TOO-ay"). It is, in 10 | essence, an arbitrary grammar, which can (by its arbitrary nature) be 11 | used to define/recognize "Type 0" languages from Chomsky's hierarchy. 12 | Because the grammar can be used to define a language of such complexity, 13 | the process, itself, is essentially Turing Complete. 14 | 15 | As a result, the Thue language (which, of course, would be much funnier 16 | if it rhymed with the Infocom "dark places" creature, but c'est la vie, 17 | I guess...) is an arbitrary grammar system, not unlike yacc or a similar 18 | beast, except that there is no way to distinguish between a terminal and 19 | a nonterminal symbol--they are completely interchangeable. 20 | 21 | > Write "Thue" with burin. 22 | 23 | A Thue program consists of two parts: The first part is the set of 24 | grammar/production rules, where each rule has the form: 25 | lhs::=rhs 26 | where the lhs is the string to be recognized, and the rhs is the string 27 | which is to replace the lhs. Each string (lhs and rhs) can be completely 28 | arbitrary, except that the lhs cannot (for rather obvious reasons) 29 | include the production symbol ("::="). The rhs, however, is not 30 | restricted in any way. 31 | 32 | Terminating the rulebase is a production symbol alone on a line, surrounded 33 | by (optional) whitespace. Following that is the description of the 34 | initial state which Thue will work with. Each line after the rulebase is 35 | concatenated to form the initial state. 36 | 37 | Once loaded, the Thue program nondeterministically applies the rulebase to 38 | the current state. It continues to do so until no rules apply to the 39 | state (pragmatically, this means that no lhs can be found in the state). 40 | 41 | > What is a burin? 42 | 43 | Don't worry about it. It's an Infocom joke. 44 | 45 | Actually, let's go with it. A burin is tool to inscribe mystical symbols 46 | into an object. Thue has one. 47 | 48 | > Examine burin. 49 | 50 | Added to this simple system are two strings which are used to permit Thue 51 | to communicate with the outside world. 52 | 53 | The first of these is the input symbol (":::"). The input symbol is 54 | actually the lhs of an implicit rule of which the user (or system's "input 55 | stream") is a component. The input symbol, therefore, is replaced by a 56 | line of text received from the "input stream." 57 | 58 | As a counterpart of input, the output symbol ("~") is supplied. Like the 59 | input symbol, the output symbol triggers an implicit rule which, in this 60 | case, encompasses the "output stream." The specific effect is that all 61 | text to the right of the output symbol in the rhs of a production is sent 62 | to the output stream. 63 | 64 | Note that either (or both) of these implicit rules may be overridden by 65 | providing explicit rules that perform some other task. 66 | 67 | > Examine Thue. 68 | 69 | The implementation of Thue, itself, is rather uninteresting, except for 70 | three command-line switches: 71 | d Activates "Debug Mode," which prints the state immediately 72 | after any rule is applied. 73 | l Activates "Left Mode," which requires Thue to apply rules 74 | deterministically in a left-to-right fashion. 75 | r Activates "Right Mode," which is identical to "Left Mode," 76 | except that rule application occurs right-to-left. 77 | The command-line switches must appear after the Thue filename, and the 78 | last incidence of 'l' or 'r' overrides all others. 79 | 80 | > Look under Thue. 81 | 82 | Sample programs included are: 83 | dec.t Decrements a binary number 84 | hello.t Hello, World! 85 | inc.t Increments a binary number 86 | incany.t Increments a binary number input by the user 87 | test.t A simple example to highlight nondeterminism 88 | 89 | > Amusing. 90 | 91 | Apologies to Axel Thue for mangling the pronunciation of his name for a 92 | cheap joke. Apologies to whatever is left of Infocom for (unknowingly) 93 | supplying the format of the cheap joke. 94 | 95 | > Exit. 96 | -------------------------------------------------------------------------------- /src/thue.c: -------------------------------------------------------------------------------- 1 | /* $Header: C:\\UTILS\\CVS\\ROOT/programs\\lang\\thue/thue.c,v 1.4 2000/09/02 16:46:29 John Colagioia Exp $ 2 | * 3 | * Recent Changes: 4 | * 5 | * $Log: thue.c,v $ 6 | * Revision 1.5 2010/12/18 Chris Pressey 7 | * Fix off-by-one error in sort routine, reported by 8 | * Nathan Thern. Fix another off-by-one error in 9 | * random number generation. Modernize: use rand() 10 | * and rename getline() to avoid name conflict. 11 | * 12 | * Revision 1.4 2000/09/02 16:46:29 John Colagioia 13 | * Trivial bugfix. 14 | * 15 | * Revision 1.2 2000/02/29 21:51:24 John Colagioia 16 | * 17 | * Just added some basic header info in the comments. 18 | * 19 | * 20 | */ 21 | 22 | /* For want of a nail, 23 | the shoe was lost. 24 | For want of a shoe, 25 | the horse was lost. 26 | For want of a horse, 27 | the knight was lost. 28 | For want of a knight, 29 | the battle was lost. 30 | So it was a kingdom was lost, 31 | all for the want of a nail. 32 | -- George Herbert, Jacula Prudentum 33 | (Colloqual Adaptation) 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define SEP "::=" 43 | 44 | char * get_line (FILE * infile); 45 | 46 | struct rule 47 | { 48 | char lhs[64]; 49 | char rhs[64]; 50 | } rulebase[128]; 51 | 52 | int ruleidx = 0, 53 | debug = 0; 54 | char *dataspace, 55 | *tempspace; 56 | 57 | int main (int argc, char *argv[]) 58 | { 59 | char *line, 60 | *c, 61 | *tmp, 62 | *target[64], 63 | tempstr[64]; 64 | int state, 65 | flagstate, 66 | i, j, k, 67 | order, 68 | temp, 69 | rnum[64]; 70 | FILE *infile; 71 | 72 | srand(time(0)); 73 | dataspace = malloc(16384); 74 | tempspace = malloc(16384); 75 | if (dataspace == NULL || tempspace == NULL) { 76 | fprintf (stderr, "Could not allocate sufficient working memory\n"); 77 | exit(1); 78 | } 79 | target[0] = dataspace; 80 | memset (rulebase, 0, sizeof (rulebase)); 81 | memset (dataspace, 0, sizeof (dataspace)); 82 | if (argc > 1) 83 | infile = fopen (argv[1], "r"); 84 | else 85 | infile = stdin; 86 | if (infile == NULL) 87 | return (-1); 88 | 89 | order = 0; 90 | if (argc > 2) 91 | switch (argv[2][0]) 92 | { 93 | case 'r': /* Right-to-Left Processing */ 94 | order = 2; 95 | break; 96 | case 'l': /* Left-to-Right */ 97 | order = 1; 98 | break; 99 | case 'd': 100 | debug = 1; 101 | break; 102 | } 103 | 104 | /* Get input file */ 105 | state = 0; 106 | while (!feof (infile)) 107 | { 108 | line = get_line (infile); 109 | if (state == 0) 110 | { 111 | if (line != NULL && !strlen (line)) 112 | continue; 113 | c = strstr (line, SEP); 114 | if (c == NULL) 115 | fprintf (stderr, "Malformed production: \"%s\"!\n", line); 116 | else if (c == line) 117 | state = 1; 118 | else { 119 | flagstate = 0; 120 | for (tmp=line;tmp!=c;tmp++) 121 | if (!isspace (*tmp)) 122 | flagstate = 1; 123 | if (flagstate) 124 | { 125 | *c = '\000'; 126 | c += strlen (SEP); 127 | strcpy (rulebase[ruleidx].lhs, line); 128 | strcpy (rulebase[ruleidx].rhs, c); 129 | ++ruleidx; 130 | } 131 | else state = 1; 132 | } 133 | } 134 | else if (line != NULL) 135 | strcat (dataspace, line); 136 | } 137 | 138 | if (debug) 139 | printf ("Initial: \"%s\"\n", dataspace); 140 | 141 | /* Apply rules */ 142 | state = 1; 143 | while (state) 144 | { 145 | /* Get all valid LHSs */ 146 | j = 1; 147 | k = 0; 148 | c = dataspace; 149 | for (i=0;i 5 | # FvdP release 1.0 (2000 oct 8) 6 | # Cat's Eye Technologies revision 2010.1218 7 | # 8 | # tested with Python 1.5.1, Python 1.5.2. 9 | # 10 | # the Thue language is a creation of John Colagioia ; 11 | # Frédéric van der Plancke wrote this 12 | # interpreter. Chris Pressey made only the following changes: 13 | # - added a hash-bang line 14 | # - changed the file encoding to UTF-8 (to placate Python 2.5.5) 15 | # - modernized the `raise` statements (to placate Python 2.7) 16 | # 17 | # This code is public domain, but please give due credit 18 | # (this includes not crediting me for code _you_ have written) 19 | 20 | 21 | import string 22 | import random 23 | import sys 24 | import getopt 25 | 26 | 27 | class Rule: 28 | #self.lhs 29 | #self.rhs 30 | 31 | def __init__(self, lhs, rhs, output=None): 32 | self.lhs = lhs 33 | self.rhs = rhs 34 | self.output = output # UNUSED in current version 35 | 36 | def __str__(self): 37 | return "%s::=%s" % (self.lhs, self.rhs) 38 | 39 | def __repr__(self): 40 | if self.output: 41 | return "Rule(%s,%s, %s)" % (repr(self.lhs), repr(self.rhs), repr(self.output)) 42 | else: 43 | return "Rule(%s,%s)" % (repr(self.lhs), repr(self.rhs)) 44 | 45 | def __cmp__(self): 46 | if not isinstance(self, other): 47 | raise Exception("Rules are not comparable with anything else for now") 48 | return cmp(self.lhs, other.lhs) and cmp(self.rhs, other.rhs) 49 | 50 | 51 | def is_space(s): 52 | "test if a string is composed of spaces only" 53 | 54 | import string 55 | assert type(s) in (type(""),) 56 | 57 | for c in s: 58 | if c not in string.whitespace: 59 | return 0 60 | else: 61 | return 1 62 | 63 | 64 | def find_all(s, pattern): 65 | """return ordered list of indexes where [pattern] appears in [s]; 66 | """ 67 | #print "###Finding [%s] in [%s]..." % (pattern, s) 68 | 69 | shift_on_match = 1 70 | 71 | i = 0 72 | indexes = [] 73 | while 1: 74 | i = string.find(s, pattern, i) 75 | if i >= 0: 76 | indexes.append(i) 77 | i = i + shift_on_match 78 | else: 79 | break 80 | 81 | #print "###For %s found: %s" % (pattern, indexes) 82 | return indexes 83 | 84 | 85 | def print_truncated(what, width=79, dotdotdot="..."): 86 | if width is None or len(what) < width: 87 | print what 88 | else: 89 | if width <= len(dotdotdot): print dotdotdot[:width] 90 | else: print what[ : width - len(dotdotdot)] + dotdotdot 91 | 92 | 93 | #------------------------------------------------------------------------- 94 | 95 | def parse_program(program, sep = "::=", output_sep = "~", **options): 96 | "(program : lines) -> (rules : Rule list, dataspace : string)" 97 | 98 | rules = [] 99 | dataspace = [] 100 | 101 | state = 0 # 0=rules, 1=dataspace 102 | 103 | for line in program: 104 | ''' 105 | @ code for comments; removed because we don't actually need 106 | @ any special Thue construct to allow for comments ! 107 | 108 | #Comments - ANY # is start of a comment 109 | comment = string.find(line, "#") 110 | if comment >= 0: 111 | line = line[ : comment] 112 | line = string.rstrip(line) 113 | ''' 114 | 115 | if line[-1:] == '\n': 116 | line = line[:-1] 117 | 118 | if is_space(line): 119 | continue 120 | 121 | #--debug--: 122 | if options.get("debug"): 123 | print line 124 | 125 | if state == 1: 126 | dataspace.append(line) 127 | continue 128 | 129 | isep = string.find(line, sep) 130 | if isep < 0: 131 | #if line[:1] == "#": #comment ! 132 | # continue 133 | if is_space(line): continue 134 | raise Exception('Malformed production: "%s"' % line) 135 | else: 136 | lhs = line[ : isep] 137 | rhs = line[isep + len(sep) : ] 138 | if is_space(lhs): 139 | if not is_space(rhs): 140 | raise Exception('Malformed production: "%s"' % line) 141 | state = 1 142 | else: 143 | output = None 144 | #in current version, output is handled at rule application 145 | rules.append(Rule(lhs, rhs, output)) 146 | 147 | return (rules, string.join(dataspace, "")) 148 | 149 | 150 | #------------------------------------------------------------------------- 151 | 152 | def step(rulebase, dataspace, **options): 153 | "return (new dataspace, applied-rule-flag)" 154 | 155 | match_choice = options.get("match_choice", "") 156 | debug = options.get("debug", 0) 157 | print_width = options.get("print_width", None) 158 | 159 | #-- find all matches 160 | 161 | matches = [] 162 | for rule in rulebase: 163 | indices = find_all(dataspace, rule.lhs) 164 | for i in indices: 165 | matches.append((i, rule)) 166 | 167 | #-- return if none 168 | 169 | if len(matches) == 0: 170 | if debug: print "Done" 171 | return (dataspace, 0) 172 | 173 | #-- choose a match 174 | 175 | if match_choice == "L": 176 | match = min(matches) 177 | elif match_choice == "R": 178 | match = max(matches) 179 | else: 180 | match = matches[random.randint(0, len(matches)-1)] 181 | 182 | #-- apply the matched rule 183 | 184 | (pos, rule) = match 185 | endpos = pos + len(rule.lhs) 186 | assert dataspace[pos : endpos] == rule.lhs 187 | 188 | #iOutput = string.find(rule.rhs, "~") 189 | if rule.rhs[:1] == "~": 190 | sys.stdout.write(rule.rhs[1:]) 191 | replacement = "" 192 | elif rule.rhs == ":::": 193 | replacement = raw_input() 194 | else: 195 | replacement = rule.rhs 196 | 197 | dataspace = dataspace[ : pos] + replacement + dataspace[endpos : ] 198 | if debug: 199 | #print "|", 200 | print_truncated(dataspace, print_width) 201 | 202 | 203 | return (dataspace, 1) 204 | 205 | 206 | def execute(rulebase, dataspace, **options): 207 | 208 | continued = 1 209 | while continued: 210 | (dataspace, continued) = apply(step, (rulebase, dataspace), options) 211 | # 'apply(f, args, {k:v, ...})' is essentially 'f(args, k=v, ...)' 212 | 213 | 214 | def execute_from_source(program, **options): 215 | (rules, data) = apply(parse_program, (program,), options) 216 | 217 | if options.get("debug", 0): 218 | print "Rules:" 219 | for r in rules: 220 | print repr(r) 221 | print "Dataspace & its transformation:" 222 | print_truncated(data) 223 | 224 | 225 | #execute(rules, data, **options) # won't work before Python 2 226 | apply(execute, (rules, data), options) 227 | 228 | #------------------------------------------------------------------------- 229 | 230 | main_usage = """ 231 | usage: [python] thue[.py] [] 232 | 233 | where can be any space-separated combination of: 234 | -d debug mode (prints: rules; dataspace after each transformation) 235 | -w N in debug mode, dataspace print width is limited to N characters; 236 | if dataspace exceeds that limit, the offending part is 237 | replaced with '...'. 238 | -l execute leftmost matches first 239 | -r execute rightmost matches first 240 | (default: matches are executed in random order) 241 | """ 242 | 243 | main_options = "dlr" 244 | 245 | 246 | class BadArgument(Exception): pass 247 | 248 | def main(argv = None): 249 | if argv is None: argv = sys.argv[1:] 250 | 251 | try: 252 | (option_list, args) = getopt.getopt(argv, "dlrw:") 253 | except getopt.error, e: 254 | print e 255 | print main_usage 256 | sys.exit(1) 257 | 258 | options = {} 259 | try: 260 | if len(args) != 1: 261 | raise BadArgument("exactly one program file name is required") 262 | (progname,) = args 263 | 264 | for (opt, arg) in option_list: 265 | if opt == "-l": options["match_choice"] = "L" 266 | if opt == "-r": options["match_choice"] = "R" 267 | if opt == "-d": options["debug"] = 1 268 | if opt == "-w": 269 | try: width = int(arg) 270 | except: raise BadArgument("Illegal width '%s'" % arg) 271 | options["print_width"] = int(arg) 272 | 273 | except BadArgument, e: 274 | print "error:", e.args[0] 275 | print main_usage 276 | sys.exit(1) 277 | 278 | try: prog = open(progname) 279 | except IOError: 280 | print "Could not open file %s" % progname 281 | sys.exit(1) 282 | 283 | try: prog = prog.readlines() 284 | except IOError: 285 | print "Error reading file %s" % progname 286 | 287 | apply(execute_from_source, (prog,), options) 288 | # == 'execute_from_source(prog, **options)' in before Python 2 289 | 290 | 291 | 292 | main() 293 | --------------------------------------------------------------------------------