├── INSTALL ├── Inventory ├── Makefile ├── README.md ├── Windows ├── ReadMe.txt ├── stdafx.cpp ├── stdafx.h ├── targetver.h ├── ttm.sdf ├── ttm.sln ├── ttm.suo ├── ttm.vcxproj ├── ttm.vcxproj.filters └── ttm.vcxproj.user ├── build.xml ├── src └── main │ └── java │ └── ucar │ └── ttm │ └── TTM.java ├── test.baseline ├── test.rs ├── test.ttm ├── ttm.c ├── ttm.html ├── ttm.py ├── ttm_batch_processing_pr_08.pdf ├── ttm_interpretive_language_pr_07.docx └── ttm_interpretive_language_pr_07.pdf /INSTALL: -------------------------------------------------------------------------------- 1 | There are three versions of the ttm interpreter. One is 2 | written in C, one is written Java, and one is written in 3 | Python. 4 | 5 | ------------- 6 | C Interpreter 7 | ------------- 8 | The C interpreter is deliberately contained in a single, 9 | self contained C file: ttm.c. A Makefile exists to create 10 | ttm.ex At the beginning of ttm.c, there are some directives 11 | that control features of the interpreter. Currently the 12 | only directives are as follows: 13 | 1. #define HAVE_MEMMOVE - define this if your C compiler/library 14 | supports the memmove() function, undefine otherwise. 15 | 16 | You may also need to change the following lines in the Makefile. 17 | 1. CC - to specify the C compiler 18 | 2. CCWARN - compile time checks 19 | 3. CCDEBUG - to set the optimization and debug levels 20 | 21 | Otherwise, compiling is as simple as "${CC} -o ttm ttmc." 22 | where ${CC} is your local C compiler. 23 | 24 | Windows Support 25 | --------------- 26 | A Windows solutions file is defined in the directory 27 | "Windows". It currently will build and run under Visual 28 | Studio C++ 2010, but the resulting ttm.exe fails when run 29 | standalone. Any help in fixing this would be Appreciated. 30 | 31 | ------------- 32 | Java Interpreter 33 | ------------- 34 | The Java implementation is contained in the single file 35 | src/main/java/ucar/ttm/TTM.java. An ant build.xml file 36 | exists to compile it. The Java version is noticably slower 37 | than the C version. 38 | 39 | ------------- 40 | Python Interpreter 41 | ------------- 42 | The python implementation is contained in the single file 43 | ttm.py. It currently used python version 2.6.7; it has not 44 | been tested against python 3.0 yet. It does not need to be 45 | compiled per-se, just executed. It can be tested using the 46 | command "make pycheck". 47 | 48 | 49 | -------------------------------------------------------------------------------- /Inventory: -------------------------------------------------------------------------------- 1 | Inventory 2 | README.md 3 | INSTALL 4 | Makefile 5 | build.xml 6 | test.baseline 7 | test.rs 8 | test.ttm 9 | ttm.c 10 | ttm.html 11 | ttm.py 12 | ttm_batch_processing_pr_08.pdf 13 | ttm_interpretive_language_pr_07.docx 14 | ttm_interpretive_language_pr_07.pdf 15 | src/main/java/ucar/ttm/TTM.java 16 | Windows/Debug 17 | Windows/ipch 18 | Windows/ReadMe.txt 19 | Windows/stdafx.cpp 20 | Windows/stdafx.h 21 | Windows/targetver.h 22 | Windows/ttm.sdf 23 | Windows/ttm.sln 24 | Windows/ttm.suo 25 | Windows/ttm.vcxproj 26 | Windows/ttm.vcxproj.filters 27 | Windows/ttm.vcxproj.user 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXPORT=f:/git/export 2 | .PHONEY: check 3 | 4 | CC=gcc 5 | CCWARN=-Wsign-compare -Wall -Wdeclaration-after-statement 6 | CCDEBUG=-g -O0 7 | 8 | all: ttm.exe 9 | 10 | clean:: 11 | rm -f ttm.exe ttm ttm.txt test.output tmp 12 | 13 | ttm.exe: ttm.c 14 | ${CC} ${CCWARN} ${CCDEBUG} -o ttm ttm.c 15 | 16 | ttm.txt:: 17 | rm -f ttm.txt 18 | gcc -E -Wall -Wdeclaration-after-statement ttm.c > ttm.txt 19 | 20 | TESTPROG=-p test.ttm 21 | TESTARGS=a b c 22 | TESTRFLAG=-f test.rs 23 | TESTCMD=./ttm ${TESTPROG} ${TESTRFLAG} ${TESTARGS} 24 | PYCMD=python ttm.py -dT ${TESTPROG} ${TESTRFLAG} ${TESTARGS} 25 | 26 | check:: ttm.exe 27 | rm -f ./test.output 28 | ${TESTCMD} >& ./test.output 29 | diff -w ./test.baseline ./test.output 30 | 31 | pycheck:: ttm.py 32 | rm -f ./test.output 33 | ${PYCMD} >& ./test.output 34 | diff -w ./test.baseline ./test.output 35 | 36 | git:: 37 | ${SH} ./git.sh 38 | 39 | install:: 40 | rm -fr ${EXPORT}/ttm/* 41 | cp -fr ./git/ttm ${EXPORT} 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project Description 2 | The goal of the **ttm** project is to provide 3 | a reference implementation for the TTM programming language. 4 | 5 | TTM is a form of macro processor similar to TRAC, GAP, and GPM. 6 | 7 | # Syntax and Semantics 8 | It is assumed that TTM is given a text file containing some combination 9 | of ordinary text and TTM function calls (i.e. invocations). 10 | The text is scanned character by character. Any ordinary text is passed 11 | to the output unchanged (except when escaped). 12 | If a TTM function is encountered, it is collected and executed. 13 | The general form of a TTM function call looks like this. 14 |
15 | #<functionname;arg1;arg2;...;argn>
16 | 
17 | where the functionname and the arguments are arbitrary character strings 18 | not characters of significance: '#', '<', '>', and ';'. 19 | The function is invoked with the specified arguments and the resulting 20 | text is inserted into the original text in place of the function call. 21 | If the function call was prefixed by a single '#' character, then scanning 22 | will resume just ''before'' the inserted text from the function call. If the 23 | function call was prefixed by two '#' characters, then scanning 24 | resumes just ''after'' the inserted text. 25 | 26 | During the collection of a function call, additional function calls 27 | may be encountered, for example, this. 28 |
29 | #<functionname;arg1;#<f2;arg;...>;...;argn>
30 | 
31 | The nested function call will be invoked when encountered and the result 32 | inserted into the text of the outer function call and scanning of 33 | the outer function call resumes at the place indicated by the number 34 | of '#' characters preceding the nested call. 35 | 36 | If a function takes, for example, 2 arguments, any extras 37 | are ignored. For user defined functions, if too few arguments 38 | are provided, additional one are added with the value of the empty 39 | string (""). 40 | 41 | As with other 42 | applicative programming languages, 43 | a TTM function may be recursive and may be defined as the result 44 | of the invocation of a sequence of other function calls. 45 | 46 | Functions are either ''built-in'' or user defined. A large number of built-in 47 | functions exist and are defined in the 48 | [TTM reference manual](https://github.com/Unidata/ttm/blob/master/ttm_batch_processing_pr_08.pdf). 49 | 50 | # External Links 51 | * [A reference implementation of TTM.](https://github.com/Unidata/ttm) 52 | * [Caine, S.H. and Gordon, E.K., TTM: An Experimental Interpretive Language. California Institute of Technology, Willis H. Booth Computing Center, Programming Report No. 7, 1968.](https://github.com/Unidata/ttm/blob/master/ttm_interpretive_language_pr_07.pdf) 53 | * [Caine, S.H. and Gordon, E.K., TTM: A Macro Language for Batch Processing. California Institute of Technology, Willis H. Booth Computing Center, Programming Report No. 8 May, 1969.](https://github.com/Unidata/ttm/blob/master/ttm_batch_processing_pr_08.pdf) 54 | -------------------------------------------------------------------------------- /Windows/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | CONSOLE APPLICATION : ttm Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this ttm application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your ttm application. 9 | 10 | 11 | ttm.vcxproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | ttm.vcxproj.filters 18 | This is the filters file for VC++ projects generated using an Application Wizard. 19 | It contains information about the association between the files in your project 20 | and the filters. This association is used in the IDE to show grouping of files with 21 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 22 | "Source Files" filter). 23 | 24 | ttm.cpp 25 | This is the main application source file. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | Other standard files: 29 | 30 | StdAfx.h, StdAfx.cpp 31 | These files are used to build a precompiled header (PCH) file 32 | named ttm.pch and a precompiled types file named StdAfx.obj. 33 | 34 | ///////////////////////////////////////////////////////////////////////////// 35 | Other notes: 36 | 37 | AppWizard uses "TODO:" comments to indicate parts of the source code you 38 | should add to or customize. 39 | 40 | ///////////////////////////////////////////////////////////////////////////// 41 | 42 | -------------------------------------------------------------------------------- /Windows/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // ttm.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /Windows/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | 14 | 15 | // TODO: reference additional headers your program requires here 16 | -------------------------------------------------------------------------------- /Windows/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /Windows/ttm.sdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unidata/ttm/8dfcf5fecbb0556dbb8075b9632a99cfd805d9c9/Windows/ttm.sdf -------------------------------------------------------------------------------- /Windows/ttm.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ttm", "ttm.vcxproj", "{6191B577-D355-4AE1-ADBF-728F1EA4D042}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {6191B577-D355-4AE1-ADBF-728F1EA4D042}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {6191B577-D355-4AE1-ADBF-728F1EA4D042}.Debug|Win32.Build.0 = Debug|Win32 14 | {6191B577-D355-4AE1-ADBF-728F1EA4D042}.Release|Win32.ActiveCfg = Release|Win32 15 | {6191B577-D355-4AE1-ADBF-728F1EA4D042}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Windows/ttm.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Unidata/ttm/8dfcf5fecbb0556dbb8075b9632a99cfd805d9c9/Windows/ttm.suo -------------------------------------------------------------------------------- /Windows/ttm.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {6191B577-D355-4AE1-ADBF-728F1EA4D042} 15 | Win32Proj 16 | ttm 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | 24 | 25 | Application 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 53 | Disabled 54 | 55 | 56 | Console 57 | true 58 | 59 | 60 | 61 | 62 | Level3 63 | 64 | 65 | MaxSpeed 66 | true 67 | true 68 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 69 | 70 | 71 | Console 72 | true 73 | true 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | false 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Windows/ttm.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Windows/ttm.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -r ../test.rs -p ../test.ttm a b c 5 | WindowsLocalDebugger 6 | 7 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /test.baseline: -------------------------------------------------------------------------------- 1 | xxcomment,0,0,V residual=0 body=|| 2 | yydef,0,3,V residual=0 body=|##>##| 3 | comment,0,0,V residual=0 body=|| 4 | [00] begin: ## 5 | [00] end: ## => "functionbodyy1" 6 | [00] end: # => "" 7 | [00] begin: ## 8 | [00] end: ## => "functionbodyy4" 9 | [00] begin: # 10 | [00] end: # => "" 11 | [00] begin: ## 12 | reading line1 13 | [00] end: ## => "line1.line2" 14 | [00] begin: ## 15 | [00] end: ## => "line3" 16 | [00] begin: # 17 | [00] end: # => "" 18 | [00] begin: ## 19 | [00] end: ## => "" 20 | [00] begin: ## 21 | [00] end: ## => "" 22 | [00] begin: # 23 | [00] end: # => "" 24 | [00] begin: #> 25 | [00] end: # => "" 26 | [00] begin: #>>>>> 27 | [00] end: # => "##>>>>>>##" 28 | [00] begin: ##>>>>> 29 | [00] end: ## => "" 30 | [00] begin: ## 31 | [00] end: ## => "" 32 | [00] begin: # 33 | [00] end: # => "#>>>>" 34 | [00] begin: #>>> 35 | [00] end: # => "#>>" 36 | [02] begin: # 37 | [02] end: # => "2" 38 | [01] begin: # 39 | [01] end: # => "#>>>>" 40 | [01] begin: #>>> 41 | [01] end: # => "#>>" 42 | [03] begin: # 43 | [03] end: # => "1" 44 | [02] begin: # 45 | [02] end: # => "#>>>>" 46 | [02] begin: #>>> 47 | [02] end: # => "1" 48 | [01] begin: # 49 | [01] end: # => "2" 50 | [00] begin: # 51 | [00] end: # => "6" 52 | 53 | 54 | 55 | 56 | 57 | 58 | greek,0,0,V residual=0 body=|ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ| 59 | kanji,0,0,V residual=0 body=|龥| 60 | katakana,0,0,V residual=0 body=|キャク| 61 | 有為の,0,0,V residual=0 body=|奥山| 62 | 63 | 64 | 65 | 66 | 67 | x,0,1,V residual=0 body=|12^0134567890| 68 | 69 | 1 70 | 2^01345678 71 | 90 72 | x,0,1,V residual=11 body=|12^0134567890| 73 | 74 | 75 | 2 76 | d,0,2,V residual=0 body=|^01:^02| 77 | 78 | d defined 79 | dd not defined 80 | 4 81 | b 82 | 14 83 | -16 84 | 64 85 | 15 86 | 5 87 | 88 | 1200005 89 | 2 90 | 2 91 | equal 92 | !equal 93 | equal? 94 | !equal? 95 | gt 96 | !gt 97 | gt? 98 | !gt? 99 | lt 100 | !lt 101 | lt? 102 | !lt? 103 | dcba 104 | 105 | 12 106 | 345 107 | 108 | 109 | a 110 | 5 111 | 1;(a,b);c 112 | a;b 113 | a;b 114 | b,(c);f 115 | 116 | test defined 117 | 118 | test not defined 119 | 120 | test not eos 121 | 123 122 | test eos 123 | 124 | 125 | test_sn eos 126 | 127 | 128 | 129 | 01 130 | 131 | testclass,0,0,V residual=3 body=|01Ab5| 132 | 133 | b5 134 | testclass,0,0,V residual=5 body=|01Ab5| 135 | 136 | 137 | testclass,0,0,V residual=0 body=|01Ab5| 138 | 139 | digit found 140 | 01Ab5 141 | digits,nonlc 142 | 143 | 144 | 145 | 146 | match 147 | testisc,0,0,V residual=3 body=|012345| 148 | 149 | match 150 | testisc,0,0,V residual=5 body=|012345| 151 | 152 | no match 153 | testisc,0,0,V residual=5 body=|012345| 154 | 155 | match 156 | testisc,0,0,V residual=6 body=|012345| 157 | 158 | match 159 | testisc,0,0,V residual=6 body=|012345| 160 | 161 | 162 | 163 | testscn,0,0,V residual=2 body=|012345| 164 | 165 | 166 | testscn,0,0,V residual=5 body=|012345| 167 | 168 | not substring 169 | testscn,0,0,V residual=5 body=|012345| 170 | 171 | Sat Nov 10 16:23:10 2012 172 | 173 | abs,ad,ap,argc,argv,cc,ccl,cf,classes,cm,cn,comment,cp,cr,cs,ctime,dcl,def,dncl,ds,dv,dvr,ecl,eos,eq,eq?,es,exit,flip,gn,gt,gt?,include,isc,lf,lt,lt?,mu,names,ndf,norm,pf,ps,psr,rrp,rs,sc,scl,scn,sn,ss,su,tcl,tf,time,tn,ttm,uf,xtime,zlc,zlcp 174 | 175 | 176 | testcr,0,0,V residual=0 body=|abc^00def^00| 177 | 178 | abc0001def0001 179 | 180 | a 181 | testcp,0,0,V residual=7 body=|a;d;<;>| 182 | 183 | d 184 | testcp,0,0,V residual=9 body=|a;d;<;>| 185 | 186 | <;> 187 | testcp,0,0,V residual=12 body=|a;d;<;>| 188 | 189 | 190 | 2 191 | testcs,0,1,V residual=0 body=|ab^01efg^01| 192 | 193 | ab 194 | efg 195 | 196 | testcs,0,1,V residual=7 body=|ab^01efg^01| 197 | 198 | 199 | 200 | 201 | testlf defined 202 | 203 | 204 | testlf not defined 205 | 206 | 2 207 | testcf,0,1,V residual=0 body=|ab^01d^01e| 208 | 209 | 210 | testcf2,0,1,V residual=0 body=|ab^01d^01e| 211 | 212 | 213 | 214 | 215 | functionbodyy1 216 | 217 | 218 | functionbodyy2 219 | 220 | functionbodyy3 221 | 222 | functionbodyy4 223 | 224 | line1.line2 225 | line3 226 | 227 | 228 | 229 | 230 | 231 | 232 | 6 233 | -------------------------------------------------------------------------------- /test.rs: -------------------------------------------------------------------------------- 1 | line1.line2 2 | line3 3 | -------------------------------------------------------------------------------- /test.ttm: -------------------------------------------------------------------------------- 1 | ##yy> 2 | #>> 3 | #> 4 | # 5 | # 6 | # 7 | ## 8 | # 9 | # 10 | # 11 | # 12 | ## 13 | ## 14 | ## 15 | ## 16 | ## 17 | # 18 | # 19 | ## 20 | # 21 | # 22 | # 23 | # 24 | # 25 | # 26 | # 27 | # 28 | # 29 | # 30 | # 31 | ## 32 | ## 33 | ## 34 | ## 35 | ## 36 | ## 37 | ## 38 | ## 39 | ## 40 | ## 41 | ## 42 | ## 43 | ## 44 | ## 45 | ## 46 | ## 47 | ## 48 | ## 49 | ## 50 | ## 51 | ## 52 | # 53 | ## 54 | ## 55 | ## 56 | ## 57 | # 58 | ## 59 | # 60 | ## 61 | # 62 | # 63 | # 64 | # 65 | ## 66 | ## 67 | # 68 | ## 69 | ## 70 | ## 71 | ## 72 | ## 73 | ## 74 | ## 75 | ## 76 | ## 77 | ## 78 | ## 79 | ## 80 | ## 81 | ## 82 | ##>> 83 | ## 84 | # 85 | ## 86 | ## 87 | ## 88 | ## 89 | ## 90 | ## 91 | ## 92 | ## 93 | ## 94 | ## 95 | # 96 | ## 97 | ## 98 | ## 99 | ## 100 | ## 101 | ## 102 | ## 103 | #>> 104 | # 105 | # 106 | # 107 | ## 108 | ## 109 | #;d;<;>>> 110 | ## 111 | ## 112 | ## 113 | ## 114 | ## 115 | ## 116 | # 117 | # 118 | ## 119 | ## 120 | ## 121 | ## 122 | ## 123 | # 124 | # 125 | # 126 | # 127 | # 128 | # 129 | # 130 | # 131 | # 132 | ## 133 | # 134 | ## 135 | #> 136 | # 137 | # 138 | ## 139 | # 140 | # 141 | ## 142 | # 143 | ## 144 | # 145 | ## 146 | # 147 | ## 149 | ## 150 | # 151 | ## 152 | ## 153 | # 155 | #>> 156 | #>>>>>> 157 | # 158 | -------------------------------------------------------------------------------- /ttm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

TTM

4 |

NAME

5 | ttm - macro processor 6 |

SYNOPSIS

7 | ttm 8 | [-d string] 9 | [-e string] 10 | [-f|-p programfile] 11 | [-i] 12 | [-o file] 13 | [-r rsfile] 14 | [-V] 15 | [-q] 16 | [-X tag=value] 17 | [--] 18 | [arg...] 19 | 20 |

DESCRIPTION

TTM (or ttm) 22 | is a string oriented, general purpose macro processing 23 | developed in 1968 by Steven H. Caine and E. Kent Gordon at 24 | the California Institute of Technology. 25 | 26 | The following description is taken from the original TTM reference manual[1]. 27 | TTM Is a recursive, interpretive language designed primarily 28 | for string manipulation, text editing, macro definition and 29 | expansion, and other applications generally classified as 30 | systems programming. It is derived, primarily, from 31 | GAP [3] and GPM [4]. 32 | 33 |

OPTIONS

34 | Except where indicated, all flags may be repeated multiple times. 35 |
36 |
-d string
37 |
38 | Set a variety of debug options as encoded in the string. 39 | Currently, the following debug flags are defined. 40 |
    41 |
  • 't': turn on tracing. 42 |
  • 'b': suppress execution of ttm 43 | startup commands. 44 |
45 |

46 |

-e string
47 |
48 | Scan the string and execute any macro invocations. Any output 49 | left after execution is discarded. 50 |

51 |

-p programfile
52 |
53 | Read and scan the contents of the program file. 54 | Any output is discarded. 55 |

56 |

-i
57 |
58 | After executing any -e or -p options, 59 | enter the read-eval loop to execute commands interactively. 60 | If not specified, then the interpreter will terminate execution 61 | after all other command line arguments have been processed. 62 |

63 |

-f inputfile
64 |
65 | Specify the input file. This file will be scanned 66 | and any resulting output will be sent to 67 | the -o file, if specified, otherwise to standard output. 68 | Note that this is not the same as the file 69 | specified by the -r flag. 70 |

71 |

-o file
72 |
73 | Send all output to the specified file. If not specified, then 74 | output is sent to standard output. 75 |

76 |

-r rsfile
77 |
78 | Specify a file from which #<rs> will read its input. 79 | If not specified, standard input is assumed. 80 | Note that this is not the same as the file 81 | specified by the -f flag. 82 |

83 |

-V
84 |
85 | Output the version number of ttm and then exit. 86 |
-q
87 |
88 | Suppress the output remaining after 89 | scanning the -f file. 90 |

91 |

-X tag=value
92 |
93 | Set a ttm interpreter resource limit. 94 | The tag is a single character indicating what resource 95 | to set, and the value is the value to assign to that resource. 96 | The legal tag characters are as follows: 97 | 98 |
TagResourceValueDescription 99 |
bBuffersizeinteger>0 100 | Set the internal buffer size. The default is 2^20 characters. 101 | The suffix m|M is allowed to indicate multiplying by 2^20. 102 | The suffix k|K is allowed to indicate multiplying by 2^10; 103 |
sStacksizeinteger>0 104 | Set the internal stack size. The default provides for a maximum 105 | depth of 64. It is a good idea to keep this number small 106 | so that runaway recursion can be detected quickly. 107 |
xExecutionsinteger>0 108 | Limit the number of executions. The default is 2^20. 109 | The purpose is to catch tail recursive executions that 110 | do not eat up space. 111 |
112 |

113 |

--
114 |
115 | Signal the end of options and disable further option 116 | processing. Anything after the -- is treated as an argument. 117 |

118 |

arg...
119 |
120 | Specify arguments that are accessible using the #<argv> function. 121 |
122 | 123 |

Java Command Line Format

124 | The java invocation of ttm differs from the C version as follows. 125 |
    126 |
  • Invoke using the command java -jar ttm.jar args.... 127 |
  • Options may only be specified once. 128 |
  • The options are specified using the java -D command line mechanism. 129 | The options are encoded as follows. 130 |
      131 |
    • -Dexec=string &mdash equivalent to the C -e flag. 132 |
    • -Dinteractive &mdash equivalent to the C -i flag. 133 |
    • -Dprogram=filepath &mdash equivalent to the C -p flag. 134 |
    • -Dinput=filepath &mdash equivalent to the C -f flag. 135 |
    • -Doutput=filepath &mdash equivalent to the C -o flag. 136 |
    • -Drsfile=filepath &mdash equivalent to the C -r flag. 137 |
    • -Dverbose &mdash equivalent to the C -V flag. 138 |
    • -DX=string &mdash equivalent to the C -X flag. The string 139 | is of the form :,:... 140 | 1
    • -Ddebug=string &mdash equivalent to the C -d flag. 141 |
    142 |
143 | 144 |

Python Command Line Format

145 | The python invocation of ttm is essentially 146 | the same as the C command line. 147 |
148 |     python ttm.py <command line arguments>
149 | 
150 | 151 |

OVERALL OPERATION

152 | The ttm macro processor starts 153 | by scanning any commands specified by the 154 | -e or -p options. 155 | Any result is discarded. 156 | The idea is that these options establish the environment 157 | in which to scan the input file. 158 |

159 | Next, the interpreter scans the -f input file, if specified, 160 | and executes any macro invocations encountered. Any resulting output 161 | is sent to the file specified by the -o option. If not specified, 162 | the output is sent to standard output. 163 |

164 | Finally, if the -i flag is specified, then the interpreter goes 165 | into a read-eval loop. It reads "balanced" strings 166 | from the standard input, 167 | scans the input and repeats. 168 | If no -i is specified, the interpreter exits. 169 |

170 | The term balanced means that input is read until 171 | all instances of '<' have a matching '>'. Once balanced, 172 | input reading will stop at the currently defined EOL meta-character. 173 |

174 | Any arguments after the file name are provided to ttm using the 175 | #<argv;i> function. 176 |

177 | It is assumed that TTM is given a text file (via e.g. -f) 178 | containing some combination 179 | of ordinary text and TTM function calls (i.e. invocations). 180 | The text is scanned character by character. Any ordinary text is passed 181 | to the output unchanged (taking escapes into consideration). 182 | If a TTM function is encountered, it is collected and executed. 183 | 184 |

SYNTAX AND SEMANTICS

185 | The general form of a TTM function call looks like this. 186 |
187 | #<functionname;arg1;arg2;...;argn>
188 | 
189 | where the functionname and the arguments are arbitrary character strings 190 | not characters of significance: '#', '<', '>', and ';'. 191 | The function is invoked with the specified arguments and the resulting 192 | text is inserted into the original text in place of the function call. 193 | If the function call was prefixed by a single '#' character, then scanning 194 | will resume just before the inserted text from the function call. If the 195 | function call was prefixed by two '#' characters, then scanning 196 | resumes just after the inserted text. 197 |

198 | During the collection of a function call, additional function calls 199 | may be encountered, for example, this. 200 |

201 | #<functionname;arg1;#<f2;arg;...>;...;argn>
202 | 
203 | The nested function call will be invoked when encountered and the result 204 | inserted into the text of the outer function call and scanning of 205 | the outer function call resumes at the place indicated by the number 206 | of '#' characters preceding the nested call. 207 |

208 | If a function takes, for example, 2 arguments, any extras 209 | are ignored. For user defined functions, if too few arguments 210 | are provided, additional one are added with the value of the empty 211 | string (""). 212 |

213 | As with other applicative programming languages, 214 | a TTM function may be recursive and may be defined as the result 215 | of the invocation of a sequence of other function calls. 216 |

217 | Functions are either built-in or user defined. 218 | A large number of built-in 219 | functions exist and are defined in the TTM reference manual [1]. 220 | 221 |

Function definition

222 | User defined functions are created using the following two built-in 223 | functions. 224 |
    225 |
  • #<ds;name;text> 226 |
  • #<ss;name;text1;text2...;textn> 227 |
228 |

229 | The first function, ds for "define string", 230 | defines a named string in the TTM 231 | dictionary. The name is "name" and its value is "text". 232 | Invoking this named string will cause it's invocation to be replaced 233 | by the value (i.e. "text"). 234 |

235 | The second function, ss for "segment string", 236 | scans the text of a previously defined string 237 | looking for occurrences of its arguments: text1, text2, ... textn. 238 | When an occurrence is found, it is replaced with a segment mark. 239 | All occurrences of each argument are replaced by the same segment mark. 240 |

241 | When a segmented string is invoked, each argument to the call 242 | is substituted for the corresponding segment mark. 243 | Consider this example. 244 |

245 | 01 #<ds;F;abcxxdefyy>
246 | 02 #<ss;F;xx;yy>
247 | 03 #<F;11;22>
248 | 
249 | The string F is defined (line 1) and its body "abcxxdefyy" 250 | is segmented on the two strings "xx" and "yy" (line2). 251 | When invoked (line 3), it will return the value "abc11def22". 252 | In effect, we have a user defined function F with two arguments. 253 | 254 |

Escaping

255 | It is possible to escape one or more characters using either of two 256 | conventions. 257 |
    258 |
  • <...> – escape multiple characters. 259 |
  • \ – escape a single character. 260 |
261 |

262 | If a string is enclosed in <...>, then it is scanned 263 | but not interpreted by TTM. In the scanning process, the outer 264 | < and > brackets are removed. If there are nested occurrences 265 | of <...>, then they are scanned but the '<' and '>' are not removed. 266 | The brackets must balance: the number of '<' characters 267 | must equal the number of '>' characters. 268 | 269 | The '\' escape convention causes the interpreter to pass as-is the character 270 | after the '\'. The leading '\' is left if it within a <...> 271 | escape sequence, otherwise it is removed. One use is to 272 | allow unbalanced occurrences of '<' or '>' characters 273 | [Note: in the reference implementation, '@' was changed to '\']. 274 | 275 |

Examples

276 | 277 |

Example 1: Function Definition

278 | 279 | The most basic example involves defining 280 | a function that is useful for defining addtional functions. 281 | This "meta" function is called def. 282 | It is written as: 283 |
284 | #<ds;def;<##<ds;name;<text>>##<ss;name;subs>>>
285 | #<ss;def;name;subs;text>
286 | 
287 |

288 | We can, for example, use def to define the string XX as 289 | 12345 and then segment XX on 34 by writing this. 290 |

291 | #<def;XX;34;12345>
292 | 
293 |

294 | The call 295 |

296 | #<XX;0000>
297 | 
298 | will then produce the string "1200005". 299 |

300 | The def function operates by invoking ds to define the function 301 | being defined — XX in our example — and then segmenting the body 302 | of XX by any specified arguments: "34" in this case. When XX is invoked, 303 | its argument is substituted for the segment mark. 304 | 305 |

Example 2: Factorial

306 | The factorial function can be defined (using the above #<def> function) 307 | as follows. 308 |
309 | #<def;n!;N;<#<lt;N;2;1;<#<mu;N;#<n!;#<su;N;1>>>>>>>
310 | 
311 | Notice that the inner computation (#<mu...) is escaped 312 | so it will only be evaluated after the #<lt... function 313 | is executed and returns that nested computation as its result. 314 |

315 | An example call would look like this. 316 | #<n!;3> 317 | and would return the string 6. 318 | 319 |

MODIFICATIONS

320 | This ttm processor adheres closely to the specification in the 321 | ttm specification documents [1,2]. 322 |

323 | However, the following general changes to TTM should be noted. 324 |

    325 |

    326 |

  1. The escape character is changed from '@' to '\'. 327 |

    328 |

  2. The end of input meta character is changed from ''' (i.e. single quote) 329 | to '\n' (i.e. newline). 330 |

    331 |

  3. 332 | Escape handling is slightly different and adheres more closely 333 | to the Unix rules. Specifically, the following escape sequences, 334 | when occurring outside of <...>, are translated as follows. 335 | 336 |
    Escape SequenceTranslation 337 |
    \rcarriage return 338 |
    \nline feed 339 |
    \ttab 340 |
    \bbackspace 341 |
    \fform feed 342 |
    343 | Additionally, if an escape occurs at the end of a line, 344 | then that end of line is ignored. This allows one to 345 | provide some formatting for function invocations without 346 | necessarily having to clutter up the result with new lines. 347 |

    348 |

  4. Only lower case built-in function names are supported. 349 |

    350 |

  5. 351 | This version of ttm supports (more or less) utf-8. The meta characters 352 | are restricted to be a subset of the US-ASCII 7-bit subset of utf-8. 353 | Otherwise, strings may contain any legal utf-8 multi-byte character. 354 | This allows for inclusion of utf-8 codepoints, but only limited validation 355 | is performed to see if the string is truly utf-8. 356 |

    357 | Internally, and in the C implementation, 358 | all characters are represented as 32-bit UTF characters. 359 | This wastes some space, but makes processing simpler. 360 | For Python and Java, characters are represented internally 361 | in UTF-16 because they provide native support for that format. 362 |

    363 |

364 | 365 |

A Note on Segment Marks

366 | In this implementation, segment (and creation) marks 367 | are represented as a single 32-bit integer, where the value zero 368 | indicates a creation mark and any integer greater than zero 369 | indicates the n'th segment mark. 370 | The integer has bits set so that is is not a legal UTF character. 371 |

372 | One more point about segment marks. It is possible using #<gn>, for 373 | example, to output a string with segment marks in it. The original 374 | documentation is silent on how such segment marks are to be treated. 375 | In this implementation, they are included. If an attempt is made to print 376 | them, they are converted to the form '^n' where n is the segment mark. 377 | 378 |

New Functions

379 | A number of new functions have been implemented and are describe below 380 | using a notation similar to that in [1]. 381 |

382 | ttm
383 | Specification: ttm,2,*,S
384 | Invocation: #<ttm;cmd;arg1;arg2;...>
385 | This function provides a general mechanism for embedding 386 | special commands to extract or alter interpreter information. 387 | The currently defined commands are as follows. 388 | 389 | 397 |
CommandDescription 390 |
#<ttm;meta;metachars> 391 | Change the set of meta-characters. 392 | It takes one argument that specifies the set of 393 | meta characters. Setting the default behavior would use 394 | this invocation: #<ttm;meta; \#\<\;\>\\>. 395 | Note that this is separate from the CM function. 396 |
#<ttm;info;subcommands;...> 398 | Dump information about internal ttm structures. 399 | The currently defined subcommands for info are as follows. 400 | 401 |
SubCommandDescription 402 |
#<ttm;info;name;name1;name2...> 403 | Return info about each namei. 404 |
#<ttm;info;class;class1;class2...> 405 | Return info about each classi. 406 |
407 |
408 | 409 |

410 | include
411 | Specification: include,1,1,S
412 | Invocation: #<include;filename>
413 | Read the contents of the specified file into the buffer and 414 | continue processing. For security reasons, the constraint 415 | is imposed that the file name must only be accessible 416 | through one of the include paths (i.e. using -I on the 417 | command line). This has the possibly undesirable 418 | consequence that if the user used #<include>, then the user 419 | must also specify a -I on the command line. This constraint 420 | also implies that the parameter filename must be a 421 | relative path. If the result from reading the file is unwanted, 422 | then one should use "-e #<include;filename>" on the command line. 423 | 424 |

425 | argv
426 | Specification: argv,1,1,V
427 | Invocation: #<argv;i>
428 | Return the i'th command line argument 429 | starting at 1 (like C). 430 | The parameter i should be a a non-negative 431 | integer. An argument of zero will return 432 | the invoking command's name. If the index 433 | is out of bounds, then a fatal error occurs 434 | => use the #<argc> function. 435 | 436 |

437 | argc
438 | Specification: argc,0,0,V
439 | Invocation: #<argc>
440 | Return the number of command line arguments, 441 | including the 0'th (like C). 442 | 443 | 444 |

445 | classes
446 | Specification: classes,0,0,V
447 | Invocation: #<classes>
448 | Return the names of all defined character classes 449 | arranged in sorted order and separated by commas. 450 | 451 |

452 | pf
453 | Specification: pf,0,1,S
454 | Invocation: #<pf;stderr>
455 | If no arguments are given, then flush stdout and stderr. 456 | If the single argument is the string 'stderr', 457 | then flush stderr. 458 | If the single argument is the string 'stdout' 459 | then flush stdout. 460 | 461 |

Modifications to Function Semantics

462 | The semantics of the following functions others have been changed. 463 |

464 | ad
465 | Specification: ad,2,*,V
466 | Invocation: #<ad;num1;...numn>
467 | This function normally takes only two arguments. 468 | It has been extended to take an arbitrary number 469 | of arguments and return the sum of all of the arguments. 470 | 471 |

472 | mu
473 | Specification: mu,2,*,V
474 | Invocation: #<mu;num1;...numn>
475 | This function normally takes only two arguments. 476 | It has been extended to take an arbitrary number 477 | of arguments and return the product of all of the arguments. 478 | 479 |

480 | ps
481 | Specification: ps,1,2,S
482 | Invocation: #<ps;string;stderr>
483 | This function normally prints the string on 484 | stdout. If the second argument is the string 485 | "stderr", however, then it prints to stderr. 486 | 487 |

488 | psr
489 | Specification: psr,1,1,S
490 | Invocation: #<psr;string>
491 | This function prints its argument to stdout 492 | and then reads from stdin up to the meta-character 493 | (see the cm function). 494 | 495 |

496 | cm
497 | Specification: cm,1,1,S
498 | Invocation: #<cm;string>
499 | This function specifies the character 500 | that is used to terminate input when using, 501 | for example #<rs>. 502 | The default meta-character has been changed 503 | from "'" to "\n". This means that as a rule, 504 | reading will read only a single line of input. 505 | One can, however use the escape character 506 | at the end of the line to read multiple lines 507 | with the escaped '\n' being elided. 508 | 509 |

510 | cf
511 | Specification: cf,2,2,S
512 | Invocation: #<cf;new-name;old-name>
513 | The original definition said that the body of 514 | old-name, including segment marks and beginning 515 | at the old-name's residual pointer was copied 516 | to the body of new-name. 517 | However, trying to figure out how to handle 518 | a set of segment marks with gaps is problematic. 519 | So, the definition has been modified to 520 | copy the whole body of the old-name 521 | as the body of the new name, ignoring (but duplicating) 522 | the residual pointer. 523 | 524 |

525 | exit
526 | Specification: exit,0,1,S
527 | Invocation: #<exit;exitcode>
528 | This was changed to allow optional specification 529 | of a return code. 530 | 531 |

532 | names
533 | Specification: exit,0,1,V
534 | Invocation: #<names>
535 | This was changed so that 536 | if the second argument is present, then 537 | all names are returned, otherwise only non-builtin 538 | names are returned. 539 | 540 |

541 | tn
542 | Specification: tn,0,*,V
543 | Invocation: #<tn;name1;...namen>
544 | This was changed so that only the specified function names 545 | will be traced. If there are no names provided, then all function 546 | invocations will be traced. 547 | 548 |

549 | tf
550 | Specification: tf,0,*,V
551 | Invocation: #<tn;name1;...namen>
552 | This was changed so that only the specified function names 553 | will have tracing turned off. If there are no names provided, then no 554 | function invocations will be traced. Note that turning off all tracing 555 | will turn off tracing on all specific function. 556 | 557 |

558 | ps
559 | Specification: ps,1,2,S
560 | Invocation: #<ps;string;stdxx>
561 | This was changed to an optional allow a second argument that 562 | specifies sending the output to either "stdout" 563 | or "stderr". If missing, then stdout is assumed. 564 | 565 |

566 | rs
567 | Specification: rs,0,0,S
568 | Invocation: #<rs>
569 | Read a string of characters until either EOF 570 | or the defined "end-of-input" character is reached. 571 | 572 |

573 | rs
574 | Specification: rs,0,0,S
575 | Invocation: #<rs>
576 | In order to avoid spoofing, the string 'ttm>' is printed 577 | before reading. 578 | 579 |

580 | The time function from the interactive version differs from 581 | that of the batch version. The interactive time function 582 | corresponds to the batch xtime function and the batch time 583 | function returns time of day instead of execution time. 584 | The batch versions of time and xtime are implemented. 585 | In additon, ctime is implemented to give time of day 586 | in Unix ctime format. 587 |

588 | time
589 | Specification: time,0,0,V
590 | Invocation: #<time>
591 | Returns the time of day in 100'ths of second 592 | from ?. 593 |

594 | xtime
595 | Specification: xtime,0,0,V
596 | Invocation: #<time>
597 | Returns the execution time in 100'ths of second. 598 |

599 | ctime
600 | Specification: ctime,1,1,V
601 | Invocation: #<ctime;time>
602 | Convert the time (the result of a call to #<time>) 603 | to a printable string as defined by the Unix ctime function. 604 | 605 |

606 | A number of functions (#<gn>, $<cn>, etc.) 607 | are supposed to return some fixed number of characters. 608 | The original documentation is silent about what should 609 | happen if there are not enough characters available to return. 610 | In this implementation, whatever characters are available are returned, 611 | even if less than the number requested. 612 | 613 |

IMPLEMENTED FUNCTIONS

614 | The following functions from the original ttm 615 | are implemented. 616 | 617 | 618 | 619 | 624 | 629 | 634 | 639 | 644 | 649 | 654 | 659 | 664 | 669 | 674 | 679 |
abs,1,1,V 620 | ad,2,2,V 621 | ap,2,2,S 622 | cc,1,1,SV 623 |
ccl,2,2,SV 625 | cf,2,2,S 626 | cm,1,1,S 627 | cn,2,2,SV 628 |
cp,1,1,SV 630 | cr,2,2,S 631 | cs,1,1,SV 632 | ctime,1,1,V 633 |
dcl,2,2,S 635 | dncl,2,2,S 636 | ds,2,2,S 637 | dv,2,2,V 638 |
dvr,2,2,V 640 | ecl,1,*,S 641 | eos,3,3,V 642 | eq,4,4,V 643 |
eq?,4,4,V 645 | es,1,*,S 646 | exit,0,0,S 647 | flip,1,1,V 648 |
gn,2,2,V 650 | gt,4,4,V 651 | gt?,4,4,V 652 | isc,4,4,SV 653 |
lt,4,4,V 655 | lt?,4,4,V 656 | mu,2,2,V 657 | names,0,1,V 658 |
ndf,3,3,V 660 | norm,1,1,V 661 | ps,1,2,S 662 | psr,1,1,SV 663 |
rrp,1,1,S 665 | rs,0,0,V 666 | sc,2,63,SV 667 | scl,2,2,S 668 |
scn,3,3,SV 670 | sn,2,2,S 671 | ss,2,2,S 672 | su,2,2,V 673 |
tcl,4,4,V 675 | tf,0,0,S 676 | time,0,0,V 677 | tn,0,0,S 678 |
xtime,0,0,V 680 | zlc,1,1,V 681 | zlcp,1,1,V 682 |
683 | 684 |

UNIMPLEMENTED FUNCTIONS

685 | The following functions from the original ttm 686 | are not implemented. 687 | 688 |
rcd,2,2,Sfm,1,*,Stabs,1,8,Sscc,2,2,S 689 |
icc,1,1,Soutb,0,3,Sstore,2,2,Sdelete,1,1,S 690 |
copy,1,1,Sshow,0,1,Slibs,2,2,Sbreak,0,1,S 691 |
692 |

693 | The following functions from the batch ttm 694 | are not implemented. 695 |

696 | 697 |
insw,2,2,S 698 | ttmsw,2,2,S 699 | cd,0,0,V 700 | cdsw,2,2,S 701 |
for,0,0,V 702 | forsw,2,2,S 703 | pk,0,0,V 704 | pksw,2,2,S 705 |
ps,1,1,S 706 | page,1,1,S 707 | sp,1,1,S 708 | fm,0,*,S 709 |
tabs,1,10,S 710 | scc,3,3,S 711 | fmsw,2,2,S 712 | des,1,1,S 713 |
714 | 715 |

BUGS

716 | 717 |
    718 |
  • The windows solution will build and run under Visual Studio C++ 2010, 719 | but the resulting ttm.exe fails when run standalone. 720 |
721 | 722 |

LICENSE

723 | 724 | This software and documents is released under the terms of 725 | the Apache License version 2. For details of the license, see 726 | 727 | http://www.apache.org/licenses/LICENSE-2.0. 728 | 729 | 730 |

VERSION

731 |
    732 |
  • C version 1.0 733 |
  • Java version 1.0 734 |
  • Python version 1.0 735 |
736 | 737 |

ACKNOWLEDGEMENTS

738 | 739 | The original ttm was designed and implemented 740 | by Steven H. Caine and E. Kent Gordon. 741 | (http://www.cfg.com/). 742 | 743 |

REFERENCES

744 |
    745 |
  1. Caine, S.H. and Gordon, E.K., 746 | TTM: An Experimental Interpretive Language. 747 | California Institute of Technology, Willis H. Booth Computing Center, Programming Report No. 7, 1968. 748 |
  2. Caine, S.H. and Gordon, E.K., 749 | TTM: A Macro Language for Batch Processing. 750 | California Institute of Technology, Willis H. Booth Computing Center, Programming Report No. 8 May, 1969. 751 |
  3. Farber, D. J., 635 Assembly System - GAP. Bell Telephone Laboratories Computation Center (1964). 752 |
  4. Strachey, C., A General Purpose Macro Generator. Comput J 8, 3(1965), pp. 225-241.. 753 | 754 |
755 | 756 |

SOURCE CODE

757 | 760 | 761 |

CHANGELOG

762 | 763 |
01 Oct. 2013— 764 | Added python implementation. 765 |
15 Nov. 2012— 766 | Version 1.0 release with C and Java 767 | implementations. 768 |
769 | 770 | 771 | 772 | -------------------------------------------------------------------------------- /ttm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # This software is released under the terms of the Apache 5 | # License version 2. For details of the license, see 6 | # http://www.apache.org/licenses/LICENSE-2.0. 7 | 8 | # TODO: 9 | # - replace class Buffer with simple list 10 | ################################################## 11 | 12 | VERSION = u"1.0" 13 | 14 | DEBUG = False 15 | 16 | ################################################## 17 | # Platform: Windows versus *nix-like 18 | 19 | WINDOWS = False 20 | 21 | ################################################## 22 | # Conditional execution 23 | IMPLEMENTED = False 24 | 25 | ################################################## 26 | 27 | import sys 28 | import getopt 29 | import time 30 | import string 31 | import codecs 32 | import traceback 33 | 34 | ################################################## 35 | # Constants 36 | 37 | EMPTY = u"" 38 | 39 | EOF = "" # for file.read() 40 | 41 | MININT = -2147483647 42 | MAXINT = 2147483647 43 | 44 | # Assign special meaning to some otherwise illegal utf-16 character values 45 | 46 | SEGMARK = 0xE000 47 | CREATE = 0xF000 48 | MARKMASK = 0xF000 49 | 50 | MAXMARKS = 62 51 | MAXARGS = 63 52 | MAXINCLUDES = 1024 53 | MAXEOPTIONS = 1024 54 | MAXINTCHARS = 32 55 | 56 | NUL = (u'\u0000') 57 | EOS = NUL 58 | COMMA = u',' 59 | SEMICOLON = u';' 60 | LPAREN = u'(' 61 | RPAREN = u')' 62 | LBRACKET = u'[' 63 | RBRACKET = u']' 64 | LANGLE = u'<' 65 | RANGLE = u'>' 66 | LBRACE = u'{' 67 | RBRACE = u'}' 68 | SHARP=u'#' 69 | ESCAPE=u'\\' 70 | NL=u'\n' 71 | 72 | DFALTBUFFERSIZE = (1 << 10) 73 | DFALTSTACKSIZE = 64 74 | DFALTEXECCOUNT = (1 << 20) 75 | 76 | CONTEXTLEN = 20 77 | 78 | CREATEFORMAT = "{0:04d}" 79 | SEGFORMAT = "{0:02d}" 80 | 81 | #Mnemonics 82 | NESTED = 1 83 | KEEPESCAPE = 1 84 | TOSTRING = 1 85 | NOTTM = None 86 | TRACING = 1 87 | INCR = 1 88 | DECR = -1 89 | ACTIVE = True 90 | 91 | if DEBUG: 92 | PASSIVEMAX = 20 93 | ACTIVEMAX = 20 94 | SHOWMAX=20 95 | 96 | # TTM Flags 97 | FLAG_EXIT = 1 98 | FLAG_TRACE = 2 99 | FLAG_BARE = 4 100 | FLAG_TESTING = 8 # make stderr = stdout 101 | 102 | ################################################## 103 | # Error Numbers 104 | ENOERR = 0 # No error; for completeness 105 | ENONAME = 1 # Dictionary Name or Character Class Name Not Found 106 | ENOPRIM = 2 # Primitives Not Allowed 107 | EFEWPARMS = 3 # Too Few Parameters Given 108 | EFORMAT = 4 # Incorrect Format 109 | EQUOTIENT = 5 # Quotient Is Too Large 110 | EDECIMAL = 6 # Decimal Integer Required 111 | EMANYDIGITS = 7 # Too Many Digits 112 | EMANYSEGMARKS = 8 # Too Many Segment Marks 113 | EMEMORY = 9 # Dynamic Storage Overflow 114 | EPARMROLL = 10 # Parm Roll Overflow 115 | EINPUTROLL = 11 # Input Roll Overflow 116 | #if IMPLEMENTED : 117 | EDUPLIBNAME = 12 # Name Already On Library 118 | ELIBNAME = 13 # Name Not On Library 119 | ELIBSPACE = 14 # No Space On Library 120 | EINITIALS = 15 # Initials Not Allowed 121 | EATTACH = 16 # Could Not Attach 122 | #endif 123 | EIO = 17 # An I/O Error Occurred 124 | #if IMPLEMENTED : 125 | ETTM = 18 # A TTM Processing Error Occurred 126 | ESTORAGE = 19 # Error In Storage Format 127 | #endif 128 | ENOTNEGATIVE = 20 129 | # Error messages new to this implementation 130 | ESTACKOVERFLOW = 30 # Leave room 131 | ESTACKUNDERFLOW = 31 132 | EBUFFERSIZE = 32 # Buffer overflow 133 | EMANYINCLUDES = 33 # Too many includes (obsolete)# 134 | EINCLUDE = 34 # Cannot read Include file 135 | ERANGE = 35 # index out of legal range 136 | EMANYPARMS = 36 # # parameters > MAXARGS 137 | EEOS = 37 # Unexpected end of string 138 | EASCII = 38 # ASCII characters only 139 | ECHAR8 = 39 # Illegal 8-bit character set value 140 | EUTF32 = 40 # Illegal utf-32 character set 141 | ETTMCMD = 41 # Illegal # command 142 | ETIME = 42 # gettimeofday failed 143 | EEXECCOUNT = 43 # too many execution calls 144 | # Python additions 145 | EARITHMETIC = 44 # arithmetic op failed (e.g. divide by zero) 146 | 147 | # Default case 148 | EOTHER = 99 149 | 150 | code = 0 151 | 152 | UTF8Writer = codecs.getwriter('utf8') 153 | UTF8Reader = codecs.getreader('utf8') 154 | 155 | def ERR(i): 156 | global code 157 | code = i 158 | 159 | ######################### 160 | # Class variables 161 | 162 | eoptions = [] 163 | argoptions = [] 164 | 165 | ################################################## 166 | # Flag functions 167 | 168 | # functions to set/clear/test these marks 169 | def setMark(w, mark): return w | mark 170 | 171 | 172 | def clearMark(w, mark): return w & ~ mark 173 | 174 | 175 | def testMark(w, mark): 176 | if (w & mark) == 0: 177 | return 0 178 | return 1 179 | 180 | def issegmark(c): 181 | return ((ord(c) & MARKMASK) == SEGMARK) 182 | 183 | def iscreate(c): 184 | return ((ord(c) & MARKMASK) == CREATE) 185 | 186 | def iscontrol(c): return (ord(c) < ord(' ') or ord(c) == 127) 187 | 188 | def iswhitespace(c): return (iscontrol(c) or ord(c) == ord(' ')) 189 | 190 | def isdec(c): return (ord(c) >= ord('0') and ord(c) <= ord('9')) 191 | 192 | def ishex(c): return ((ord(c) >= ord('0') and ord(c) <= ord('9')) 193 | or (ord(c) >= ord('a') and ord(c) <= ord('f')) 194 | or (ord(c) >= ord('A') and ord(c) <= ord('F'))) 195 | 196 | ################################################## 197 | # Misc. functions 198 | 199 | def fromhex(c): 200 | ic = ord(c) 201 | if ic >= ord('0') and ic <= ord('9'): 202 | return (ic - ord('0')) 203 | elif ic >= ord('a') and ic <= ord('f'): 204 | return (ic - ord('a')) + 10 205 | elif ic >= ord('A') and ic <= ord('F'): 206 | return (ic - ord('a')) + 10 207 | else: 208 | return -1 209 | 210 | 211 | ######################### 212 | # Utility class declarations 213 | 214 | # Define the limits to prevent 215 | # run-away computations. 216 | 217 | class Limits: 218 | def __init__(self, buffersize, stacksize, execcount): 219 | self.buffersize = buffersize 220 | self.stacksize = stacksize 221 | self.execcount = execcount 222 | 223 | #end class Limits 224 | 225 | # Define a ttm frame 226 | class Frame: 227 | def __init__(self, active=True): 228 | self.args = [] 229 | self.active = active # true => '#<' false => '##<' 230 | self.result = None 231 | 232 | def reset(self): 233 | self.args = [] 234 | 235 | def push(self,arg): 236 | self.args.append(arg) 237 | 238 | def pop(self): 239 | return self.args.pop() 240 | 241 | def top(self): 242 | return self.args[len(self.args)-1] 243 | 244 | def argv(self,i): 245 | if(i < 0 or i >= len(self.args)): 246 | raise Exception("Framestack: illegal ith index") 247 | return self.args[i] 248 | 249 | def argc(self): return len(self.args) 250 | 251 | def __str__(self): 252 | s = SHARP 253 | if(not self.active): s += SHARP 254 | s += LANGLE 255 | if len(self.args) > 0: 256 | s += self.args[0] 257 | for i in range(1,len(self.args)): 258 | s += ";" + self.args[i] 259 | s += ">" 260 | if(self.result != None) : 261 | s += " -> |" 262 | s += str(self.result) 263 | s += '|' 264 | return s 265 | 266 | # class Frame 267 | 268 | 269 | #Name Storage and the Dictionary 270 | # If you add field to this, you need 271 | # to modify especially ttm_ds 272 | class Name: 273 | def __init__(self, ttm, name=None): 274 | self.ttm = ttm 275 | self.name = name 276 | self.trace = False 277 | self.locked = False 278 | self.builtin = False 279 | self.sideeffect = False # side effect only 280 | self.minargs = 0 281 | self.maxargs = 0 282 | self.residual = 0 283 | self.maxsegmark = 0 # highest segment mark number in use in this string 284 | self.fcn = None # builtin == True 285 | self.body = EMPTY # builtin == False 286 | 287 | def copy(self, other): 288 | self.name = self.name 289 | self.trace = other.trace 290 | self.locked = other.locked 291 | self.builtin = other.builtin 292 | self.sideeffect = other.sideeffect 293 | self.minargs = other.minargs 294 | self.maxargs = other.maxargs 295 | self.residual = other.residual 296 | self.maxsegmark = other.maxsegmark 297 | self.fcn = other.fcn 298 | self.body = other.body 299 | # end copy 300 | 301 | def __str__(self): 302 | s = "{" 303 | s += "{0}[{1}:{2}]".format(self.name,self.minargs,self.maxargs) 304 | if self.trace: s += "T" 305 | if self.locked: s += "L" 306 | s += " = " 307 | if not self.builtin and self.body != None: 308 | ps1 = printablestring(self.body[0:self.residual],self.ttm.escapec) 309 | ps2 = printablestring(self.body[self.residual:],self.ttm.escapec) 310 | ps = "|" + ps1 + "^" + ps2 311 | if len(ps) >= SHOWMAX : ps = ps[0:SHOWMAX] + "..." 312 | ps += "|" 313 | s += ps 314 | else: 315 | s += "" 316 | s += "}" 317 | return s 318 | 319 | #end class Name 320 | 321 | 322 | #Character Classes and the Charclass table 323 | 324 | class Charclass: 325 | def __init__(self, name, charset, negative=False): 326 | self.name = name 327 | self.characters = charset 328 | self.negative = negative 329 | 330 | def charclassmatch(self, c): 331 | if self.characters.find(c) < 0: 332 | if self.negative: return True 333 | return False 334 | elif self.negative: 335 | return False 336 | return True 337 | 338 | def __str__(self): 339 | s = LBRACE + str(self.name) 340 | if(self.negative) : s += u"-" 341 | s += u"|" 342 | s += self.characters 343 | s += u"|" 344 | s += RBRACE 345 | return s 346 | 347 | #end class Charclass 348 | 349 | # Define a buffer of string contents 350 | # for holding e.g. the result of a macro call 351 | # or the current active string or the current 352 | # passive string. 353 | # The buffer has a position and allows insertions 354 | # at position. Note that once space is allocated, 355 | # it is never reduced; however the part of the content 356 | # that has data is recorded. Note also that the string 357 | # is kept as a list of characters since Python has no 358 | # mutable string type. 359 | 360 | class StringBuffer: 361 | def __init__(self, alloc=DFALTBUFFERSIZE): 362 | self.used = 0 # defines what of 363 | # the allocated space is actual content. 364 | self.pos = 0 # provide a scanner index 365 | # Pre-allocate the content buffer 366 | self.content = [NUL] * alloc 367 | # end __init__ 368 | 369 | # Ensure buffer has enough space for n new characters 370 | def ensure(self, n): 371 | alloc = len(self.content) 372 | avail = alloc - self.used 373 | if (avail >= n): return # we have enough space 374 | # We need to extend and materialize the extra space 375 | need = (n - avail) 376 | if(need == 0) : need = 1 377 | self.content += [NUL] * need 378 | # end ensure 379 | 380 | # Reset buffer 381 | def reset(self): 382 | self.used = 0 383 | self.pos = 0 384 | #end resetbuffer 385 | 386 | # Open up space at offset to 387 | # make room for n characters 388 | def makeroom(self, offset, n): 389 | self.ensure(n) 390 | # Make space at location offset 391 | # Assumes move will not overwrite 392 | self.content[offset+n:] = self.content[offset:] 393 | # end makeroom 394 | 395 | # Insert (substring of) text at pos; 396 | # move everything above pos up n characters 397 | # Text can be either a string or another 398 | # string buffer. 399 | 400 | def insert(self, src, offset=-1, n=-1): 401 | isstring = isinstance(src, basestring) 402 | start = offset 403 | if offset < 0: start = 0 404 | count = n 405 | if isstring: 406 | size = len(src) 407 | else: 408 | size = src.size() 409 | if (count < 0): 410 | count = (size - start) 411 | # Make space at location pos 412 | self.makeroom(self.pos,count) 413 | # insert 414 | if isstring: 415 | x = unicode(src[start:(start + count)]) # may not be necessary 416 | newdata = list(x) 417 | else: 418 | newdata = src.getcontents(start,start + count) 419 | self.content[self.pos:(self.pos + count)] = newdata 420 | self.used += count 421 | return count 422 | # end insert 423 | 424 | # Overwrite the existing n characters of the buffer 425 | # starting at offset. Do not overwrite buffer text 426 | # beginning at endpos. 427 | # Text can be either a string or another 428 | # string buffer. 429 | 430 | def overwrite(self, src, offset=-1, n=-1, endpos=-1): 431 | isstring = isinstance(src, basestring) 432 | start = offset 433 | if offset < 0: start = 0 434 | count = n 435 | if isstring: 436 | size = len(src) 437 | else: 438 | size = src.size() 439 | if (count < 0): 440 | count = (size - start) 441 | if endpos < 0 or endpos > size : 442 | endpos = size 443 | # See if we have room for n characters 444 | # between start and endpos 445 | avail = endpos - start 446 | if avail < n: # no, insert space before endpos 447 | need = (n - avail) 448 | # Make space at location endpos 449 | self.makeroom(endpos,need) 450 | # fixup 451 | if self.pos > endpos: 452 | self.pos += need 453 | self.used += need 454 | # insert 455 | if isstring: 456 | x = unicode(src[start:(start + count)]) # may not be necessary 457 | newdata = list(x) 458 | else: 459 | newdata = src.getcontents(start,endpos) 460 | self.content[start:endpos] = newdata 461 | return count 462 | # end insert 463 | 464 | # append substring of text to the end 465 | def append(self, src, offset=-1, n=-1): 466 | savepos = self.pos 467 | self.pos = self.used 468 | self.insert(src,offset,n) 469 | # end append 470 | 471 | # prepend text to the front; modifies pos 472 | def prepend(self, src, offset=-1, n=-1): 473 | savepos = self.pos 474 | self.pos = 0 475 | count = self.insert(src,offset,n) 476 | self.pos = savepos + count 477 | #end prepend 478 | 479 | def size(self): 480 | return self.used 481 | 482 | # Return result as a (unicode) string 483 | def getcontents(self, start=0, end=-1): 484 | stop = end 485 | if (stop < 0): stop = self.used 486 | return EMPTY.join(self.content[start:stop]) 487 | # end getcontents 488 | 489 | # Provide single character actions 490 | 491 | def next(self): 492 | if(self.pos >= self.used) : return EOS; 493 | c = self.content[self.pos] 494 | self.pos += 1 495 | return c 496 | # end next 497 | 498 | def peek(self, i=0): 499 | if((self.pos + i)>= self.used) : return EOS; 500 | c = self.content[self.pos + i] 501 | return c 502 | # end peek 503 | 504 | # Append character at end 505 | def put(self, c): 506 | if (self.used >= len(self.content)): 507 | self.ensure(DFALTBUFFERSIZE + len(self.content)) 508 | self.content[self.used] = unicode(c) # Probably not necessary 509 | self.used += 1 510 | # end put 511 | 512 | def skip(self, n=1): 513 | self.pos += n 514 | if self.pos >= self.used: self.pos = self.used 515 | # end skip 516 | 517 | def __str__(self): 518 | if self.used == 0: 519 | return '""' 520 | return ('"' + self.getcontents(0, self.pos) + "|" 521 | + self.getcontents(self.pos,self.used) + '"') 522 | # end __str__ 523 | 524 | #end class StringBuffer 525 | 526 | ################################################## 527 | # TTM Class 528 | 529 | # For python, this class only contains the interpreter state 530 | class TTM: 531 | def __init__(self, buffersize=DFALTBUFFERSIZE, stacksize=DFALTSTACKSIZE, execcount=DFALTEXECCOUNT): 532 | self.limits = Limits(buffersize, stacksize, execcount) 533 | self.sharpc = SHARP 534 | self.openc = LANGLE 535 | self.closec = RANGLE 536 | self.semic = SEMICOLON 537 | self.escapec = ESCAPE 538 | self.metac = NL 539 | 540 | self.stack = [] 541 | 542 | self.stdinclose = False 543 | self.stdout = sys.stdout 544 | self.stdoutclose = False 545 | self.stdin = sys.stdin 546 | self.stderrclose = False 547 | self.stderr = sys.stderr 548 | 549 | # Following 2 fields are dictionaries 550 | self.dictionary = {} 551 | self.charclasses = {} 552 | 553 | self.flags = 0 554 | if DEBUG: 555 | self.flags |= FLAG_TRACE 556 | #endif 557 | 558 | # record the current processor time 559 | self.xtimebase = 0.0 560 | if WINDOWS: 561 | self.xtimebase = time.clock() # floating point number 562 | 563 | self.exitcode = 0 564 | self.crcounter = 0 565 | 566 | argv = None # after options have been removed 567 | 568 | self.active = StringBuffer(DFALTBUFFERSIZE) 569 | 570 | # end __init__ 571 | 572 | # This is basic top level scanner. 573 | def scan(self, ba): 574 | bp = StringBuffer() 575 | while True: 576 | c = ba.peek() # NOTE that we do not bump here 577 | if (c == NUL): # End of active buffer 578 | break 579 | elif (c == self.escapec): 580 | ba.skip() # skip the escape 581 | bp.put(ba.next()) 582 | elif (c == self.sharpc): # Start of call? 583 | c1 = ba.peek(1) 584 | if (c1 == self.openc 585 | or (c1 == self.sharpc 586 | and ba.peek(2) == self.openc)): 587 | # It is a real call 588 | self.execute(ba, bp) 589 | if ((self.flags & FLAG_EXIT) != 0): 590 | return 591 | else: # not a call; just pass the '#' along passively 592 | bp.put(c) 593 | ba.skip() 594 | elif (c == self.openc): # Start of <...> escaping 595 | # skip the leading lbracket 596 | depth = 1 597 | ba.skip() 598 | while True: 599 | c = ba.get() 600 | if (c == NUL): self.fail(EEOS) # Unexpected EOF 601 | bp.put(c) 602 | ba.skip() 603 | if (c == self.escapec): 604 | bp.put(ba.next()) 605 | elif (c == self.openc): 606 | depth += 1 607 | elif (c == self.closec): 608 | depth -= 1 609 | if (depth == 0): 610 | break # we are done 611 | else: # keep moving 612 | pass 613 | # end while 614 | else: # non-signficant character 615 | bp.put(c) 616 | ba.skip() 617 | # endwhile 618 | return bp.getcontents() 619 | #end scan 620 | 621 | def execute(self, ba, bp): 622 | self.limits.execcount -= 1 623 | if (self.limits.execcount <= 0): 624 | self.fail( EEXECCOUNT) 625 | frame = self.pushframe() 626 | # Skip to the start of the function name 627 | if (ba.peek(1) == self.openc): 628 | ba.skip(2) 629 | frame.active = True 630 | else: 631 | ba.skip(3) 632 | frame.active = False 633 | # Parse and store relevant pointers into frame. 634 | self.parsecall(frame, ba) 635 | if ((self.flags & FLAG_EXIT) != 0): 636 | self.popframe() 637 | return 638 | # Now execute this function, which will leave result in result 639 | if (frame.argc() == 0): self.fail( ENONAME) 640 | if (frame.argv(0) == None): self.fail( ENONAME) 641 | if (len(frame.argv(0)) == 0): self.fail( ENONAME) 642 | # Locate the function to execute 643 | if frame.argv(0) in self.dictionary: 644 | fcn = self.dictionary[frame.argv(0)] 645 | else: 646 | self.fail(ENONAME) 647 | if (fcn.minargs > (frame.argc() - 1)): # -1 to account for function name 648 | self.fail(EFEWPARMS) 649 | if (not fcn.sideeffect): 650 | frame.result = StringBuffer() 651 | if ((self.flags & FLAG_TRACE) != 0 or fcn.trace): 652 | trace(self, True, TRACING) 653 | if (fcn.builtin): 654 | fcn.fcn(self, frame, frame.result) 655 | else: # invoke the pseudo function "call" 656 | self.call(frame, fcn.body) 657 | 658 | if ((self.flags & FLAG_EXIT) != 0): 659 | self.popframe() 660 | return 661 | 662 | if DEBUG: 663 | self.stderr.write("result: ") 664 | if frame.result == None: 665 | self.stderr.write("None") 666 | else: 667 | dbgprint(self,frame.result.getcontents(), '|') 668 | self.stderr.write('\n') 669 | #endif 670 | 671 | if ((self.flags & FLAG_TRACE) != 0 or fcn.trace): 672 | trace(self, False, TRACING) 673 | 674 | # Now, put the result into the active/passive buffer 675 | if frame.result != None: 676 | if (frame.active): 677 | ba.insert(frame.result) 678 | else : #frame.passive 679 | bp.append(frame.result) 680 | if DEBUG: 681 | self.stderr.write("context:\n\tpassive=") 682 | dbgprint(self.stderr,bp.content, '|') 683 | self.stderr.write("...|\n") 684 | self.stderr.write("\tactive=|") 685 | dbgprint(self.stderr,ba.content, '|') 686 | self.stderr.write("...|\n") 687 | #endif 688 | 689 | #exiting: 690 | self.popframe() 691 | #end exec 692 | 693 | #Construct a frame; leave ba.pos pointing just past the call. 694 | def parsecall(self, frame, ba): 695 | tmp = StringBuffer() 696 | done = False 697 | while True: 698 | # collect the the ith arg 699 | tmp.reset() 700 | while not done: 701 | c = ba.peek() # Note that we do not bump here 702 | if (c == EOS): self.fail(EEOS) # Unexpected end of buffer 703 | if (c == self.escapec): 704 | ba.skip() 705 | tmp.next(ba.next()) 706 | elif (c == self.semic or c == self.closec): 707 | # End of an argument; capture 708 | if DEBUG: 709 | self.stderr.write("parsecall: argv[{0}]=".format(frame.argc())) 710 | dbgprint(self.stderr,tmp, '|') 711 | self.stderr.write("\n") 712 | #endif 713 | ba.skip() # skip the semi or close 714 | # store arg in frame 715 | frame.push(tmp.getcontents()) 716 | if (c == self.closec): 717 | done = True 718 | break 719 | elif (frame.argc() >= MAXARGS): 720 | self.fail(EMANYPARMS) 721 | tmp.reset() 722 | elif (c == self.sharpc): 723 | # check for call within call 724 | if (ba.peek(1) == self.openc 725 | or (ba.peek(1) == self.sharpc 726 | and ba.peek(2) == self.openc)): 727 | # Recurse to compute inner call 728 | self.execute(ba, tmp) 729 | if ((self.flags & FLAG_EXIT) != 0): return 730 | elif (c == self.openc): # <...> nested brackets 731 | ba.skip() # skip leading lbracket 732 | depth = 1 733 | while True: 734 | c = ba.peek() 735 | if (c == NUL): self.fail( EEOS) # Unexpected EOF 736 | elif (c == self.escapec): 737 | tmp.put(c) 738 | tmp.put(ba.next()) 739 | elif (c == self.openc): 740 | tmp.put(c) 741 | ba.skip() 742 | depth += 1 743 | elif (c == self.closec): 744 | depth -= 1 745 | ba.skip() 746 | if (depth == 0): break # we are done 747 | tmp.put(c) 748 | else: 749 | tmp.put(ba.next()) 750 | # loop for <...> 751 | else: # keep moving 752 | tmp.put(c) 753 | ba.skip() 754 | # collect argument for 755 | if(done) : break # while !done 756 | # end while(!done) 757 | 758 | # end parsecall 759 | 760 | ################################################## 761 | #Execute a non-builtin function 762 | 763 | def call(self, frame, body): 764 | # Expand the body 765 | assert (frame.result != None) 766 | br = frame.result 767 | if len(body) == 0: 768 | br.reset() 769 | return 770 | crvalue = None # use same cr number for all substitutions 771 | for c in body: 772 | if issegmark(c): 773 | segindex = ord(c) & 0xFF 774 | if (segindex < frame.argc()): 775 | arg = frame.argv(segindex) 776 | br.append(arg) 777 | elif iscreate(c) : 778 | # Construct the cr value 779 | if crvalue == None: 780 | self.crcounter += 1 781 | crvalue = self.crcounter 782 | crval = CREATEFORMAT.format(crvalue) 783 | size = len(crval) 784 | br.append(crval, 0, size) 785 | else: 786 | br.put(c) 787 | # end while 788 | 789 | # end call 790 | 791 | ################################################## 792 | # Built-in Support Procedures 793 | 794 | # Manage the frame stack 795 | def pushframe(self): 796 | if (len(self.stack) >= self.limits.stacksize): 797 | self.fail(ESTACKOVERFLOW) 798 | frame = Frame(not ACTIVE) 799 | self.stack.append(frame) 800 | return frame 801 | 802 | def popframe(self): 803 | if (len(self.stack) == 0): 804 | self.fail(ESTACKUNDERFLOW) 805 | frame = self.stack.pop() 806 | return frame 807 | 808 | ################################################## 809 | # Error reporting 810 | def fail(self, eno): 811 | self.fatal("({0}) {1}".format(eno, errorstring(eno))) 812 | #end fail 813 | 814 | def fatal(self, msg): 815 | self.stderr.write("Fatal error: " + msg + "\n") 816 | # Dump the frame stack 817 | dumpstack(self) 818 | #raise Exception("Fatal exception") 819 | sys.exit(1) 820 | # end class TTM 821 | 822 | ################################################## 823 | # Builtin functions 824 | # Note that these are outside the TTM class 825 | ################################################## 826 | 827 | # Original TTM functions 828 | 829 | # Dictionary Operations 830 | def ttm_ap(ttm, frame, br): # Append to a string 831 | # Note: br is the result buffer 832 | arg = frame.argv(1) 833 | if (arg in ttm.dictionary): 834 | entry = ttm.dictionary[arg] 835 | else: # Define the string 836 | ttm_ds(ttm,frame, br) 837 | return 838 | if (entry.builtin): ttm.fail(ENOPRIM) 839 | apstring = frame.argv(2) 840 | aplen = len(apstring) 841 | body = entry.body 842 | bodylen = len(body) 843 | newbody = body + apstring 844 | entry.body = newbody 845 | entry.residual = bodylen + aplen 846 | # end ttm_ap 847 | 848 | # The semantics of # 849 | # have been changed. See ttm.html 850 | 851 | def ttm_cf(ttm, frame, br): # Copy a function 852 | newname = frame.argv(1) 853 | oldname = frame.argv(2) 854 | if oldname in ttm.dictionary: 855 | oldentry = ttm.dictionary[oldname] 856 | else: 857 | ttm.fail(ENONAME) 858 | if newname in ttm.dictionary: 859 | newentry = ttm.dictionary[newname] 860 | else: 861 | # create a new string object 862 | newentry = Name(ttm, newname) 863 | ttm.dictionary[newname] = newentry 864 | newentry.copy(oldentry) 865 | pass 866 | #end ttm_cf 867 | 868 | def ttm_cr(ttm, frame, br): # Mark for creation 869 | arg = frame.argv(1) 870 | if (arg in ttm.dictionary): 871 | entry = ttm.dictionary[arg] 872 | else: 873 | ttm.fail(ENONAME) 874 | if (entry.builtin): 875 | ttm.fail(ENOPRIM) 876 | 877 | body = entry.body 878 | bodylen = len(body) 879 | crstring = frame.argv(2) 880 | crlen = len(crstring) 881 | if (crlen > 0): # search only if possible success 882 | # Search for all occurrences of arg; replace same mark 883 | offset = entry.residual 884 | newbody = EMPTY 885 | while True: 886 | pos = string.find(body, crstring, offset) 887 | if (pos < 0): 888 | newbody += body[offset:] 889 | entry.body = newbody 890 | break 891 | else: 892 | newbody += body[offset:pos] + unichr(CREATE) 893 | offset = pos + crlen 894 | # end ttm_cr 895 | # end ttm_cr 896 | 897 | def ttm_ds(ttm, frame, br): # Define string 898 | arg = frame.argv(1) 899 | if (arg in ttm.dictionary): 900 | entry = ttm.dictionary[arg] 901 | else: 902 | # create a new string object 903 | entry = Name(ttm, arg) 904 | ttm.dictionary[arg] = entry 905 | # reset as needed 906 | entry.builtin = False 907 | entry.minargs = 0 908 | entry.maxargs = 0 909 | entry.residual = 0 910 | entry.maxsegmark = 0 911 | entry.fcn = None 912 | entry.body = frame.argv(2) 913 | # end ttm_ds 914 | 915 | def ttm_es(ttm, frame, br): # Erase string 916 | for i in range(1, frame.argc()): 917 | strname = frame.argv(i) 918 | if (strname in ttm.dictionary): 919 | entry = ttm.dictionary[strname] 920 | if not entry.locked : 921 | del ttm.dictionary[strname] 922 | else: 923 | pass 924 | #end ttm_es 925 | 926 | # Helper function for # and # 927 | def ttm_ss0(ttm, frame, br): 928 | arg = frame.argv(1) 929 | if (arg in ttm.dictionary): 930 | entry = ttm.dictionary[arg] 931 | else: 932 | ttm.fail(ENONAME) 933 | if (entry.builtin): 934 | ttm.fail(ENOPRIM) 935 | body = entry.body 936 | # pull off the residual prefix 937 | residual = body[0:entry.residual] 938 | body = body[entry.residual:] 939 | bodylen = len(body) 940 | segcount = 0 941 | startseg = entry.maxsegmark 942 | for i in range(2, frame.argc()): 943 | sstring = frame.argv(i) 944 | sslen = len(sstring) 945 | newbody = EMPTY 946 | # Search for occurrences of arg 947 | offset = 0 948 | newbody = EMPTY 949 | found = False 950 | while True: 951 | pos = string.find(body, sstring, offset) 952 | if (pos < 0): 953 | newbody += body[offset:] 954 | body = newbody 955 | break 956 | else: 957 | if (not found): # first match 958 | startseg += 1 959 | found = True 960 | newbody += body[offset:pos] 961 | newbody += unichr(int(SEGMARK) | startseg) 962 | segcount += 1 963 | offset = pos + sslen 964 | entry.maxsegmark = startseg 965 | entry.body = newbody 966 | return segcount 967 | 968 | # ttm_ss0 969 | 970 | def ttm_sc(ttm, frame, br): # Segment and count 971 | nsegs = ttm_ss0(ttm, frame, br) 972 | br.append(str(nsegs)) 973 | 974 | # end ttm_sc 975 | 976 | def ttm_ss(ttm, frame, br): # Segment 977 | ttm_ss0(ttm, frame, br) 978 | 979 | # end ttm_ss 980 | 981 | # Name Selection 982 | def ttm_cc(ttm, frame, br): # Call one character 983 | arg = frame.argv(1) 984 | if (arg in ttm.dictionary): 985 | entry = ttm.dictionary[arg] 986 | else: 987 | ttm.fail(ENONAME) 988 | if (entry.builtin): 989 | ttm.fail(ENOPRIM) 990 | # Check for pointing at trailing NUL 991 | if (entry.residual < len(entry.body)): 992 | c = entry.body[entry.residual] 993 | br.append(c) 994 | entry.residual += 1 995 | 996 | # end ttm_cc 997 | 998 | def ttm_cn(ttm, frame, br): # Call n characters 999 | arg2 = frame.argv(2) 1000 | if arg2 in ttm.dictionary: 1001 | entry = ttm.dictionary[arg2] 1002 | else: 1003 | ttm.fail(ENONAME) 1004 | if (entry.builtin): 1005 | ttm.fail(ENOPRIM) 1006 | 1007 | # Get number of characters to extract 1008 | try: 1009 | n = int(frame.argv(1)) 1010 | except ValueError: 1011 | ttm.fail(EDECIMAL) 1012 | if (n < 0): ttm.fail(ENOTNEGATIVE) 1013 | 1014 | # See if we have enough space 1015 | bodylen = len(entry.body) 1016 | if (entry.residual >= bodylen): 1017 | avail = 0 1018 | else: 1019 | avail = (bodylen - entry.residual) 1020 | 1021 | if (n == 0 or avail == 0): 1022 | return 1023 | 1024 | if (avail < n): n = avail # return what is available 1025 | 1026 | # We want n characters starting at residual 1027 | # ok, copy n characters from startn into the return buffer 1028 | br.append(entry.body, entry.residual, n) 1029 | # increment residual 1030 | entry.residual += n 1031 | 1032 | # end ttm_cn 1033 | 1034 | def ttm_cp(ttm, frame, br): # Call parameter 1035 | arg = frame.argv(1) 1036 | if arg in ttm.dictionary: 1037 | entry = ttm.dictionary[arg] 1038 | else: 1039 | ttm.fail(ENONAME) 1040 | if (entry.builtin): 1041 | ttm.fail(ENOPRIM) 1042 | 1043 | depth = 0 1044 | p = entry.residual 1045 | while p < len(entry.body): 1046 | c = entry.body[p] 1047 | if (c == ttm.semic): 1048 | if (depth == 0): break # reached unnested semicolon# 1049 | elif (c == ttm.openc): 1050 | depth += 1 1051 | elif (c == ttm.closec): 1052 | depth -= 1 1053 | p += 1 1054 | #end while 1055 | delta = (p - entry.residual) 1056 | br.append(entry.body, entry.residual, delta) 1057 | entry.residual += delta 1058 | if p < len(entry.body): 1059 | entry.residual += 1 1060 | #end ttm_cp 1061 | 1062 | def ttm_cs(ttm, frame, br): # Call segment 1063 | arg = frame.argv(1) 1064 | if arg in ttm.dictionary: 1065 | entry = ttm.dictionary[arg] 1066 | else: 1067 | ttm.fail(ENONAME) 1068 | if (entry.builtin): 1069 | ttm.fail(ENOPRIM) 1070 | 1071 | # Locate the next segment mark 1072 | # Unclear if create marks also qualify; assume yes 1073 | p = entry.residual 1074 | while p < len(entry.body): 1075 | c = entry.body[p] 1076 | if issegmark(c) or iscreate(c): break 1077 | p += 1 1078 | delta = (p - entry.residual) 1079 | if (delta > 0): 1080 | br.append(entry.body, entry.residual, delta) 1081 | # set residual pointer correctly 1082 | entry.residual += delta 1083 | if p < len(entry.body): entry.residual += 1 1084 | #end ttm_cs 1085 | 1086 | def ttm_isc(ttm, frame, br): # Initial character scan; 1087 | # moves residual pointer 1088 | arg2 = frame.argv(2) 1089 | if arg2 in ttm.dictionary: 1090 | entry = ttm.dictionary[arg2] 1091 | else: 1092 | ttm.fail(ENONAME) 1093 | if (entry.builtin): 1094 | ttm.fail(ENOPRIM) 1095 | 1096 | arg = frame.argv(1) 1097 | arglen = len(arg) 1098 | t = frame.argv(3) 1099 | f = frame.argv(4) 1100 | 1101 | # check for initial string match 1102 | if (entry.body.startswith(arg, entry.residual)): 1103 | result = t 1104 | entry.residual += arglen 1105 | slen = len(entry.body) 1106 | if (entry.residual > slen): entry.residual = slen 1107 | else: 1108 | result = f 1109 | br.append(result, 0, len(result)) 1110 | 1111 | #end ttm_isc 1112 | 1113 | def ttm_rrp(ttm, frame, br): # Reset residual pointer 1114 | arg = frame.argv(1) 1115 | if arg in ttm.dictionary: 1116 | entry = ttm.dictionary[arg] 1117 | else: 1118 | ttm.fail(ENONAME) 1119 | if (entry.builtin): 1120 | ttm.fail(ENOPRIM) 1121 | entry.residual = 0 1122 | 1123 | #end ttm_rrp 1124 | 1125 | def ttm_scn(ttm, frame, br): # Character scan 1126 | arg2 = frame.argv(2) 1127 | if arg2 in ttm.dictionary: 1128 | entry = ttm.dictionary[arg2] 1129 | else: 1130 | ttm.fail(ENONAME) 1131 | if (entry.builtin): 1132 | ttm.fail(ENOPRIM) 1133 | 1134 | arg = frame.argv(1) 1135 | f = frame.argv(3) 1136 | 1137 | # check for sub string match 1138 | p = entry.body.find(arg,entry.residual) 1139 | if p < 0: # no match; return argv[3] 1140 | br.append(f) 1141 | elif p > entry.residual: # return from residual ptr to location of string 1142 | delta = (p - entry.residual) 1143 | br.append(entry.body, entry.residual, delta) 1144 | else: # if the match is at the residual ptr, mv ptr 1145 | entry.residual += len(arg) 1146 | bodylen = len(entry.body) 1147 | if (entry.residual > bodylen): entry.residual = bodylen 1148 | #end ttm_scn 1149 | 1150 | def ttm_sn(ttm, frame, br): # Skip n characters 1151 | arg2 = frame.argv(2) 1152 | if arg2 in ttm.dictionary: 1153 | entry = ttm.dictionary[arg2] 1154 | else: 1155 | ttm.fail(ENONAME) 1156 | if (entry.builtin): 1157 | ttm.fail(ENOPRIM) 1158 | 1159 | try: 1160 | num = int(frame.argv(1)) 1161 | except ValueError: 1162 | ttm.fail(EDECIMAL) 1163 | if (num < 0): ttm.fail(ENOTNEGATIVE) 1164 | 1165 | entry.residual += num 1166 | bodylen = len(entry.body) 1167 | if (entry.residual > bodylen): 1168 | entry.residual = bodylen 1169 | 1170 | #end ttm_sn 1171 | 1172 | def ttm_eos(ttm, frame, br): # Test for end of string 1173 | arg = frame.argv(1) 1174 | if arg in ttm.dictionary: 1175 | entry = ttm.dictionary[arg] 1176 | else: 1177 | ttm.fail(ENONAME) 1178 | if (entry.builtin): 1179 | ttm.fail(ENOPRIM) 1180 | bodylen = len(entry.body) 1181 | t = frame.argv(2) 1182 | f = frame.argv(3) 1183 | if (entry.residual >= bodylen): 1184 | result = t 1185 | else: 1186 | result = f 1187 | br.append(result, 0, len(result)) 1188 | 1189 | #end ttm_eos 1190 | 1191 | # Name Scanning Operations 1192 | 1193 | def ttm_gn(ttm, frame, br): # Give n characters from argument string# 1194 | snum = frame.argv(1) 1195 | s = frame.argv(2) 1196 | slen = len(s) 1197 | 1198 | try: 1199 | num = int(snum) 1200 | except ValueError: 1201 | ttm.fail(EDECIMAL) 1202 | 1203 | if (num > 0): 1204 | if (slen < num): num = slen 1205 | startp = 0 1206 | elif (num < 0): 1207 | num = -num 1208 | startp = num 1209 | num = (slen - num) 1210 | if (num != 0): 1211 | br.append(s, startp, num) 1212 | 1213 | #end ttm_gn 1214 | 1215 | def ttm_zlc(ttm, frame, br): # Zero-level commas 1216 | s = frame.argv(1) + EOS # avoid having to test against len 1217 | slen = len(s) 1218 | depth = 0 1219 | p = 0 1220 | while True: 1221 | c = s[p] 1222 | if c == EOS: break 1223 | if c == ttm.escapec: 1224 | br.put(c) 1225 | p += 1 1226 | br.put(s[p]) 1227 | elif (c == COMMA and depth == 0): 1228 | br.put(ttm.semic); 1229 | elif (c == LPAREN): 1230 | depth += 1 1231 | br.put(c) 1232 | elif (c == RPAREN): 1233 | depth -= 1 1234 | br.put(c) 1235 | else: 1236 | br.put(c) 1237 | p += 1 1238 | # end while 1239 | #end ttm_zlc 1240 | 1241 | def ttm_zlcp(ttm, frame, br): # Zero-level commas and parentheses 1242 | # exact algorithm is unknown 1243 | # A(B) and A,B will both give A;B and (A),(B),C will give A;B;C 1244 | s = frame.argv(1) + EOS # Add EOS to avoid having to test p all the time 1245 | slen = len(s) 1246 | p = 0 1247 | depth = 0 1248 | while True: 1249 | c = s[p] 1250 | if c == EOS: break 1251 | if (c == ttm.escapec): 1252 | br.put(c) 1253 | p += 1 1254 | br.put(s[p]) 1255 | elif (depth == 0 and c == COMMA): 1256 | if (s[p+1] != LPAREN): 1257 | br.put(ttm.semic) 1258 | elif (c == LPAREN): 1259 | if (depth == 0 and p > 0): 1260 | br.put(ttm.semic) 1261 | if (depth > 0): 1262 | br.put(c) 1263 | depth += 1 1264 | elif (c == RPAREN): 1265 | depth -= 1 1266 | if (depth == 0 and s[p+1] == COMMA): 1267 | pass 1268 | elif (depth == 0 and s[p + 1] == EOS): # do nothing 1269 | pass 1270 | elif (depth == 0): 1271 | br.put(ttm.semic) 1272 | else: # depth > 0 1273 | br.put(c) 1274 | else: 1275 | br.put(c) 1276 | p += 1 1277 | # end while 1278 | #end ttm_zlcp 1279 | 1280 | def ttm_flip(ttm, frame, br): # Flip a string 1281 | s = frame.argv(1) 1282 | for p in range(len(s), 0, -1): 1283 | br.put(s[p-1]) 1284 | #end ttm_flip 1285 | 1286 | def ttm_ccl(ttm, frame, br): # Call class 1287 | arg = frame.argv(1) 1288 | arg2 = frame.argv(2) 1289 | 1290 | if arg in ttm.charclasses: 1291 | cl = ttm.charclasses[arg] 1292 | else: 1293 | ttm.fail(ENONAME) 1294 | if arg2 in ttm.dictionary: 1295 | entry = ttm.dictionary[arg2] 1296 | else: 1297 | ttm.fail(ENONAME) 1298 | 1299 | if (entry.builtin): 1300 | ttm.fail(ENOPRIM) 1301 | 1302 | # Starting at entry.residual, locate first char not in class 1303 | p = entry.residual 1304 | while p < len(entry.body): 1305 | c = entry.body[p] 1306 | if (cl.negative and cl.characters.find(c) >= 0): break; 1307 | if (not cl.negative and cl.characters.find(c) < 0): break; 1308 | p += 1 1309 | count = (p - entry.residual) 1310 | if (count > 0): 1311 | br.append(entry.body, entry.residual, count) 1312 | entry.residual += count 1313 | # end ttm_ccl 1314 | 1315 | # Shared helper for dcl and dncl 1316 | def ttm_dcl0(ttm, frame, negative): 1317 | arg = frame.argv(1) 1318 | characters = frame.argv(2) 1319 | if (arg in ttm.charclasses): 1320 | cl = ttm.charclasses[arg] 1321 | cl.negative = negative 1322 | cl.characters = characters 1323 | else: 1324 | # create a new charclass object 1325 | cl = Charclass(arg, characters, negative) 1326 | ttm.charclasses[arg] = cl 1327 | 1328 | # end ttm_dcl0 1329 | 1330 | def ttm_dcl(ttm, frame, br): # Define a negative class 1331 | ttm_dcl0(ttm, frame, False) 1332 | 1333 | #end ttm_dcl 1334 | 1335 | def ttm_dncl(ttm, frame, br): # Define a negative class 1336 | ttm_dcl0(ttm, frame, True) 1337 | 1338 | # end ttm_dncl 1339 | 1340 | def ttm_ecl(ttm, frame, br): # Erase a class 1341 | for i in range(1, frame.argc()): 1342 | clname = frame.argv(i) 1343 | if clname in ttm.charclasses: 1344 | del ttm.charclasses[clname] 1345 | # end ttm_ecl 1346 | 1347 | def ttm_scl(ttm, frame, br): # Skip class 1348 | arg = frame.argv(1) 1349 | arg2 = frame.argv(2) 1350 | 1351 | if arg in ttm.charclasses: 1352 | cl = ttm.charclasses[arg] 1353 | else: 1354 | ttm.fail(ENONAME) 1355 | if arg2 in ttm.dictionary: 1356 | entry = ttm.dictionary[arg2] 1357 | else: 1358 | ttm.fail(ENONAME) 1359 | if (entry.builtin): 1360 | ttm.fail(ENOPRIM) 1361 | 1362 | # Starting at entry.residual, locate first char not in class 1363 | p = entry.residual 1364 | while p < len(entry.body): 1365 | c = entry.body[p] 1366 | if (cl.negative and cl.characters.find(c) >= 0): break; 1367 | elif (not cl.negative and cl.characters.find(c) < 0): break; 1368 | p += 1 1369 | count = (p - entry.residual) 1370 | entry.residual += count 1371 | # end ttm_scl 1372 | 1373 | def ttm_tcl(ttm, frame, br): # Test class 1374 | arg = frame.argv(1) 1375 | arg2 = frame.argv(2) 1376 | t = frame.argv(3) 1377 | f = frame.argv(4) 1378 | 1379 | if arg in ttm.charclasses: 1380 | cl = ttm.charclasses[arg] 1381 | else: 1382 | ttm.fail(ENONAME) 1383 | if arg2 in ttm.dictionary: 1384 | entry = ttm.dictionary[arg2] 1385 | if (entry.builtin): 1386 | ttm.fail(ENOPRIM) 1387 | else: 1388 | entry = None 1389 | 1390 | if str != None: 1391 | # see if char at entry.residual is in class 1392 | if entry.residual < len(entry.body): 1393 | c = entry.body[entry.residual] 1394 | if (cl.negative and cl.characters.find(c) < 0): 1395 | retval = t 1396 | elif (not cl.negative and cl.characters.find(c) >= 0): 1397 | retval = t 1398 | else: 1399 | retval = f 1400 | else: 1401 | retval = f 1402 | else: 1403 | retval = f 1404 | retlen = len(retval) 1405 | if (retlen > 0): 1406 | br.append(retval, 0, retlen) 1407 | #end ttm_tcl 1408 | 1409 | # Arithmetic Operators 1410 | 1411 | def ttm_abs(ttm, frame, br): # Obtain absolute value 1412 | slhs = frame.argv(1) 1413 | try: 1414 | lhs = int(slhs) 1415 | except ValueError: 1416 | ttm.fail(EDECIMAL) 1417 | if (lhs < 0): lhs = -lhs 1418 | br.append(str(lhs)) 1419 | 1420 | # end ttm_abs 1421 | 1422 | def ttm_ad(ttm, frame, br): # Add 1423 | total = 0 1424 | try: 1425 | for i in range(1, frame.argc()): 1426 | snum = frame.argv(i) 1427 | num = int(snum) 1428 | total += num 1429 | br.append(str(total)) 1430 | except ValueError: 1431 | ttm.fail(EDECIMAL) 1432 | # end ttm_ad 1433 | 1434 | def ttm_dv(ttm, frame, br): # Divide and give quotient 1435 | slhs = frame.argv(1) 1436 | srhs = frame.argv(2) 1437 | try: 1438 | lhs = int(slhs) 1439 | rhs = int(srhs) 1440 | try: 1441 | lhs = (lhs / rhs) 1442 | except ZeroDivisionError: 1443 | ttm.fail(EARITHMETIC) 1444 | br.append(str(lhs)) 1445 | except ValueError: 1446 | ttm.fail(EDECIMAL) 1447 | # end ttm_dv 1448 | 1449 | def ttm_dvr(ttm, frame, br): # Divide and give remainder 1450 | slhs = frame.argv(1) 1451 | srhs = frame.argv(2) 1452 | try: 1453 | lhs = int(slhs) 1454 | rhs = int(srhs) 1455 | lhs = (lhs % rhs) 1456 | br.append(str(lhs)) 1457 | except ValueError: 1458 | ttm.fail(EDECIMAL) 1459 | # end ttm_dvr 1460 | 1461 | def ttm_mu(ttm, frame, br): # Multiply 1462 | total = 1 1463 | try: 1464 | for i in range(1, frame.argc()): 1465 | snum = frame.argv(i) 1466 | num = int(snum) 1467 | total *= num 1468 | br.append(str(total)) 1469 | except ValueError: 1470 | ttm.fail(EDECIMAL) 1471 | 1472 | # end ttm_mu 1473 | 1474 | def ttm_su(ttm, frame, br): # Substract 1475 | slhs = frame.argv(1) 1476 | srhs = frame.argv(2) 1477 | try: 1478 | lhs = int(slhs) 1479 | rhs = int(srhs) 1480 | lhs = (lhs - rhs) 1481 | br.append(str(lhs)) 1482 | except ValueError: 1483 | ttm.fail(EDECIMAL) 1484 | # end ttm_su 1485 | 1486 | def ttm_eq(ttm, frame, br): # Compare numeric equal 1487 | slhs = frame.argv(1) 1488 | srhs = frame.argv(2) 1489 | t = frame.argv(3) 1490 | f = frame.argv(4) 1491 | try: 1492 | lhs = int(slhs) 1493 | rhs = int(srhs) 1494 | if (lhs == rhs): 1495 | br.append(t) 1496 | else: 1497 | br.append(f) 1498 | except ValueError: 1499 | ttm.fail(EDECIMAL) 1500 | # end ttm_eq 1501 | 1502 | def ttm_gt(ttm, frame, br): # Compare numeric greater-than 1503 | slhs = frame.argv(1) 1504 | srhs = frame.argv(2) 1505 | t = frame.argv(3) 1506 | f = frame.argv(4) 1507 | try: 1508 | lhs = int(slhs) 1509 | rhs = int(srhs) 1510 | if (lhs > rhs): 1511 | br.append(t) 1512 | else: 1513 | br.append(f) 1514 | except ValueError: 1515 | ttm.fail(EDECIMAL) 1516 | # end ttm_gt 1517 | 1518 | def ttm_lt(ttm, frame, br): # Compare numeric less-than 1519 | slhs = frame.argv(1) 1520 | srhs = frame.argv(2) 1521 | t = frame.argv(3) 1522 | f = frame.argv(4) 1523 | try: 1524 | lhs = int(slhs) 1525 | rhs = int(srhs) 1526 | if (lhs < rhs): 1527 | br.append(t) 1528 | else: 1529 | br.append(f) 1530 | except ValueError: 1531 | ttm.fail(EDECIMAL) 1532 | # end ttm_lt 1533 | 1534 | def ttm_eql(ttm, frame, br): # ? Compare logical equal 1535 | slhs = frame.argv(1) 1536 | srhs = frame.argv(2) 1537 | t = frame.argv(3) 1538 | f = frame.argv(4) 1539 | if cmp(slhs, srhs) == 0: 1540 | br.append(t) 1541 | else: 1542 | br.append(f) 1543 | # end ttm_eql 1544 | 1545 | def ttm_gtl(ttm, frame, br): # ? Compare logical greater-than 1546 | slhs = frame.argv(1) 1547 | srhs = frame.argv(2) 1548 | t = frame.argv(3) 1549 | f = frame.argv(4) 1550 | if cmp(slhs, srhs) > 0: 1551 | br.append(t) 1552 | else: 1553 | br.append(f) 1554 | 1555 | # end ttm_gtl 1556 | 1557 | def ttm_ltl(ttm, frame, br): # ? Compare logical less-than 1558 | slhs = frame.argv(1) 1559 | srhs = frame.argv(2) 1560 | t = frame.argv(3) 1561 | f = frame.argv(4) 1562 | if cmp(slhs, srhs) < 0: 1563 | br.append(t) 1564 | else: 1565 | br.append(f) 1566 | 1567 | # end ttm_ltl 1568 | 1569 | # Peripheral Input/Output Operations 1570 | 1571 | def ttm_ps(ttm, frame, br): # Print a Name 1572 | s = frame.argv(1) 1573 | stdxx = None 1574 | if (frame.argc() == 3): 1575 | stdxx = frame.argv(2) 1576 | if (stdxx != None and stdxx == "stderr"): 1577 | target = ttm.stderr 1578 | else: 1579 | target = ttm.stdout 1580 | printstring(target, s, ttm.escapec) 1581 | 1582 | # end ttm_ps 1583 | 1584 | def ttm_rs(ttm, frame, br): # Read a Name 1585 | while True: 1586 | c = ttm.stdin.read(1) 1587 | if (c == EOF): break 1588 | if (c == ttm.metac): 1589 | break 1590 | br.put(c) 1591 | # end ttm_rs 1592 | 1593 | def ttm_psr(ttm, frame, br): # Print Name and Read 1594 | ttm_ps(ttm, frame, br) 1595 | ttm_rs(ttm, frame, br) 1596 | # end ttm_psr 1597 | 1598 | def ttm_cm(ttm, frame, br): # Change meta character 1599 | smeta = frame.argv(1) 1600 | if (len(smeta) > 0): 1601 | if (ord(smeta[0]) > 127): ttm.fail(EASCII) 1602 | ttm.metac = smeta[0] 1603 | # end ttm_cm 1604 | 1605 | def ttm_pf(ttm, frame, br): # Flush stdout and/or stderr 1606 | stdxx = None 1607 | if (frame.argc() > 1): stdxx = frame.argv(1) 1608 | if (stdxx == None or stdxx == "stdout"): 1609 | ttm.stdout.flush() 1610 | if (stdxx == None or stdxx == "stderr"): 1611 | ttm.stderr.flush() 1612 | # end ttm_pf 1613 | 1614 | # Library Operations 1615 | 1616 | def ttm_names(ttm, frame, br): # Obtain all Name instance names in sorted order 1617 | allnames = (frame.argc() > 1) 1618 | 1619 | # Collect all the names 1620 | names = [] 1621 | for key in ttm.dictionary: 1622 | name = ttm.dictionary[key] 1623 | if (allnames or not name.builtin): 1624 | names.append(key) 1625 | # Now sort the set of names 1626 | names.sort() 1627 | # Return the set of names separated by commas 1628 | first = True 1629 | for key in names: 1630 | if not first: br.put(',') 1631 | br.append(key) 1632 | first = False 1633 | # end ttm_names 1634 | 1635 | def ttm_exit(ttm, frame, br): # Return from TTM 1636 | exitcode = 0 1637 | ttm.flags |= FLAG_EXIT 1638 | if (frame.argc() > 1): 1639 | try: 1640 | exitcode = int(frame.argv(1)) 1641 | except ValueError: 1642 | ttm.fail(EDECIMAL) 1643 | if (exitcode < 0): exitcode = - exitcode 1644 | ttm.exitcode = exitcode 1645 | 1646 | # end ttm_exit 1647 | 1648 | # Utility Operations 1649 | 1650 | def ttm_ndf(ttm, frame, br): # Determine if a Name is Defined 1651 | arg = frame.argv(1) 1652 | t = frame.argv(2) 1653 | f = frame.argv(3) 1654 | if arg in ttm.dictionary: 1655 | br.append(t) 1656 | else: 1657 | br.append(f) 1658 | 1659 | # end ttm_ndf 1660 | 1661 | def ttm_norm(ttm, frame, br): # Obtain the Norm (length) of a string 1662 | s = frame.argv(1) 1663 | br.append(str(len(s))) 1664 | 1665 | # end ttm_norm 1666 | 1667 | def ttm_time(ttm, frame, br): # Obtain time of day 1668 | dt = time.time # floating point in seconds 1669 | dt *= 100 # need value to 1/100 second 1670 | idt = int(dt) 1671 | br.append(str(idt)) 1672 | 1673 | # end ttm_time 1674 | 1675 | def ttm_xtime(ttm, frame, br): # Obtain Execution Time 1676 | xtime = time.clock() + ttm.xtimebase 1677 | xtime *= 100 # want hundredths of a second 1678 | br.append(str(int(xtime))) 1679 | # end ttm_xtime 1680 | 1681 | def ttm_ctime(ttm, frame, br): # Convert ##