dietz@zhmti.UUCP (Dieter H. Zebbedies) (08/20/87)
"Producer", A package to translate Smalltalk-80 code to your favorite object oriented language, Objective-C. #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./Makefile` then echo "writting ./Makefile" cat > ./Makefile << '\Rogue\Monster\' DOC= README producer.f .SUFFIXES: .me .i .f all: README README: readme.f ; mv readme.f README .me.i: ; itroff -me mac.me $< .me.f: ; nroff -me mac.me $< >$$$$.f && mv $$$$.f $*.f \Rogue\Monster\ else echo "will not over write ./Makefile" fi if `test ! -s ./mac.me` then echo "writting ./mac.me" cat > ./mac.me << '\Rogue\Monster\' .\" Must be defined externally .\" \nN Chapter Number .po .5i .ll 7i .nr % 1 .ds NM \\nN .de H . tm .H0 \\*(NM-\\n% "\\$1" . sp .(l C \\s12\\fB\\$1\\fP\\s0 .)l . pp .. .de H1 . tm .H1 \\*(NM-\\n% "\\$1" . uh "\\$1" . pp .. .de H2 . tm .H2 \\*(NM-\\n% "\\$1" . uh "\\$1" .. .de M . tm .M \\*(NM-\\n% "\\$1" . uh "\\$1" .. .de MC . tm .MC \\*(NM-\\n% "\\$1" . uh "\\$1" .. .de (C . (l . sz -2n . ls 1 . ta .25i +.25i +.25i +.25i +.25i +.25i +.25i +.25i +.25i +.25i +.25i +.25i .. .de )C . ta . ls 2 . sz . )l .. .nr fn 1 .ds F \*(NM-1 .de (F . ds F \\*(NM-\\n(fn . tm .F \\*(NM-\\n% "Figure \\*F: \\$1" . (l M F . hl . sp . ce \fIFigure \\*F:\fP \\$1 .. .de )F . hl . )l . nr fn +1 . ds F \\*(NM-\\n(fn .. .de (N . (q \\fINOTE IN DRAFT: \\$1\\fP .. .de )N . )q .. .de NT . (q \\fINOTE IN DRAFT: \\$1\\fP . )q . tm .NT \\*(NM-\\n% "NOTE: \\$1" .. .de C .nr N \\$1 .ds NA \\$1 .he ''\\*(NA'' .fo 'Brad Cox'%'\\*(td' .(l C \\s14\\fB\\$2\\fP\\s0 .sp 2 .)l .he ''\\*(NA'' .tm .C \\nN-\\n% "\\$2" .pp .. \Rogue\Monster\ else echo "will not over write ./mac.me" fi if `test ! -s ./README` then echo "writting ./README" cat > ./README << '\Rogue\Monster\' Producer: Smalltalk-80 to Objective-C Translator Brad J. Cox Productivity Products International 75 Glen Road Sandy Hook, CT 06482 (203) 426 1875. Smalltalk-80 is a tool for turning raw concepts into working software prototypes. Objective-C is a tool for turning proven concepts into fast, commercial-quality, production systems. Producer is a tool for bridging the gap between prototyping and production by automati- cally translating Smalltalk-80 sources into Objective-C sources. The translation is guided by a rule base in which the programmer describes how differences between the Smalltalk-80 prototyping environment and the Objective-C production environment should be resolved when translating the code. At SIGGRAPH-87, PPI will announce a library of user interface components from which programmers build applications with iconic user interfaces. The library and applications built using it are portable across diverse window systems, initially X-Windows, SunWindows and Hewlett Packard's window system. While the Objective-C user interface classes are different from Smalltalk's, they are similar enough that Producer can usually bridge the differences with some hand-tuning of the translated output. We confidently hope that Objective-C, this library and Producer will make automatic translation of Smalltalk-80 prototypes a routine part of many companies' software development lifecycle. I'm distributing Producer to enlist your help in testing the practicality of this notion. Disclaimer Producer is not a mature software product but an embryo that could grow to maturity someday. Specifically it is not supported or warranteed in any way. It was written by myself, an individual employed by PPI, and has been released prior to maturity by myself as an individual with the consent of the company. This document will make its strengths and some of its present shortcomings clear. However, even in its present state, Producer demonstrates that automatic translation is technically feasible and its present imple- mentation provides a capable foundation on which to build. Since the market for Smalltalk-80 translators is insufficient for PPI to pursue presently, we've released Producer for you to make what use of it you can. I do ask that you keep me informed of your experiences in using it in its current state, and PPI requests that you feed back any Brad Cox 1 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator improvements so that we can offer a fully supported translation pro- duct in the future. PPI retains the copyright and all other applicable rights. For example, you may not sell products that contain any part of the Producer distribution without PPI's permission. How it works The following is a brief description of how Producer works inter- nally. This was written from my recollection of how I left the code over a year ago. It may be inaccurate in places. Producer is basically a compiler. It's lexical analyzer (written in lex) divides Smalltalk-80 text into lexemes, and its parser (writ- ten in yacc) recognizes valid lexeme sequences and constructs an abstract representation of the program as an expression tree. The expression tree consists of instances of Objective-C classes; e.g. Method, Statement, Expression, Message, and Variable. The grammar was derived from the syntax diagrams in Goldberg and Robson; _S_m_a_l_l_t_a_l_k-_8_0: _T_h_e _L_a_n_g_u_a_g_e _a_n_d _i_t_s _I_m_p_l_e_m_e_n_t_a_t_i_o_n; Addison Wesley; 1986. The grammar was extended to also recognize rules that may also appear in the lexeme stream. Rules are enclosed in { braces } to help fend off shift-reduce conflicts from yacc. The parser stores the rules in separate data structures for use during code generation. At certain points, the parser sends the top of the expression tree a gen message to trigger code generation[1]. Recall that Smalltalk-80 is an extremely simple language with basically two com- ponents; data references (variables, literals, etc) and messages. Rules may influence how each case is treated during code generation. Code generation proceeds in two passes. The first pass collects typing information for each symbol and message by examining the expression tree from the bottom up. The bottom-most nodes are either literals whose type is immediately obvious (e.g. 1, 2.3, or 'string'), or they are symbols whose type can be known or unknown. Symbol types ____________________ 9 [1] I now regard this as a major architectural flaw whenever I see it in any application. It represents a key departure from an important but often ignored rule of object-oriented design. The expression tree classes should be abstract so that they could be reused in other tools. But their code generation methods pollute the abstraction with knowledge about a particular concrete interface; Objective-C. The code generation methods should have been provided in a separate hierarchy of classes that know how to connect the abstract classes to one of many potential concrete interfaces. This rule is simply a generaliza- tion of the model/view/controller paradigm to apply to interfaces of any kind, not just user interfaces. 9 Brad Cox 2 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator become known either as the result of a previous type inferencing operation or because their type was specified in a rule. Unknown sym- bols default to id when first referenced. Most of the internal nodes are messages. Message typing is slightly more complicated because any message can have multiple trans- lations depending on how the message is used because different rules may specify different translations for different receiver and argu- ments types. The diverse translations may each compute a different type. Since we assign types bottom up, types have been assigned for the arguments and the receiver, so a translation for that selector is chosen by searching a table of possible translations for one matching the receiver and argument types. In all cases, unless overridden by a specific rule, default translations are used. These amount to a fairly literal translation from Smalltalk-80 syntax to Objective-C syntax. However exceptions are made for Smalltalk literal constants, which translate to C literal constants. In other words, 2+2 translates to [2 plus:2], which is _g_u_a_r_a_n_t_e_e_d to fail catastrophically in Objective-C. The integer 2 is an object only in Smalltalk! The moral: _N_e_v_e_r believe the translator. _A_l_w_a_y_s monitor it closely. Remember the 90-10 rule. The automatic translation concept is capable, with suitable rules, of automatically translating only 90% of an application correctly; the other 10% (where the bugs will have congregated) is still up to you. Implementation Status Producer currently represents about three man-weeks of effort, spent in two intensive bursts separated by about a year. The most recent burst was nearly a year and a half ago. The first burst was to demonstrate the feasibility and practicality of the translation con- cept. The second burst was in the course of preparing a paper that, coauthored with Kurt Schmucker, will appear in the OOPSLA-87 proceed- ings. A (very) early draft is provided with this distribution. For being developed so quickly, the translator does an effective job of translation. I refer you to the paper for discussions of the strengths and limitations of the translation concept. This section discusses the current implementation of this concept, the items on my own must-do list for the planned, but not yet completed, third stage of Producer's evolution. (1) Smalltalk-80 fileout format uses '!' delimiters in a fashion that I was never able to formalize correctly in Producer's yacc gram- mar. The symptom is that the translator will generate syntax errors in nearly every translated file for certain of these del- imiters. I'm told that fileout format has been documented in a Brad Cox 3 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator paper somewhere, but I've never worked the repairs back into the code. The fix should be local to gram.y. (2) The translator loads its rule base by reading files of rules as if they were concatenated with the sources to be translated. The rule-specification syntax is abysmal, primarily because it was chosen to minimize the amount of time I spent struggling with shift-reduce conflicts from yacc, rather than making the rules intelligible to users. Smalltalk's formal grammar seemed unrea- sonably difficult for yacc to swallow, and I suspect the problem may lie in some mistake I've made in translating Smalltalk-80 syntax diagrams into yacc specifications. (3) The program contains extensive provisions for reporting its cogi- tations in type inferencing. The various error, warning, logging, and debugging messages need to be tuned for greater utility. (4) The code was based on an as yet unreleased libary (phylum) called "Substrate", which supports features that are not yet in our standard product set, like Blocks, Coroutining, and exception handling. I made a fast editing pass to remove any dependencies on these nonstandard library features. I also added a file, Substrate.h, that defines stylistic conventions that I adhere to in all my work. See USE, IMPORT, EXPORT, etc in the sources. The preceeding problems are superficial and easily repaired. The following ones are somewhat more substantial in that they involve design work in addition to coding work. (1) The type inferencing machinery infers types of newly-encountered (unknown) messages and variables by seeing how they are combined with variables and messages whose types are known apriori or else determined earlier through inferencing. The only types that are known apriori are literals like 1, 2.3, or 'string'. This gen- erally provides insufficient typing information from which to infer anything useful, so you should generally provide variable rules to pin down types for key instance variables and method arguments You do this with rules that state, in effect, that `the type of the Smalltalk variable named foo is int, and the variable is called foobar in Objective-C'. Presently rules have global scope. If different Smalltalk classes use the name, foo, in ways that should be translated differently, different rule sets must be provided manually to the translator. Creating and managing these application-specific rules sets adds to the translation effort and tends to make rules non-reusable across translations. The rules should be organized with a scoping mechanism, ideally one based on inheritance. (2) The inferencing logic is ad-hoc and quite possibly slow. However the main bottleneck seems to be loading the rule-base; transla- tion speed has never been a real problem. Inferencing is presently deductive, and a more inductive scheme based on both forwards and backwards reasoning might produce higher quality Brad Cox 4 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator translations. In other words, the translation of a given message expression is determined exclusively by whatever information can be inferred about the types of the receiver and arguments to that message (forward reasoning). Backward reasoning would also con- sider how the results of the expression are used in other expres- sions. (3) Producer does not presently handle non-trivial uses of Blocks correctly; ie. Block expressions that cannot be translated directly into C conditional expressions like if, while, or for, which Producer handles just fine already. Nearly all occurrences of Smalltalk-80 Blocks could be handled without changing the Objective-C language by adding a trivially simple Block class to the library. A named instance variable holds a pointer to a static function and indexed instance variables hold _c_o_p_i_e_s _o_f any variables that the block accesses in the instantiation site[2]. This copy could be taken entirely automatically by copying the instantiation site's stack frame. However I prefer to have more control over space than that. So I've been using a scheme that requires the programmer (and someday the compiler) to specify which variables are really accessed by the block as arguments to the message that instantiates the block; like this ... { IMPORT void aStaticFunction(); id var1 = something, var2 = something; aBlock = [Block function:aStaticFunction args:2, var1, var2]; [anyObject do:aBlock]; ... } LOCAL void aStaticFunction(instantiationSiteVariables, value1, value2) struct { id var1, var2; } *instantiationSiteVariables; id value1, value2; { if ([instantiationSiteVariables->var1 someMessage]) ... } The block will call the function when anyObject sends the block one of several evaluation messages (value:arg1 or value:arg1 value:arg2 or ...). The first argument is a _p_o_i_n_t_e_r to block's copy of the instantiation site's variables. The trailing argu- ments contain the arguments that the invocation site passed in the value: message. I've used this approach extensively by writ- ing the static functions by hand, and am trying to get our staff to extend the language to provide some kind of language-level support to make the syntax simpler. This approach could be, but has not yet been, taken by Producer. ____________________ 9 [2] In Smalltalk-80, the block seems to have access to the instan- tiation site's variables, so that the block can change variables in 9 Brad Cox 5 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator The inferencing machinery's primary current virtue is that it can be made to work for selected test cases. It leaves lots to be desired. Call me if you decide to extend it so that I can prevent unnecessary duplication of effort. About the distribution The top level of the distribution consists of total 88 -rw-r--r-- 1 cox 181 Jun 22 14:32 Makefile -rw-r--r-- 1 cox 26592 Jun 22 14:30 README drwxr-xr-x 2 cox 512 Jun 22 14:19 example -rw-r--r-- 1 cox 166 Jun 16 13:18 log -rw-r--r-- 1 cox 997 Jun 15 11:09 mac.me -rw-r--r-- 1 cox 26751 Jun 15 11:02 producer.me -rw-r--r-- 1 cox 21444 Jun 22 14:29 readme.me drwxr-xr-x 2 cox 512 Jun 12 10:22 rules drwxr-xr-x 2 cox 3072 Jun 22 14:31 src The Makefile governs formatting of the two documents; this README (from readme.me) and the draft of the OOPSLA-87 paper (from Producer.me). The mac.me file contains text formatting macros that are common to both papers; used like this: nroff -me mac.me Producer.me >Producer.f The rules directory contains a single file, generic.ru, that represents a first pass at an application-independent rules base. This set of rules translate Smalltalk to the conventions used in my proto- type version of the user interface library. For example, it translates Smalltalk Integer operations to C int operations, and it translates Smalltalk Point operations to C macros that manage points as type PT; a pair of 16-bit coordinates in a 32- bit C int. For example, pt(x,y) invokes a C macro that trims and shifts two ints, x and y, to fit side by side in a 32-bit integer, ptPlus(p,q) invokes a macro that computes the vector sum of two points, p and q, etc. rules: total 35 ____________________ 9 the instantiation site. In Objective-C the block receives a copy of the variables and cannot use them to communicate with the instantia- tion site. I believe that this is the sole functional difference between the two schemes. 9 Brad Cox 6 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator -rw-r--r-- 1 cox 35567 Jun 12 10:22 generic.ru The src directory contains a fragment from the video animation program that appears at the end of the Smalltalk-80 video tape. BounceInBoxNode.st is the Smalltalk-80 source file, animation.ru con- tains the application-specific rule set, BounceInBoxNode.m is the translated version built by Producer as invoked by Makefile[3]. example: total 7 -rw-r--r-- 1 cox 1730 Jun 16 10:24 BounceInBoxNode.m -rw-r--r-- 1 cox 868 Jun 16 10:18 BounceInBoxNode.st -rw-r--r-- 1 cox 394 Jun 16 10:20 Makefile -rw-r--r-- 1 cox 2178 Jun 16 10:18 animation.ru -rw-r--r-- 1 cox 185 Jun 16 10:24 log -rw-r--r-- 1 cox 239 Jun 16 10:18 st80.h The log file records the results of the translation session. The syntax error is innocuous, the result of the beforementioned problem in the grammar in handling '!' delimiters. Producer -c ../rules/generic.ru animation.ru BounceInBoxNode.st >BounceInBoxNode.m error 7:BounceInBoxNode.st: tegory:'Graphics-Animation'!! : syntax error *** Error code 1 (ignored) The src directory contains the sources for Producer, with its own Makefile. The Substrate.h header file, which is automatically included by the Producer.h header file, is technically a part of a internal lower level library, Substrate, on which Producer was origi- nally developed. Substrate.h was copied and changed superficially so that Producer compiles correctly without the Substrate library. src: total 70 -rw-r--r-- 1 cox 483 Jun 12 10:21 AbstractTranslation.m -rw-r--r-- 1 cox 282 Jun 12 10:21 ArgumentList.m -rw-r--r-- 1 cox 897 Jun 12 10:21 Block.m -rw-r--r-- 1 cox 143 Jun 12 10:21 CharConstant.m -rw-r--r-- 1 cox 2205 Jun 12 10:21 Class.m -rw-r--r-- 1 cox 630 Jun 12 10:21 Comment.m -rw-r--r-- 1 cox 176 Jun 12 10:21 Constant.m -rw-r--r-- 1 cox 2032 Jun 12 10:21 Expr.m -rw-r--r-- 1 cox 1243 Jun 12 10:21 FunctionTranslation.m -rw-r--r-- 1 cox 1484 Jun 12 10:21 Identifier.m -rw-r--r-- 1 cox 1248 Jun 12 10:21 IdentifierTranslation.m ____________________ 9 [3] The full source for the animation program is not provided. My copyright paranoia argued against providing even this fragment. 9 Brad Cox 7 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator -rw-r--r-- 1 cox 105 Jun 12 10:21 List.m -rw-r--r-- 1 cox 1985 Jun 15 11:55 METHODDECLS.m -rw-r--r-- 1 cox 1384 Jun 15 11:51 Makefile -rw-r--r-- 1 cox 4302 Jun 12 10:21 Method.m -rw-r--r-- 1 cox 3136 Jun 12 10:21 Msg.m -rw-r--r-- 1 cox 583 Jun 12 10:21 MsgArgPattern.m -rw-r--r-- 1 cox 828 Jun 12 10:21 MsgNamePattern.m -rw-r--r-- 1 cox 1280 Jun 12 10:21 MsgTranslation.m -rw-r--r-- 1 cox 775 Jun 12 10:21 MsgTranslator.m -rw-r--r-- 1 cox 1868 Jun 12 10:21 Node.m -rw-r--r-- 1 cox 229 Jun 12 10:21 NumberConstant.m -rw-r--r-- 1 cox 1402 Jun 15 11:27 Producer.h -rw-r--r-- 1 cox 306 Jun 12 10:21 Return.m -rw-r--r-- 1 cox 825 Jun 12 10:21 Scope.m -rw-r--r-- 1 cox 3157 Jun 12 10:21 Selector.m -rw-r--r-- 1 cox 253 Jun 12 10:21 SelectorConstant.m -rw-r--r-- 1 cox 457 Jun 12 10:21 StArray.m -rw-r--r-- 1 cox 492 Jun 12 10:21 Stmt.m -rw-r--r-- 1 cox 381 Jun 12 10:21 StringConstant.m -rw-r--r-- 1 cox 1268 Jun 12 10:21 StringTranslation.m -rw-r--r-- 1 cox 2140 Jun 15 11:38 Substrate.h -rw-r--r-- 1 cox 1405 Jun 15 11:53 Symbol.m -rw-r--r-- 1 cox 452 Jun 12 10:21 Template.m -rw-r--r-- 1 cox 901 Jun 12 10:21 Type.m -rw-r--r-- 1 cox 1800 Jun 12 10:21 design.me -rw-r--r-- 1 cox 3271 Jun 12 10:21 gen.m -rw-r--r-- 1 cox 9007 Jun 12 10:21 gram.y -rw-r--r-- 1 cox 3601 Jun 12 10:21 lex.l -rw-r--r-- 1 cox 2212 Jun 12 10:21 main.m -rw-r--r-- 1 cox 260 Jun 12 10:21 st80.h -rw-r--r-- 1 cox 259 Jun 15 11:59 y.tab.h The files are exactly as I left them nearly a year and a half ago, except for: (1) The addition of this README document. An early draft of the OOPSLA-87 paper, sadly prior to Kurt Schmucker's improvements, is in Producer.me. (2) One recompilation pass to remove any obvious dependencies on my private Substrate library and to verify that Producer compiles and runs correctly on the standard Foundation library. I tested the changes by verifing that the Makefile in the example direc- tory ran to completion, but this is hardly an ironclad guarantee. Using Producer Flags controlling the translation process, source files, and rules files are provided on the command line and are processed in the Brad Cox 8 June 22, 1987 Producer: Smalltalk-80 to Objective-C Translator order they appear. The flags are[4] -d: Enable debugging functions (dbg()) scattered throughout the code. Seldom useful. -m: Enables the Objective-C Foundation library message tracing feature. Seldom useful in Producer. -a: Enables the Objective-C Foundation library allocation tracing feature. Seldom useful in Producer. -l: Enables printing of each lexical token as produced by lex. Useful only for debugging lex.l. -g: Enables automatic redirection of each class into a separate file based on the class name parsed from the input file. Automatically puts class Foobar into file Foobar.m. CAREFUL! This puts at risk other files whose name might coincide with a Smalltalk-80 class name! -s: Generate Smalltalk-80 sources in the output file as Objective-C comments (the default). -c: Don't generate Smalltalk-80 sources in the output file. -i: Generate information that was thought at one time to be useful when debugging rules. -M: Send storeOn: to the message rule dictionary just before ter- minating as a debugging aid. -I: Send storeOn: to the variable rule dictionary just before ter- minating as a debugging aid. Typically, the generic rules in rules/generic.ru is specified first, then any application-specific rules, then a single Smalltalk-80 source file. Unless -g is set, the translated output appears on stdout. The various creaks, groans and mumbles that can be elicited about the translation process itself appear on stderr. For the syntax for writing new rules, refer to the examples in generic.ru and animation.ru, and if necessary, the rules section of the grammar in gram.y. And good luck! Let me know how you fare... ____________________ 9 [4] I'm working from memory about what these flags mean. Some may be nonfunctional: 9 Brad Cox 9 June 22, 1987 \Rogue\Monster\ else echo "will not over write ./README" fi if `test ! -s ./readme.me` then echo "writting ./readme.me" cat > ./readme.me << '\Rogue\Monster\' .C "Producer: Smalltalk-80 to Objective-C Translator" .(l C Brad J. Cox Productivity Products International 75 Glen Road Sandy Hook, CT 06482 (203) 426 1875. .)l .pp Smalltalk-80 is a tool for turning raw concepts into working software prototypes. Objective-C is a tool for turning proven concepts into fast, commercial-quality, production systems. Producer is a tool for bridging the gap between prototyping and production by automatically translating Smalltalk-80 sources into Objective-C sources. The translation is guided by a rule base in which the programmer describes how differences between the Smalltalk-80 prototyping environment and the Objective-C production environment should be resolved when translating the code. .pp At SIGGRAPH-87, PPI will announce a library of user interface components from which programmers build applications with iconic user interfaces. The library and applications built using it are portable across diverse window systems, initially X-Windows, SunWindows and Hewlett Packard's window system. While the Objective-C user interface classes are different from Smalltalk's, they are similar enough that Producer can usually bridge the differences with some hand-tuning of the translated output. We confidently hope that Objective-C, this library and Producer will make automatic translation of Smalltalk-80 prototypes a routine part of many companies' software development lifecycle. .pp I'm distributing Producer to enlist your help in testing the practicality of this notion. .H "Disclaimer" .pp Producer is not a mature software product but an embryo that could grow to maturity someday. Specifically it is not supported or warranteed in any way. It was written by myself, an individual employed by PPI, and has been released prior to maturity by myself as an individual with the consent of the company. This document will make its strengths and some of its present shortcomings clear. .pp However, even in its present state, Producer demonstrates that automatic translation is technically feasible and its present implementation provides a capable foundation on which to build. Since the market for Smalltalk-80 translators is insufficient for PPI to pursue presently, we've released Producer for you to make what use of it you can. .pp I do ask that you keep me informed of your experiences in using it in its current state, and PPI requests that you feed back any improvements so that we can offer a fully supported translation product in the future. PPI retains the copyright and all other applicable rights. For example, you may not sell products that contain any part of the Producer distribution without PPI's permission. .H "How it works" .pp The following is a brief description of how Producer works internally. This was written from my recollection of how I left the code over a year ago. It may be inaccurate in places. .pp Producer is basically a compiler. It's lexical analyzer (written in lex) divides Smalltalk-80 text into lexemes, and its parser (written in yacc) recognizes valid lexeme sequences and constructs an abstract representation of the program as an expression tree. The expression tree consists of instances of Objective-C classes; e.g. Method, Statement, Expression, Message, and Variable. The grammar was derived from the syntax diagrams in Goldberg and Robson; \fISmalltalk-80: The Language and its Implementation\fP; Addison Wesley; 1986. .pp The grammar was extended to also recognize rules that may also appear in the lexeme stream. Rules are enclosed in { braces } to help fend off shift-reduce conflicts from yacc. The parser stores the rules in separate data structures for use during code generation. .pp At certain points, the parser sends the top of the expression tree a gen message to trigger code generation\**. Recall that Smalltalk-80 is an extremely simple language with basically two components; data references (variables, literals, etc) and messages. Rules may influence how each case is treated during code generation. .(f \** I now regard this as a major architectural flaw whenever I see it in any application. It represents a key departure from an important but often ignored rule of object-oriented design. The expression tree classes should be abstract so that they could be reused in other tools. But their code generation methods pollute the abstraction with knowledge about a particular concrete interface; Objective-C. The code generation methods should have been provided in a separate hierarchy of classes that know how to connect the abstract classes to one of many potential concrete interfaces. This rule is simply a generalization of the model/view/controller paradigm to apply to interfaces of any kind, not just user interfaces. .)f .pp Code generation proceeds in two passes. The first pass collects typing information for each symbol and message by examining the expression tree from the bottom up. The bottom-most nodes are either literals whose type is immediately obvious (e.g. 1, 2.3, or 'string'), or they are symbols whose type can be known or unknown. Symbol types become known either as the result of a previous type inferencing operation or because their type was specified in a rule. Unknown symbols default to id when first referenced. .pp Most of the internal nodes are messages. Message typing is slightly more complicated because any message can have multiple translations depending on how the message is used because different rules may specify different translations for different receiver and arguments types. The diverse translations may each compute a different type. Since we assign types bottom up, types have been assigned for the arguments and the receiver, so a translation for that selector is chosen by searching a table of possible translations for one matching the receiver and argument types. .pp In all cases, unless overridden by a specific rule, default translations are used. These amount to a fairly literal translation from Smalltalk-80 syntax to Objective-C syntax. However exceptions are made for Smalltalk literal constants, which translate to C literal constants. In other words, 2+2 translates to [2 plus:2], which is \fIguaranteed\fP to fail catastrophically in Objective-C. The integer 2 is an object only in Smalltalk! .pp The moral: \fINever\fP believe the translator. \fIAlways\fP monitor it closely. Remember the 90-10 rule. The automatic translation concept is capable, with suitable rules, of automatically translating only 90% of an application correctly; the other 10% (where the bugs will have congregated) is still up to you. .H "Implementation Status" .pp Producer currently represents about three man-weeks of effort, spent in two intensive bursts separated by about a year. The most recent burst was nearly a year and a half ago. The first burst was to demonstrate the feasibility and practicality of the translation concept. The second burst was in the course of preparing a paper that, coauthored with Kurt Schmucker, will appear in the OOPSLA-87 proceedings. A (very) early draft is provided with this distribution. .pp For being developed so quickly, the translator does an effective job of translation. I refer you to the paper for discussions of the strengths and limitations of the translation concept. This section discusses the current implementation of this concept, the items on my own must-do list for the planned, but not yet completed, third stage of Producer's evolution. .np Smalltalk-80 fileout format uses '!' delimiters in a fashion that I was never able to formalize correctly in Producer's yacc grammar. The symptom is that the translator will generate syntax errors in nearly every translated file for certain of these delimiters. I'm told that fileout format has been documented in a paper somewhere, but I've never worked the repairs back into the code. The fix should be local to gram.y. .np The translator loads its rule base by reading files of rules as if they were concatenated with the sources to be translated. The rule-specification syntax is abysmal, primarily because it was chosen to minimize the amount of time I spent struggling with shift-reduce conflicts from yacc, rather than making the rules intelligible to users. Smalltalk's formal grammar seemed unreasonably difficult for yacc to swallow, and I suspect the problem may lie in some mistake I've made in translating Smalltalk-80 syntax diagrams into yacc specifications. .np The program contains extensive provisions for reporting its cogitations in type inferencing. The various error, warning, logging, and debugging messages need to be tuned for greater utility. .np The code was based on an as yet unreleased libary (phylum) called "Substrate", which supports features that are not yet in our standard product set, like Blocks, Coroutining, and exception handling. I made a fast editing pass to remove any dependencies on these nonstandard library features. I also added a file, Substrate.h, that defines stylistic conventions that I adhere to in all my work. See USE, IMPORT, EXPORT, etc in the sources. .pp The preceeding problems are superficial and easily repaired. The following ones are somewhat more substantial in that they involve design work in addition to coding work. .np The type inferencing machinery infers types of newly-encountered (unknown) messages and variables by seeing how they are combined with variables and messages whose types are known apriori or else determined earlier through inferencing. The only types that are known apriori are literals like 1, 2.3, or 'string'. This generally provides insufficient typing information from which to infer anything useful, so you should generally provide variable rules to pin down types for key instance variables and method arguments You do this with rules that state, in effect, that `the type of the Smalltalk variable named foo is int, and the variable is called foobar in Objective-C'. Presently rules have global scope. If different Smalltalk classes use the name, foo, in ways that should be translated differently, different rule sets must be provided manually to the translator. Creating and managing these application-specific rules sets adds to the translation effort and tends to make rules non-reusable across translations. The rules should be organized with a scoping mechanism, ideally one based on inheritance. .np The inferencing logic is ad-hoc and quite possibly slow. However the main bottleneck seems to be loading the rule-base; translation speed has never been a real problem. Inferencing is presently deductive, and a more inductive scheme based on both forwards and backwards reasoning might produce higher quality translations. In other words, the translation of a given message expression is determined exclusively by whatever information can be inferred about the types of the receiver and arguments to that message (forward reasoning). Backward reasoning would also consider how the results of the expression are used in other expressions. .np Producer does not presently handle non-trivial uses of Blocks correctly; ie. Block expressions that cannot be translated directly into C conditional expressions like if, while, or for, which Producer handles just fine already. Nearly all occurrences of Smalltalk-80 Blocks could be handled without changing the Objective-C language by adding a trivially simple Block class to the library. A named instance variable holds a pointer to a static function and indexed instance variables hold \fIcopies of\fP any variables that the block accesses in the instantiation site\**. This copy could be taken entirely automatically by copying the instantiation site's stack frame. However I prefer to have more control over space than that. So I've been using a scheme that requires the programmer (and someday the compiler) to specify which variables are really accessed by the block as arguments to the message that instantiates the block; like this .(C ... { IMPORT void aStaticFunction(); id var1 = something, var2 = something; aBlock = [Block function:aStaticFunction args:2, var1, var2]; [anyObject do:aBlock]; ... } LOCAL void aStaticFunction(instantiationSiteVariables, value1, value2) struct { id var1, var2; } *instantiationSiteVariables; id value1, value2; { if ([instantiationSiteVariables->var1 someMessage]) ... } .)C .ip The block will call the function when anyObject sends the block one of several evaluation messages (value:arg1 or value:arg1 value:arg2 or ...). The first argument is a \fIpointer\fP to block's copy of the instantiation site's variables. The trailing arguments contain the arguments that the invocation site passed in the value: message. I've used this approach extensively by writing the static functions by hand, and am trying to get our staff to extend the language to provide some kind of language-level support to make the syntax simpler. This approach could be, but has not yet been, taken by Producer. .(f \** In Smalltalk-80, the block seems to have access to the instantiation site's variables, so that the block can change variables in the instantiation site. In Objective-C the block receives a copy of the variables and cannot use them to communicate with the instantiation site. I believe that this is the sole functional difference between the two schemes. .)f .pp The inferencing machinery's primary current virtue is that it can be made to work for selected test cases. It leaves lots to be desired. Call me if you decide to extend it so that I can prevent unnecessary duplication of effort. .H "About the distribution" .pp The top level of the distribution consists of .(C total 88 -rw-r--r-- 1 cox 181 Jun 22 14:32 Makefile -rw-r--r-- 1 cox 26592 Jun 22 14:30 README drwxr-xr-x 2 cox 512 Jun 22 14:19 example -rw-r--r-- 1 cox 166 Jun 16 13:18 log -rw-r--r-- 1 cox 997 Jun 15 11:09 mac.me -rw-r--r-- 1 cox 26751 Jun 15 11:02 producer.me -rw-r--r-- 1 cox 21444 Jun 22 14:29 readme.me drwxr-xr-x 2 cox 512 Jun 12 10:22 rules drwxr-xr-x 2 cox 3072 Jun 22 14:31 src .)C The Makefile governs formatting of the two documents; this README (from readme.me) and the draft of the OOPSLA-87 paper (from Producer.me). The mac.me file contains text formatting macros that are common to both papers; used like this: .(C nroff -me mac.me Producer.me >Producer.f .)C .pp The rules directory contains a single file, generic.ru, that represents a first pass at an application-independent rules base. This set of rules translate Smalltalk to the conventions used in my prototype version of the user interface library. .pp For example, it translates Smalltalk Integer operations to C int operations, and it translates Smalltalk Point operations to C macros that manage points as type PT; a pair of 16-bit coordinates in a 32-bit C int. For example, pt(x,y) invokes a C macro that trims and shifts two ints, x and y, to fit side by side in a 32-bit integer, ptPlus(p,q) invokes a macro that computes the vector sum of two points, p and q, etc. .(C rules: total 35 -rw-r--r-- 1 cox 35567 Jun 12 10:22 generic.ru .)C .pp The src directory contains a fragment from the video animation program that appears at the end of the Smalltalk-80 video tape. BounceInBoxNode.st is the Smalltalk-80 source file, animation.ru contains the application-specific rule set, BounceInBoxNode.m is the translated version built by Producer as invoked by Makefile\**. .(f \** The full source for the animation program is not provided. My copyright paranoia argued against providing even this fragment. .)f .(C example: total 7 -rw-r--r-- 1 cox 1730 Jun 16 10:24 BounceInBoxNode.m -rw-r--r-- 1 cox 868 Jun 16 10:18 BounceInBoxNode.st -rw-r--r-- 1 cox 394 Jun 16 10:20 Makefile -rw-r--r-- 1 cox 2178 Jun 16 10:18 animation.ru -rw-r--r-- 1 cox 185 Jun 16 10:24 log -rw-r--r-- 1 cox 239 Jun 16 10:18 st80.h .)C .pp The log file records the results of the translation session. The syntax error is innocuous, the result of the beforementioned problem in the grammar in handling '!' delimiters. .(C Producer -c ../rules/generic.ru animation.ru BounceInBoxNode.st >BounceInBoxNode.m error 7:BounceInBoxNode.st: tegory:'Graphics-Animation'!! : syntax error *** Error code 1 (ignored) .)C .pp The src directory contains the sources for Producer, with its own Makefile. The Substrate.h header file, which is automatically included by the Producer.h header file, is technically a part of a internal lower level library, Substrate, on which Producer was originally developed. Substrate.h was copied and changed superficially so that Producer compiles correctly without the Substrate library. .(C src: total 70 -rw-r--r-- 1 cox 483 Jun 12 10:21 AbstractTranslation.m -rw-r--r-- 1 cox 282 Jun 12 10:21 ArgumentList.m -rw-r--r-- 1 cox 897 Jun 12 10:21 Block.m -rw-r--r-- 1 cox 143 Jun 12 10:21 CharConstant.m -rw-r--r-- 1 cox 2205 Jun 12 10:21 Class.m -rw-r--r-- 1 cox 630 Jun 12 10:21 Comment.m -rw-r--r-- 1 cox 176 Jun 12 10:21 Constant.m -rw-r--r-- 1 cox 2032 Jun 12 10:21 Expr.m -rw-r--r-- 1 cox 1243 Jun 12 10:21 FunctionTranslation.m -rw-r--r-- 1 cox 1484 Jun 12 10:21 Identifier.m -rw-r--r-- 1 cox 1248 Jun 12 10:21 IdentifierTranslation.m -rw-r--r-- 1 cox 105 Jun 12 10:21 List.m -rw-r--r-- 1 cox 1985 Jun 15 11:55 METHODDECLS.m -rw-r--r-- 1 cox 1384 Jun 15 11:51 Makefile -rw-r--r-- 1 cox 4302 Jun 12 10:21 Method.m -rw-r--r-- 1 cox 3136 Jun 12 10:21 Msg.m -rw-r--r-- 1 cox 583 Jun 12 10:21 MsgArgPattern.m -rw-r--r-- 1 cox 828 Jun 12 10:21 MsgNamePattern.m -rw-r--r-- 1 cox 1280 Jun 12 10:21 MsgTranslation.m -rw-r--r-- 1 cox 775 Jun 12 10:21 MsgTranslator.m -rw-r--r-- 1 cox 1868 Jun 12 10:21 Node.m -rw-r--r-- 1 cox 229 Jun 12 10:21 NumberConstant.m -rw-r--r-- 1 cox 1402 Jun 15 11:27 Producer.h -rw-r--r-- 1 cox 306 Jun 12 10:21 Return.m -rw-r--r-- 1 cox 825 Jun 12 10:21 Scope.m -rw-r--r-- 1 cox 3157 Jun 12 10:21 Selector.m -rw-r--r-- 1 cox 253 Jun 12 10:21 SelectorConstant.m -rw-r--r-- 1 cox 457 Jun 12 10:21 StArray.m -rw-r--r-- 1 cox 492 Jun 12 10:21 Stmt.m -rw-r--r-- 1 cox 381 Jun 12 10:21 StringConstant.m -rw-r--r-- 1 cox 1268 Jun 12 10:21 StringTranslation.m -rw-r--r-- 1 cox 2140 Jun 15 11:38 Substrate.h -rw-r--r-- 1 cox 1405 Jun 15 11:53 Symbol.m -rw-r--r-- 1 cox 452 Jun 12 10:21 Template.m -rw-r--r-- 1 cox 901 Jun 12 10:21 Type.m -rw-r--r-- 1 cox 1800 Jun 12 10:21 design.me -rw-r--r-- 1 cox 3271 Jun 12 10:21 gen.m -rw-r--r-- 1 cox 9007 Jun 12 10:21 gram.y -rw-r--r-- 1 cox 3601 Jun 12 10:21 lex.l -rw-r--r-- 1 cox 2212 Jun 12 10:21 main.m -rw-r--r-- 1 cox 260 Jun 12 10:21 st80.h -rw-r--r-- 1 cox 259 Jun 15 11:59 y.tab.h .)C .pp The files are exactly as I left them nearly a year and a half ago, except for: .np The addition of this README document. An early draft of the OOPSLA-87 paper, sadly prior to Kurt Schmucker's improvements, is in Producer.me. .np One recompilation pass to remove any obvious dependencies on my private Substrate library and to verify that Producer compiles and runs correctly on the standard Foundation library. I tested the changes by verifing that the Makefile in the example directory ran to completion, but this is hardly an ironclad guarantee. .H "Using Producer" .pp Flags controlling the translation process, source files, and rules files are provided on the command line and are processed in the order they appear. The flags are\** .(f \** I'm working from memory about what these flags mean. Some may be nonfunctional: .)f .ip -d: Enable debugging functions (dbg()) scattered throughout the code. Seldom useful. .ip -m: Enables the Objective-C Foundation library message tracing feature. Seldom useful in Producer. .ip -a: Enables the Objective-C Foundation library allocation tracing feature. Seldom useful in Producer. .ip -l: Enables printing of each lexical token as produced by lex. Useful only for debugging lex.l. .ip -g: Enables automatic redirection of each class into a separate file based on the class name parsed from the input file. Automatically puts class Foobar into file Foobar.m. .(q CAREFUL! This puts at risk other files whose name might coincide with a Smalltalk-80 class name! .)q .ip -s: Generate Smalltalk-80 sources in the output file as Objective-C comments (the default). .ip -c: Don't generate Smalltalk-80 sources in the output file. .ip -i: Generate information that was thought at one time to be useful when debugging rules. .ip -M: Send storeOn: to the message rule dictionary just before terminating as a debugging aid. .ip -I: Send storeOn: to the variable rule dictionary just before terminating as a debugging aid. .pp Typically, the generic rules in rules/generic.ru is specified first, then any application-specific rules, then a single Smalltalk-80 source file. Unless -g is set, the translated output appears on stdout. The various creaks, groans and mumbles that can be elicited about the translation process itself appear on stderr. .pp For the syntax for writing new rules, refer to the examples in generic.ru and animation.ru, and if necessary, the rules section of the grammar in gram.y. .pp And good luck! Let me know how you fare... \Rogue\Monster\ else echo "will not over write ./readme.me" fi echo "Finished archive 1 of 5" exit ---- Dieter H. Zebbedies ('dee-ter ayech 'zeb-ed-eez) Zebb-Hoff Mach. Tool's Automated Manufacturing Project Cleveland, OH (USnail): 9535 Clinton Rd, Cleveland, OH 44144 (+216 631 6100) (+216 741-5994) (UUCP): ...{decvax,sun,cbosgd}!cwruecmp!zhmti!dieter (CSNET/ARPA/BITNET): dieter@CWRU.Ewil
dietz@zhmti.UUCP (Dieter H. Zebbedies) (08/20/87)
"Producer", A package to translate Smalltalk-80 code to your favorite object oriented language, Objective-C. #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./example` then mkdir ./example echo "mkdir ./example" fi if `test ! -s ./example/Makefile` then echo "writting ./example/Makefile" cat > ./example/Makefile << '\Rogue\Monster\' O= BounceInBoxNode.o M= BounceInBoxNode.m S= BounceInBoxNode.st .SUFFIXES: .SUFFIXES: .m .st .o .i P=../src/producer -c R=../rules/generic.ru animation.ru X=objcc -c -I/u/cox/ui all: rm src rm: ; rm -f $M src: $M obj: $O stvd.a: $O ; ar ruv stvd.a $* .st.o: ; $P $R $< >$*.m && $X $*.m .st.m: ; -$P $R $< >$*.m .st.i: ; $P -i $R $< >$*.i 2>&1 .m.o: ; $X $*.m wc: ; wc $S clean: ; rm -f *.[mcoi] log core \Rogue\Monster\ else echo "will not over write ./example/Makefile" fi if `test ! -s ./example/BounceInBoxNode.st` then echo "writting ./example/BounceInBoxNode.st" cat > ./example/BounceInBoxNode.st << '\Rogue\Monster\' MovingNode subclass: #BounceInBoxNode instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Animation'! !BounceInBoxNode methodsFor: 'display'! displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm | relLoc | super displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm. relLoc _ location + clipRectangle origin. (velocity x < 0 and: [relLoc x < clipRectangle left]) ifTrue: [velocity _ velocity*(-1@1)]. (velocity x > 0 and: [(relLoc x+contents width) > clipRectangle right]) ifTrue: [velocity _ velocity*(-1@1)]. (velocity y < 0 and: [relLoc y < clipRectangle top]) ifTrue: [velocity _ velocity*(1@-1)]. (velocity y > 0 and: [(relLoc y+contents height) > clipRectangle bottom] ) ifTrue: [velocity _ velocity*(1@-1)]. ! ! \Rogue\Monster\ else echo "will not over write ./example/BounceInBoxNode.st" fi if `test ! -s ./example/st80.h` then echo "writting ./example/st80.h" cat > ./example/st80.h << '\Rogue\Monster\' // Collecting.h: #ifndef VIDEOANIMATION_H #define VIDEOANIMATION_H # include "DisplayObjects.h" # ifdef OBJC_COX # define CATEGORIES() # else # define CATEGORIES() (VideoAnimation, DisplayObjects, Collecting, Primitive) # endif #endif \Rogue\Monster\ else echo "will not over write ./example/st80.h" fi if `test ! -s ./example/animation.ru` then echo "writting ./example/animation.ru" cat > ./example/animation.ru << '\Rogue\Monster\' { # BounceInBoxNode (id) BounceInBoxNode } { # DisplayObject (id) DisplayObject } { # EvaluationNode (id) EvaluationNode } { # MovingNode (id) MovingNode } { # PositionNode (id) PositionNode } { # SequenceNode (id) SequenceNode } { # SuperpositionNode (id) SuperpositionNode } { # WindowNode (id) WindowNode } { # aBlock (id) aBlock } { # aCollection (id) aCollection } { # aDisplayPoint (PT) aDisplayPoint } { # aForm (id) aForm } { # boundingBox (id) boundingBox } { # c (id) aCollection } { # clipRect (id) clipRect } { # clipRectangle (id) clipRectangle } { # contents (id) contents } { # destForm (id) destForm } { # evalBlock (id) evalBlock } { # index (int) index } { # l (PT) locationPoint } { # location (PT) location } { # node (id) node } { # position (PT) position } { # relLoc (PT) relLoc } { # ruleInteger (int) ruleInteger } { # subNodes (id) subNodes } { # v (PT) velocityPoint } { # velocity (PT) velocity } { # window (id) window } { (id)displayOn:(id) at:(PT) clippingBox:(id) rule:(int) mask:(id) # (id) [displayOn:%1 at:(PT)%2 clipBy:%3 rule:(int)%4 mask:%5] } { # [(id)at:(PT)pt] } { # [(id)contents:(id)a location:(PT)b ] } { # (id)[fixTemps] } { # (id)[setBlock:(id)a] } { # (id)[setContents:(id)a location:(PT)b] } { # (id)[setSubNodes:(id)a ] } { # (id)[setVelocity:(PT)a ] } { (id)setNodes:(id) index:(int) # (id)[setNodes:(id)nodeCollection index:(int)anInteger] } { (id)subNodes:(id) index:(int) # (id)[subNodes:(id)aCollection index:(int)anInteger] } { # (BOOL)[anyButtonPressed] } { # (id)[block:(BLOCK)aBlock ] } { # (id)[contents:(id)aForm location:(PT)aPoint velocity:(PT)velocityPoint ] } { # (id)[displayView] } { # (id)[extent:(PT)anExtent fromArray:anArray offset:(PT)aPoint ] } { # (id)[model:(id)aModel ] } { # (PT)[mousePoint] } { # (RULE)[over] } { (id)pi # (float)'3.414' } { # (id)[release] } { # (id)[run:(id)aWindowNode ] } { # (id)[setWindow:(id)aWindow contents:(id)aCollection ] } { # (id)[spiral:(float)anAngle] } { # (id)[subNodes:(id)aNode ] } { # (id)[window:(id)aWindow contents:(id)aCollection ] } { (id)with:(id) with:(id) with:(id) # (id)'[%0 with:3, %1, %2, %3]' } { # n (int) n } { # (int)[collect:(BLOCK)aBlock ] } \Rogue\Monster\ else echo "will not over write ./example/animation.ru" fi if `test ! -s ./example/BounceInBoxNode.m` then echo "writting ./example/BounceInBoxNode.m" cat > ./example/BounceInBoxNode.m << '\Rogue\Monster\' // // MovingNode subclass: #BounceInBoxNode // instanceVariableNames: '' // classVariableNames: '' // poolDictionaries: '' // category: 'Graphics-Animation'! #include "st80.h" = BounceInBoxNode:MovingNode CATEGORIES(){ } // // !BounceInBoxNode methodsFor: 'display'! // displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle rule: // ruleInteger mask: aForm // | relLoc | // super displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle // rule: ruleInteger mask: aForm. // relLoc _ location + clipRectangle origin. // (velocity x < 0 and: [relLoc x < clipRectangle left]) // ifTrue: [velocity _ velocity*(-1@1)]. // (velocity x > 0 and: [(relLoc x+contents width) > clipRectangle right]) // ifTrue: [velocity _ velocity*(-1@1)]. // (velocity y < 0 and: [relLoc y < clipRectangle top]) // ifTrue: [velocity _ velocity*(1@-1)]. // (velocity y > 0 and: [(relLoc y+contents height) > clipRectangle bottom] // ) // ifTrue: [velocity _ velocity*(1@-1)]. // ! ! - displayOn:destForm at:(PT)aDisplayPoint clippingBox:clipRectangle rule:(int)ruleInteger mask:aForm { PT relLoc; [super displayOn:destForm at:aDisplayPoint clipBy:clipRectangle rule:ruleInteger mask:aForm]; relLoc = ptPlus(location, [clipRectangle origin]); if (ptX(velocity) < 0 && ptX(relLoc) < [clipRectangle left]) velocity = ptTimes(velocity, pt(-1,1))if (ptX(velocity) > 0 && ptX(relLoc) + [contents width] > [clipRectangle right]) velocity = ptTimes(velocity, pt(-1,1))if (ptY(velocity) < 0 && ptY(relLoc) < [clipRectangle top]) velocity = ptTimes(velocity, pt(1,-1))if (ptY(velocity) > 0 && ptY(relLoc) + [contents height] > [clipRectangle bottom]) velocity = ptTimes(velocity, pt(1,-1))return self; } // // \Rogue\Monster\ else echo "will not over write ./example/BounceInBoxNode.m" fi if `test ! -s ./log` then echo "writting ./log" cat > ./log << '\Rogue\Monster\' itroff -me mac.me doc.me .C 0-1 "" .H0 0-1 "Provisos" .H0 0-1 "How it works" .H0 0-2 "Status" .H0 0-3 "What's in this distribution" .H0 0-5 "Usage" ------ make doc.i \Rogue\Monster\ else echo "will not over write ./log" fi if `test ! -s ./producer.me` then echo "writting ./producer.me" cat > ./producer.me << '\Rogue\Monster\' .tp .po .5i .ll 7i .(l C \fB\s14Producer\s0\fP \fIFrom the Prototyping Laboratory to the Production Line Translating Smalltalk-80 Applications to Objective-C\fP Brad Cox, Ph.D. Productivity Products International Sandy Hook, CT 06497 (203) 426 1875 For presentation at OOPSLA/86 .)C .(q .hl .ce \fIAbstract\fP .sp This paper proposes that source to source translation tools could provide a way of integrating the strengths of production programming environments like C/Unix with rapid prototyping environments like Smalltalk-80 into a comprehensive hybrid environment that spans more of the software development life-spiral than ever before. It describes a tool-assisted process for translating Smalltalk-80 programs into Objective-C, and shows how the tool is used in practice. .hl .)q .he 'Producer'' .fo 'Productivity Products International'Page %'\*(td' .(f \fIObjective-C, Software-IC, Vici\fP and \fIPPI\fP are trademarks of Productivity Products International. .)f .(f \fISmalltalk-80\fP is a trademark of Xerox Corporation. .)f .(f \fIUnix\fP is a trademark of AT&T. .)f .pp Smalltalk-80 and C/Unix are very different tools that are optimized for different jobs at opposite ends of the process for transforming raw ideas into commercial software products. To use an analogy from automobile manufacturing, Smalltalk-80 is at its best in the design shop for building prototypes from which next year's model can be visualized, and C/Unix are at their best on the production line for building cost-effective implementations once the design has been proven through prototyping. .pp However it is becoming increasingly harder to maintain a strict separation between prototyping and production. Many production systems, particularly in CAD and office automation, are providing features, like iconic user interfaces, that grew up in prototyping environments and which depend on experimentation and tuning for good human factors. This kind of tuning is far easier in prototyping languages like Smalltalk-80, because machine resources can be spent lavishly on pervasive productivity aids like automatic garbage collection, and because the tools are tightly coupled to the system being constructed. .pp However the C/Unix community has historically favored a non-integrated approach that keeps the product separate from the tools, and they commonly exploit special purpose languages to amplify C's basic capabilities; i.e. \fIyacc\fP for building parsers, \fIlex\fP for building lexical analyzers, \fItroff\fP for formating documents, \fImake\fP for controlling compilations, \fIadb\fP or \fIdbx\fP for debugging, and many others\**. So why not treat Smalltalk-80 as a special purpose tool for rapid prototyping and ultimately translate the prototypes into C for production? This could eliminate the cradle to grave committment problem that has hindered Smalltalk's acceptance to date, and would integrate the strengths of the two approaches into a comprehensive hybrid software development environment. .(f \** Unix Programmers Manual. In particular see \fILex - A Lexical Analyzer Generator\fP, and \fIYacc - Yet Another Compiler Compiler\fP. Also see \fILR Parsing\fP, Aho and Johnson, Computing Surveys, June, 1974. .)f .pp This article describes a tool-assisted process for translating Smalltalk-80 applications into C language. The routine parts of the process are handled by a translation tool, \fIproducer\fP, which translates Smalltalk-80 code into Objective-C, an object-oriented extention to C language. Obviously, it is not realistic to expect any tool to turn arbitrary half-baked prototypes into polished software products automatically. Nor can static analysis make perfectly accurate predictions about a program's dynamic behavior, such as the specific type a polymorphic variable will hold at run time or when it is safe to release storage that may be multiply referenced. This information is not available in the text of a Smalltalk program and deriving it by static analysis is a truly hard (NP-complete) problem. This work sidesteps these problems by keeping the programmer involved in the translation process. .pp Translating code between languages as different as these is like designing a bridge. Although bridge builders often use generic tools and components, each bridge is custom designed to fit within a larger system of expressways, access roads, and approach ramps. No part is designed in isolation, but in combination to reduce the overall cost of the project. Producer is not a bridge, but a component from which bridges can be assembled. It need not solve the general problem of translating arbitrary Smalltalk-80 program into C with guaranteed reliablity, since the programmer can help by changing the source and target environments, by guiding the translation, or even by repairing translation errors by hand. .H "Background" This work was made feasible by several other PPI projects, all directed at the same goal; adapting concepts and tools that have been proven in the prototyping community for use in production programming. The foundation is Objective-C language, which is implemented by a compiler that translates object-oriented constructs of Smalltalk-80 into expressions that can compiled by any C compiler. The same language is also available in \fIVici\fP, an interpreter that allows classes and ordinary C code to be developed, tested, and changed interactively by eliminating the usual edit, compile, link, and test cycle. .(f \** \fIObject-oriented Programming, An Evolutionary Approach\fP, Brad Cox, Addison Wesley, 1986. .)f .pp Both products make object-oriented programming available to production programmers. This make it possible for them to cooperate in building and reusing large libaries of pretested, documented classes that PPI calls \fISoftware-ICs\fP to dramatize important parallels between object-oriented programming and the invention of the integrated circuit chip, two technologies for packaging the efforts of suppliers for reuse by consumers. This provides the crucial missing element that has until now prevented software developers from obtaining the explosive growth in productivity that hardware developers achieve routinely. .pp Several such libraries have been implemented. Classes similar to Smalltalk's lower-level classes (Arrays, Collections, etc) have long been available\**, .(f \** \fISoftware-IC Specification Sheets\fP, Objective-C Programmers Reference Manual; PPI. .)f and a compatible library of user interface Software-ICs has recently been developed in a form that is portable across the windowing environments of most engineering workstations\**. .(f \** \fIObject-oriented Programming and Iconic User Interfaces\fP, Bill Hunt (Hewlett Packard) and Brad Cox (PPI), Byte Magazine, August 1986. .)f Other projects\** have developed additional features of the Smalltalk substrate, including an (optional) automatic garbage collector. .(f \** Unpublished; Frank Parish (Hewlett Packard) and Alan Watt (PPI). .)f .pp These libraries provide the substrate needed to build an eventual Unix-based environment that has many Smalltalk-like features such as an interactive browser for developing, describing, learning about, and using large collections of ordinary C code and/or Software-ICs. Although this will make programming with C and/or Objective-C more productive, it is by design an environment for production programming and will not eliminate the need for Smalltalk-80 as a specialized prototyping environment. .H "Prototyping versus Production Programming" When a line of Smalltalk-80 code works correctly, it has successfully met several layers of requirements imposed by the Smalltalk-80 language, its run-time environment, and the programmer who wrote it. In transforming it into Objective-C code, it must meet similar requirements of the target language, environment, and programmer. These requirement layers amount to progressively higher hurdles over which the code must be carried: .np Syntactic: The first hurdle involves converting syntactically valid Smalltalk-80 statements to syntactically valid Objective-C statements. This can be done with simple tools that are concerned only the syntax of the two programming \fIlanguages\fP. .np Semantic: The second hurdle involves preserving the meaning of the code, when executed in the Smalltalk-80 environment, when it has been transformed for execution in the Objective-C environment. This requires knowledge of the two \fIenvironments\fP. .np Intentional: This is a coined term that signifies transforming code that reflects the intentions of the prototype builder to meet the intentions of one building a system for production. This requires knowledge of the intentions of the \fIprogrammers\fP themselves, or more practically, their direct involvement in one or more stages of the translation. .pp Since Objective-C's object-oriented capabilities were modeled directly after Smalltalk's, message expressions can be translated one for one into Objective-C messages. However this is of so little practical interest that it has never been attempted. After passing each of the Smalltalk classes through such a translator, implementing the entire Smalltalk virtual machine, and installing an automatic garbage collector, the translated code would have the vices of both languages and the virtues of neither. It would run no faster and it would be just as incompatible with other Unix tools. To be of practical interest, the translation process must also provide a way of providing correctness at each one of the other levels; syntactic, semantic, and intentional. This is hard to do automatically, but relatively easy if the programmer is involved in the translation. .pp For example, a Smalltalk-80 prototype might conceivably compose statements as data and compile them for execution on the fly. Although Objective-C is designed primarily as a language to be compiled in advance of program execution, several options are available for translating these prototypes. The programmer might choose to incorporate \fIVici\fP into the target system, as this would reduce labor costs at the source side. Or he might decide to modify the prototype to avoid this feature to provide better execution speed on the target side. Similarly, Smalltalk-80 implements low-level types like points (coordinates) as dynamically-bound objects, and this allows higher level code to be independent of whether points are represented as integers or floating point numbers. Objective-C's user interface library removes this freedom in favor of greater machine efficiency. Should a Smalltalk program rely on polymorphic points, its programmer could choose between developing a different user interface library that does use polymorphic points, changing the prototype to avoid the problem, or accepting a certain proportion of errors in the translation to be repaired by hand. No translation is impossible; some just cost more than others. .H "Producer" The automatic part of a translation involves a tool named `producer'. Producer is basically a Smalltalk-80 compiler that generates Objective-C code, but it differs from most compilers in that it can also accept additional information to guide how code will be generated. If this information is not provided, producer translates Smalltalk-80 statements into Objective-C code in a straightforward, non-rigorous manner. For example, it will translate each Smalltalk message expression to a syntactically equivalent Objective-C expression, but it translates literal constants into primitive C types and does nothing special to ensure that the code generated from Smalltalk block expressions is syntactically legal. .pp Producer is implemented in Objective-C and other Unix tools. Its lexical analyzer, written in \fIlex\fP, composes characters into tokens and delivers them to the parser, written in \fIyacc\fP. The parser recognizes syntactic components of the Smalltalk-80 language and executes Objective-C statements that build a syntax tree bottom-up as instances of syntactic classes like Method, Message, and Identifier. Some of these classes eliminate minor syntactic incompatibilities by rewriting the tree. For example, the Expr (expression) class eliminates cascaded message expressions by adding temporary variables to the nearest enclosing scope and builds the tree as a sequence of simple message expressions. .pp As a refinement, the lexer accumulates every token in a list that can be printed just before the code generation pass as an Objective-C comment. This preserves the original Smalltalk statements and comments in the generated code. Code generation begins when the grammar recognizes a method or class declaration and sends the top of the syntax tree (an instance of Method or Class) the message, \fIgen\fP. The method class implements this message by first requesting its subnodes to determine their types, a recursive process that involves rule processing and type inferencing. Then it begins code generation by generating tokens that Objective-C will recognize as a method declaration and requests its subnodes to do likewise. .pp In principle, translation rules could be attached to any syntactic class, but currently only the Method, Message, and Identifier classes provide the necessary hooks. Rules are typically provided in separate files that are read before the source to be translated and are parsed by special grammar productions. Identifier rules declare the type of specific identifiers and can optionally assign a new spelling for each identifier in the generated code. Most often, declarations are provided only for instance variables and method arguments since these generally provide sufficient information that types of other variables (method and block local variables) can be inferred from how they are used. .pp The Message class provides a more elaborate kind of hook that allows more than one translation to be associated with each selector (a similar hook exists in the Method class). The specific translation for such messages is chosen according to typing information that can be derived from several sources. The type of literal constants is derived automatically, and types of variables are determined by declarative rules provided by the programmer and also by type inferencing rules that are built into producer. The argument or receiver of one message is often another message expression, whose type depends on the type of its arguments, and so on recursively. Currently, translations for messages are chosen according to the selector and the type of the message's receiver and arguments (forward reasoning), but this may be extended to also consider how the result of the translated message is used (backward reasoning). .pp Blocks are translated via the usual message translation rules, triggered by arguments of type BLOCK. While many blocks can be translated as as C conditional statements and expressions (if, for, while, etc), elaborate blocks cannot be translated in this way. If experience demonstrates the need, more ambitious translations can be added, such as rewriting block bodies as C function bodies. .H "Results" The current implementation of producer consists of 1804 lines of Objective-C code, 223 of which are also processed by yacc and 105 by lex. Only three man-weeks have been invested, two in developing producer and producing the results shown here and the other in writing this paper. To date, translations have been verified by desk checking, but it should be possible to show translated applications in operation by the time this paper is presented. .pp The group that developed Smalltalk-80 has produced a video tape that Xerox uses to promote sales of Smalltalk systems. The tape concludes with a animation of three shapes bouncing in a rectangular enclosure; a circle, a rotating star, and a square that shows the area near the cursor. The tape was filmed on a Dorado, a relatively powerful computer, but the same program is also available on less expensive machines like the Tektronix Artificial Intelligence Workstation, which is based on the same Motorola 68010 chip as the Sun workstations used in most of our work. This program was chosen to demonstrate the translation process in action. .pp The animation application involves 236 lines of Smalltalk code. Its source environment is the standard Smalltalk-80 environment, and the goal is to translate it to run in a target environment that is defined by the Objective-C user interface library. This environment involves three layers of abstraction: .np The substrate layer includes the primitive data types of the C compiler, Unix, and the proprietary windowing environments of diverse workstation vendors, and is therefore not part of the library. The library confines itself to building user interfaces to run in virtual terminals provided by the windowing environment. .np The DisplayObjects level duplicates the functionality of the Smalltalk graphics environment insofar as this has been described publically\**. It provides a Form class whose instances hold rectangular raster images in memory, a DisplayScreen class (a subclass of Form) whose instances provide an object-oriented interface to a virtual terminal, and a number of specialized subclasses like InfiniteForm, OpaqueForm, Cursor, and Sensor. The classes and methods in this library are essentially identical to Smalltalk's, but they assume that objects below the level of class Rectangle are primitive types of the substrate; e.g. Points, Numbers, Booleans, and so forth. .(f \** Goldberg and Robson's \fISmalltalk-80: The language and its implementation\fP .)f .np The UserInterface level is similar to, but does not duplicate, the Smalltalk Interface-Framework classes that implement the model/view/controller (MVC) user interface paradigm. This level of the library is not compatible, partially because MVC seems unnecessarily complicated, but primarily to avoid any appearance of violating Xerox copyright protection on concepts that are documented only in Smalltalk-80 sources. Unless this restriction can be lifted, the only recourse may be to code prototypes to use a new Smalltalk library written to parallel the Objective-C library. .pp The DisplayObjects level also defines a statically typed implementation of the Point (coordinate) class as a collection of C macros and functions: .(C typedef struct { short x, y; } PT;\** // aPoint .)C This trades the advantages of polymorphic points to gain machine efficiency and compatibility with other tools in the target environment; particularly vendor-supplied windowing environments. And managing low level objects like points by value instead of by reference reduces the need for automatic garbage collection significantly. .(f \** Since many C compilers implement structure assignment inefficiently, the library coerces this type such that points are passed to and returned from functions as integers rather than as structures. This is why the x portion of aPoint is accessed as X(aPoint) rather than aPoint.x. .)f .pp The translation for this application involves 796 lines of rules, 735 of which are generic rules that describe low-level Smalltalk objects (Booleans, Numbers, etc) and 61 of which are application-specific rules that describe types in the animation application. The following is a typical translation: .(C // MovingNode subclass: #BounceInBoxNode // instanceVariableNames: '' // classVariableNames: '' // poolDictionaries: '' // category: 'Graphics-Animation'! #include "st80.h" = BounceInBoxNode:MovingNode CATEGORIES() { } // !BounceInBoxNode methodsFor: 'display'! // displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle rule: // ruleInteger mask: aForm // | relLoc | // super displayOn: destForm at: aDisplayPoint clippingBox: clipRectangle // rule: ruleInteger mask: aForm. // relLoc \(<- location + clipRectangle origin. // (velocity x < 0 and: [relLoc x < clipRectangle left]) // ifTrue: [velocity \(<- velocity*(-1@1)]. // (velocity x > 0 and: [(relLoc x+contents width) > clipRectangle right]) // ifTrue: [velocity \(<- velocity*(-1@1)]. // (velocity y < 0 and: [relLoc y < clipRectangle top]) // ifTrue: [velocity \(<- velocity*(1@-1)]. // (velocity y > 0 and: [(relLoc y+contents height) > clipRectangle bottom]) // ifTrue: [velocity \(<- velocity*(1@-1)]. - displayOn:destForm at:(PT)aDisplayPoint clipBy:clipRectangle rule:(int)ruleInteger mask:aForm { PT relLoc; [super displayOn:destForm at:aDisplayPoint clipBy:clipRectangle rule:ruleInteger mask:aForm]; relLoc = ptPlus(location, [clipRectangle origin]); if (X(velocity) < 0 && X(relLoc) < [clipRectangle left]) velocity = ptTimes(velocity, pt(-1,1)); if (X(velocity) > 0 && X(relLoc) + [contents width] > [clipRectangle right]) velocity = ptTimes(velocity, pt(-1,1)); if (Y(velocity) < 0 && Y(relLoc) < [clipRectangle top]) velocity = ptTimes(velocity, pt(1,-1)); if (Y(velocity) > 0 && Y(relLoc) + [contents height] > [clipRectangle bottom]) velocity = ptTimes(velocity, pt(1,-1)); return self; } .)C .pp The original Smalltalk statements are shown in the comments preceeding each block of translated code. The translation of the first few lines in the method were influenced primarily by rules from a file of application-specific information, of which three are shown here: .(C { # location (PT) location } { # velocity (PT) velocity } { (id)displayOn:(id) at:(PT) clippingBox:(id) rule:(int) mask:(id) # (id) [displayOn:%1 at:(PT)%2 clipBy:%3 rule:(int)%4 mask:%5] } .)C The two instance variables, location and velocity, were inherited from another application-specific class, PositionNode. The first two rules declare that they are both of type PT (Point) and should be translated without change in spelling. The third rule specifies that methods and messages whose selector is displayOn:at:clippingBox:rule:mask: and whose receiver and arguments type id, id, PT, id, int and id respectively, should be translated into a message with the modified selector displayOn:at:clipBy:rule:mask: and with argument types and positions as denoted in the right hand part of the rule. No declaration was provided for the local variable relLoc, so its type was inferred from the type of the first statement that assigns it a value, namely .(C relLoc \(<- location + clipRectangle origin. .)C The translation of this statement was determined by two rules from a different file of generic rules about low-level Smalltalk-80 objects. .(C { # (PT) [origin] } { (PT)+ (PT) # (PT)'ptPlus(%0, %1)' } .)C The first rule denotes that the origin message, applied to an receiver of any type, returns a Point, and the second that the + message sent to an receiver of type Point with an argument of the same type translates as a call on the C function ptPlus which returns a value of type PT. .pp Currently, producer does nothing special for blocks, aside from giving them a unique type (BLOCK) that can be tested by ordinary message rules. For example, the `if' statements were produced by such a rule: .(C { ifTrue:(BLOCK) # (STMT)'if (%0) %1' } .)C .H "Conclusions" The primary conclusion that can be drawn from the results to date is that small Smalltalk applications that do not lean heavily on complex features of the Smalltalk environment can be translated to Objective-C with remarkably little trouble. Only two man-weeks sufficed to build a reasonably effective translation tool and a generic collection of rules that could translate a 263 line Smalltalk program into Objective-C with only 61 lines of application specific information. Once the application is operational, it may be possible to draw additional conclusions about the overall merit of the translation concept, for example by comparing the animation example's performance before and after translation. .pp This work has also identified several limitations in how the translation program is currently implemented, but it is too early to know whether they are serious enough to warrant fixing. For example, the scheme used for handling Smalltalk-80 block expressions works primarily because of serendipity, not because Smalltalk blocks are equivalent to any construct that C provides. Experience may prove that Smalltalk-80 programmers usually use blocks just as C programmers use simple conditional statements, in which case there would be little need for anything more elaborate, particularly since misses are automatically flagged as syntax errors when the generated code is compiled and could be repaired by hand. A number of more elaborate schemes are also available, up to and including a direct implementation of Smalltalk's scheme, and it is too soon to say which of these will prove sufficient. .pp Several limitations have been identified in how rules are currently processed. The fundamental restriction is that rules can only define data which is interpreted by hard-coded logic, so it is not possible to write rules that recognize and translate entities larger than an individual message expression. For example, the current scheme could not eliminate wasteful Smalltalk idioms like `Form under' by replacing the message with a constant, because this would involve examining not only the type of the message's receiver, but also its name. For much the same reason, it could not eliminate the unnecessary function call in the body of the first `if' statement by translating it to: .(C X(velocity) *= -1;\** .)C .(f \** X() and Y() are C macros, not functions. They are L-values and can be used as the target of assignment statements. .)f If this restriction proves too limiting in practice, it could be relieved by incorporating a general-purpose rule interpreter such as Prolog. .pp There are a number of smaller problems that should certainly be repaired. Currently, rules have global scope and they should instead be attached to specific classes, methods, or blocks to provide finer control over how identifier spellings and types are assigned. Identifier rules should be organized into a type hierarchy, and types should be compared by something more sophisticated than mere equality of their names. These enhancements would increase the modularity and generality of the translation rules, but would not influence the range of translations that producer can perform. .pp The final result is that we have received the encouragement that we need to pursue this idea further. Translation does appear to be a viable way of integrating the best of two worlds into a comprehensive hybrid programming environment that spans a much larger segment of the software development life-spiral than ever before. \Rogue\Monster\ else echo "will not over write ./producer.me" fi echo "Finished archive 2 of 5" exit ---- Dieter H. Zebbedies ('dee-ter ayech 'zeb-ed-eez) Zebb-Hoff Mach. Tool's Automated Manufacturing Project Cleveland, OH (USnail): 9535 Clinton Rd, Cleveland, OH 44144 (+216 631 6100) (+216 741-5994) (UUCP): ...{decvax,sun,cbosgd}!cwruecmp!zhmti!dieter (CSNET/ARPA/BITNET): dieter@CWRU.EDU
dietz@zhmti.UUCP (Dieter H. Zebbedies) (08/20/87)
"Producer", A package to translate Smalltalk-80 code to your favorite object oriented language, Objective-C. #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./src` then mkdir ./src echo "mkdir ./src" fi if `test ! -s ./src/Type.m` then echo "writting ./src/Type.m" cat > ./src/Type.m << '\Rogue\Monster\' #include "Producer.h" TYPE types = { 0 }; LOCAL id knownTypes; USE Set; = Type : Constant CATEGORIES() + initialize { static BOOL beenHere; if (!beenHere) { beenHere = YES; self = Type; knownTypes = [Set new]; // C base types types.ID = [self str:"id"]; types.CHAR = [self str:"char"]; types.SHORT = [self str:"short"]; types.INT = [self str:"int"]; types.LONG = [self str:"long"]; types.FLOAT = [self str:"float"]; types.DOUBLE = [self str:"double"]; // Common derived types types.CSTRING = [self str:"STR"]; types.POINT = [self str:"PT"]; types.RECTANGLE = [self str:"RT"]; // Language types types.BLOCK = [self str:"BLOCK"]; types.STMT = [self str:"STMT"]; types.SELECTOR = [self str:"SEL"]; types.SHARED = [self str:"SHR"]; // Misc types.UNKNOWN = [self str:"unknown"]; types.ANY = [self str:"any"]; } return self; } - type { return self; } =: \Rogue\Monster\ else echo "will not over write ./src/Type.m" fi if `test ! -s ./src/gen.m` then echo "writting ./src/gen.m" cat > ./src/gen.m << '\Rogue\Monster\' #include "Producer.h" #include "stdio.h" #include "ctype.h" = CATEGORIES() LOCAL unsigned indent = 0; LOCAL BOOL bol = YES; LOCAL IOD genFD = stdout; LOCAL STR yyfilename = "<stdin>"; // Debug option EXPORT void po(o) id o; { [o show]; } EXPORT void dbgo(o) id o; { IMPORT BOOL dbgFlag; if (dbgFlag) [o show]; } EXPORT int yywrap() { IMPORT unsigned yylineno; yylineno = 1; return 1; } // stockpile the last n tokens for printing in error messages #define SIZ 30 LOCAL char minPt[SIZ+10] = ""; // slop LOCAL STR inPt = &minPt[SIZ-1], maxPt = &minPt[SIZ-1]; EXPORT void stockToken(s) STR s; { if (!s) return; if (isalnum(*inPt) && isalnum(*s)) { if (inPt >= maxPt) inPt = minPt; *inPt++ = ' '; } while(*s) { if (inPt >= maxPt) inPt = minPt; *inPt++ = *s++; } } LOCAL printToken(iod) IOD iod; { STR outPt, stopPt; if (inPt >= maxPt) { outPt = minPt; stopPt = maxPt-1; } else { outPt = inPt; stopPt = inPt-1; } for(;;) { if (outPt >= maxPt) outPt = minPt; putc(*outPt ? *outPt : ' ', iod); if (outPt++ == stopPt) break; } } EXPORT IOD yyopen(file, mode, iod) STR file, mode; IOD iod; { IMPORT IOD yyin; IMPORT unsigned yylineno; yylineno = 1; return freopen(yyfilename = file, mode, yyin=stdin); } EXPORT int errorCount = 0; EXPORT void wer(fmt, arg) STR fmt, arg; { IMPORT unsigned yylineno; fflush(stdout); fflush(genFD); fflush(stderr); fprintf(stderr, "error: "); _doprnt(fmt, &arg, stderr); fprintf(stderr, "\n"); fflush(stdout); fflush(genFD); fflush(stderr); errorCount++; } EXPORT void yyerror(fmt, arg) STR fmt, arg; { IMPORT unsigned yylineno; fflush(stdout); fflush(genFD); fflush(stderr); fprintf(stderr, "error %3d:%s: ", yylineno, yyfilename); printToken(stderr); fprintf(stderr, " : "); _doprnt(fmt, &arg, stderr); fprintf(stderr, "\n"); fflush(stdout); fflush(genFD); fflush(stderr); errorCount++; } EXPORT BOOL infoFlag = NO; EXPORT void info(fmt, arg) STR fmt, arg; { IMPORT unsigned yylineno; if (!infoFlag) return; fflush(stdout); fflush(genFD); fflush(stderr); fprintf(stderr, "fyi %3d:%s: ", yylineno, yyfilename); _doprnt(fmt, &arg, stderr); fflush(stderr); } // String copy EXPORT STR strCopy(s) STR s; { return (STR)strcpy(malloc(strlen(s)+1), s); } // fopen(genFD) EXPORT void genOpen(className) STR className; { if (genFD != stdout) fclose(genFD); genFD = fopen(className, "w"); indent = 0; bol = YES; if (!genFD) { fprintf(stderr, "cannot open %s for writing ", className); perror(""); exit(-1); } } EXPORT void genReset() { indent = 0; bol = YES; } // Generate: formatted, string, newline, char EXPORT void gc(c) { BOOL eol = NO, discardSemi = NO; switch(c) { case '\n': case '\r': case '\f': newline(); return; case '{': eol = YES; discardSemi = NO; break; case '}': indent--; eol = YES; discardSemi = YES; newline(); break; case '(': indent++; discardSemi = NO; break; case ')': indent--; discardSemi = NO; break; case ';': if (discardSemi) return; eol = YES; discardSemi = YES; break; default: discardSemi = NO; break; } if (bol) idn(); putc(c, genFD); if (eol) newline(); if (c == '{'/*}*/) indent++; } EXPORT void gf(s, arg) STR s, arg; { _doprnt(s, &arg, genFD); } EXPORT void gs(s) STR s; { while(*s) gc(*s++); } EXPORT void gn() { bol = NO; newline(); } LOCAL idn() { unsigned i; for (i = indent; i-- != 0; ) putc('\t', genFD); bol = NO; } LOCAL newline() { if (!bol) putc('\n', genFD); bol = YES; } \Rogue\Monster\ else echo "will not over write ./src/gen.m" fi if `test ! -s ./src/main.m` then echo "writting ./src/main.m" cat > ./src/main.m << '\Rogue\Monster\' #include "stdio.h" #include "Producer.h" = CATEGORIES() LOCAL BOOL showMsgTranslationFlag, showIdTranslationFlag; STR *parseArguments(argc, argv) STR *argv; { unsigned i; USE Object; IMPORT BOOL dbgFlag, msgFlag, allocFlag, printFlag, lexFlag, infoFlag, autoFileFlag, stripCommentsFlag, infoFlag; IMPORT IOD dbgIOD; // settrap(); // trap interrupts [Object self]; // trigger initializes now dbgIOD = stderr; for (i = 1; i < argc; i++) { STR arg = argv[i]; static char usage[]="usage: %s [-(dmaplgsci)] files .."; if (*arg == '-') switch (*++arg) { case 'd': dbgFlag = YES; break; case 'm': msgFlag = YES; break; case 'a': allocFlag = YES; break; case 'p': printFlag = YES; break; case 'l': lexFlag = YES; break; case 'g': autoFileFlag = YES; break; case 's': stripCommentsFlag = YES; break; case 'c': stripCommentsFlag = NO; break; case 'i': infoFlag = YES; break; case 'M': showMsgTranslationFlag = YES; break; case 'I': showIdTranslationFlag = YES; break; default: fprintf(stderr, usage, argv[0]); bye(-2); } else return &argv[i]; } return 0; } main(argc, argv) STR *argv; { USE Comment; IMPORT unsigned errorCount; STR *file = parseArguments(argc, argv); if (file) for (; *file; file++) { if (yyopen(*file, "r", stdin)) yyparse(); else wer("cannot open %s\n", *file); } else yyparse(); bye(errorCount != 0); } // Exit here. bye(n) { static BOOL beenHere = 0; IMPORT id msgTranslator, identifierTranslator; if (beenHere++) exit(n); if (showMsgTranslationFlag) { printf("msgTranslator============================\n"); [msgTranslator show]; } if (showIdTranslationFlag) { printf("identifierTranslator============================\n"); [identifierTranslator show]; } exit(n); } @class(String, Block, ByteArray, Return, Selector, Comment, Stmt, AsciiFiler, Expr, Msg, StArray, Method, List, IdArray, Sequence, Cltn, Set, IntArray, Class, Identifier, MsgArgPattern, MsgNamePattern, MsgTranslator, AbstractTranslation, FunctionTranslation, MsgTranslation, StringTranslation, IdentifierTranslation, CharConstant, Constant, NumberConstant, SelectorConstant, StringConstant, Template, Type, ArgumentList, Scope) @phyla CATEGORIES() \Rogue\Monster\ else echo "will not over write ./src/main.m" fi if `test ! -s ./src/Producer.h` then echo "writting ./src/Producer.h" cat > ./src/Producer.h << '\Rogue\Monster\' #ifndef PRODUCER_H # include "Substrate.h" # undef CATEGORIES #ifndef COXLIB # define CATEGORIES() (Producer, Collection, Primitive) #else # define CATEGORIES() (Producer, Substrate, Primitive) #endif # define ARYSIZ(x) (sizeof(x)/sizeof(*x)) # define strEq(a, b) (strcmp(a, b) == 0) typedef struct _TYPES { id DEFAULT, ID, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE; id CSTRING, POINT, RECTANGLE; id BLOCK, STMT; id SELECTOR, SHARED; id UNKNOWN, ANY; } TYPE; IMPORT TYPE types; # define AbstractTranslation prAbstractTranslation # define ArgumentList prArgumentList # define Block prBlock # define CharConstant prCharConstant # define Class prClass # define Comment prComment # define Constant prConstant # define Expr prExpr # define FunctionTranslation prFunctionTranslation # define Identifier prIdentifier # define IdentifierTranslation prIdentifierTranslation # define List prList # define Method prMethod # define Msg prMsg # define MsgArgPattern prMsgArgPattern # define MsgNamePattern prMsgNamePattern # define MsgTranslation prMsgTranslation # define MsgTranslator prMsgTranslator # define Node prNode # define NumberConstant prNumberConstant # define Return prReturn # define Scope prScope # define Selector prSelector # define SelectorConstant prSelectorConstant # define StArray prStArray # define Stmt prStmt # define StringConstant prStringConstant # define StringTranslation prStringTranslation # define Template prTemplate # define Type prType #define PRODUCER_H #endif \Rogue\Monster\ else echo "will not over write ./src/Producer.h" fi if `test ! -s ./src/st80.h` then echo "writting ./src/st80.h" cat > ./src/st80.h << '\Rogue\Monster\' #ifndef PRODUCERTEST_H # include "Layers.h" # define ARYSIZ(x) (sizeof(x)/sizeof(*x)) # define strEq(a, b) (strcmp(a, b) == 0) # undef CATEGORIES() # define CATEGORIES() (ProducerTest, Collecting, Primitive) # define unknown id # define PRODUCERTEST_H #endif \Rogue\Monster\ else echo "will not over write ./src/st80.h" fi if `test ! -s ./src/y.tab.h` then echo "writting ./src/y.tab.h" cat > ./src/y.tab.h << '\Rogue\Monster\' typedef union TYP { id O; char B; char *S; } YYSTYPE; extern YYSTYPE yylval; # define IDENTIFIER 257 # define DIGITS 258 # define KEYWORD 259 # define STRING 260 # define CHARCON 261 # define DOUBLESPECIAL 262 # define _ 95 # define VariableIdentifier 263 \Rogue\Monster\ else echo "will not over write ./src/y.tab.h" fi if `test ! -s ./src/gram.y` then echo "writting ./src/gram.y" cat > ./src/gram.y << '\Rogue\Monster\' %{ /* Grammar for Smalltalk-80 * 3 shift/reduce, 0 reduce/reduce conflicts */ #include <stdio.h> #include "objc.h" #include "Producer.h" = CATEGORIES() USE Block, Expr, List, Array, Type, StArray, Msg, Method, Return, Selector, Stmt, Identifier, IdentifierTranslation, StringTranslation, FunctionTranslation, NumberConstant, SelectorConstant, CharConstant, StringConstant, MsgTranslation, Template, ArgumentList, Class, Comment; EXPORT BOOL printFlag, isFactory = NO; IMPORT id findSymbol(); LOCAL id thisClass = nil; IMPORT id msgTranslator; %} %union TYP { id O; char B; char *S; } %type <O> LocalVariables VarList PrimMarker Type Method MethodName Keyword %type <O> KwdMethodDecl StmtList Expr AssignmentList Primary %type <O> UnaryObjDesc BinaryObjDesc SimpleMsgExpr UnaryExpr BinaryExpr %type <O> KeywordExpr KeywordArgList CascadedMsgExpr CascadedMsg %type <O> Literal ArrayMemberList ArrayMember Block BinarySelector %type <O> VariableName UnarySelector Template BlockVarList KeywordList %type <O> InferenceRule ObjcFunctionArgList ObjcFunctionPattern BlockVariables %type <O> ObjcKeywordPattern ObjcMsgPattern OptionalType %type <O> ParameterDesignator KeywordPattern PatternType TemplateType %type <O> CharacterConstant NumberConstant StringConstant Keyword %type <B> SpecialCharacter %token <S> IDENTIFIER DIGITS KEYWORD STRING CHARCON DOUBLESPECIAL %token <B> '%' '|' '&' '?' '#' '!' ',' '_' %token <B> '+' '-' '/' '\\' '*' '~' '<' '>' '=' '@' %left '_' %left IDENTIFIER STRING UnarySelector VariableIdentifier %right KEYWORD BinarySelector %start ChunkList %% ChunkList: | ChunkList Chunk { USE Comment; [Comment gen]; } ; Chunk: VariableName KEYWORD '#' VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant '!' { [Comment gen]; if (thisClass) { [thisClass gen]; [thisClass free]; } thisClass = [Class name:findSymbol($4)]; [thisClass superclass:findSymbol($1)]; [thisClass instanceVariableNames:$6]; [thisClass classVariableNames:$8]; [thisClass poolDictionaries:$10]; [thisClass category:$12]; [thisClass gen]; } | VariableName KEYWORD STRING '!' {isFactory=NO;} Methods Sep | VariableName VariableName KEYWORD STRING '!' {isFactory=YES;} Methods Sep | '{' InferenceRule '}' { [Comment free]; } | '{' error '}' | error '!' ; Sep: '!' | Sep '!' ; Methods: | Methods Method '!' { [Comment gen]; [$2 gen]; [$2 free]; } | Methods error '!' { [Comment gen]; } ; /* Declare a new method */ Method: MethodName LocalVariables { [$$=$1 variables:$2]; } PrimMarker StmtList { [$$ primitive:$4]; [$$ statements:$5]; } ; MethodName: UnarySelector { $$ = [Method selector:$1 asFactory:isFactory]; } | BinarySelector VariableName { $$ = [Method selector:[$1 argument:$2] asFactory:isFactory]; } | KwdMethodDecl { $$ = [Method selector:$1 asFactory:isFactory]; } ; PrimMarker: { $$ = nil; } | '<' KEYWORD NumberConstant '>' { $$ = [NumberConstant sprintf:"primitive(%s);", [$3 str]]; [$3 free]; } ; LocalVariables: { $$ = nil; } | '|' VarList '|' { $$ = $2; } ; VarList: { $$ = nil; } | VarList VariableName { $$ = $1 ? $1 : [List new]; [$$ add:$2]; } ; KwdMethodDecl: Keyword VariableName { [$$=$1 argument:$2]; } | KwdMethodDecl Keyword VariableName { [$$=$1 add:[$2 argument:$3]]; } ; /* Statements and expressions */ StmtList: '^' Expr { $$ = [Return expr:$2]; } | Expr { $$ = [Stmt expr:$1]; } | Expr '.' { $$ = [Stmt expr:$1]; } | Expr '.' StmtList { [$$=[Stmt expr:$1] successor:$3]; } | error '.' { wer("erroneous statement ignored"); $$=nil; } ; Expr: AssignmentList Primary { $$ = [Expr assign:$1 value:$2]; } | AssignmentList SimpleMsgExpr { $$ = [Expr assign:$1 value:$2]; } | AssignmentList SimpleMsgExpr CascadedMsgExpr { [$$ = [Expr assign:$1 value:$2] cascade:$3]; } ; AssignmentList: { $$ = nil; } | AssignmentList VariableName '_' { $$ = $1 ? $1 : [List new]; [$$ add:findSymbol($2)]; } ; CascadedMsgExpr: CascadedMsg { $$=[Expr assign:nil value:$1]; } | CascadedMsgExpr CascadedMsg { [$$=$1 add:[Expr assign:nil value:$2]]; } ; CascadedMsg: ';' UnarySelector { $$ = [Msg selector:$2]; } | ';' BinarySelector UnaryObjDesc { $$ = [Msg selector:[$2 argument:$3]]; } | ';' KeywordArgList { $$ = [Msg selector:$2]; } ; Primary: VariableName { $$ = findSymbol($1); } | Literal | Block | '(' Expr ')' { $$ = $2; } ; SimpleMsgExpr: UnaryExpr | BinaryExpr | KeywordExpr ; UnaryObjDesc: Primary | UnaryExpr ; UnaryExpr: UnaryObjDesc UnarySelector { $$ = [Msg receiver:$1 selector:$2]; } ; BinaryObjDesc: UnaryObjDesc | BinaryExpr ; BinaryExpr: BinaryObjDesc BinarySelector UnaryObjDesc { $$ = [Msg receiver:$1 selector:[$2 argument:$3]]; } ; KeywordExpr: BinaryObjDesc KeywordArgList { $$ = [Msg receiver:$1 selector:$2]; } ; KeywordArgList: Keyword BinaryObjDesc { [$$=$1 argument:$2]; } | KeywordArgList Keyword BinaryObjDesc { [$$=$1 add:[$2 argument:$3]]; } ; Literal: CharacterConstant | StringConstant | NumberConstant | '#' '(' ArrayMemberList ')' { $$ = $3; } | '#' UnarySelector { $$=[SelectorConstant name:$2]; [$2 free]; } | '#' BinarySelector { $$=[SelectorConstant name:$2]; [$2 free]; } | '#' KeywordList { $$=[SelectorConstant name:$2]; [$2 free]; } ; ArrayMemberList: { $$ = nil; } | ArrayMemberList ArrayMember { [$$ = $1 ? $1 : [StArray new] add:$2]; } ; ArrayMember: CharacterConstant | StringConstant | NumberConstant | UnarySelector | BinarySelector | KeywordList | '(' ArrayMemberList ')' { $$ = $2; } ; Block: '[' BlockVariables StmtList ']' { $$ = [$2 statements:$3]; } | '[' BlockVariables ']' { $$ = $2; } ; BlockVariables: { $$ = [Block new]; } | BlockVarList '|' { $$ = [[Block new] variables:$1]; } ; BlockVarList: ':' VariableName { [$$ = [List new] add:$2]; } | BlockVarList ':' VariableName { [$$=$1 add:$3]; } ; /* Type inferencing rules */ InferenceRule: '#' PatternType '[' ObjcMsgPattern ']' { [msgTranslator install:[Template receiverType:[$4 receiverType] selector:[$4 selector]] translation:[$4 type:$2]]; } | Template '#' PatternType '[' ObjcMsgPattern ']' { [msgTranslator install:$1 translation:[$5 type:$3]]; } | Template '#' PatternType ObjcFunctionPattern { [msgTranslator install:$1 translation:[$4 type:$3]]; } | Template '#' PatternType StringConstant { [msgTranslator install:$1 translation:[StringTranslation type:$3 translation:$4]]; } | '#' VariableName PatternType VariableName { [IdentifierTranslation sourceName:$2 targetType:$3 targetName:$4]; } ; Template: TemplateType UnarySelector { $$ = [Template receiverType:$1 selector:$2]; } | TemplateType BinarySelector TemplateType { $$ = [Template receiverType:$1 selector:[$2 type:$3]]; } | TemplateType KeywordPattern { $$ = [Template receiverType:$1 selector:$2]; } ; KeywordPattern: Keyword PatternType { [$$=$1 type:$2]; } | KeywordPattern Keyword PatternType { [$$=$1 add:$2]; [$2 type:$3]; } ; ObjcFunctionPattern: VariableName '(' ObjcFunctionArgList ')' { $$ = [FunctionTranslation name:$1 args:$3]; } | VariableName '(' ')' { $$ = [FunctionTranslation name:$1 args:0]; } ; ObjcFunctionArgList: PatternType ParameterDesignator { $$ = [ArgumentList type:$1 name:$2]; } | ObjcFunctionArgList ',' PatternType ParameterDesignator { [$$=$1 add:[ArgumentList type:$3 name:$4]]; } ; ObjcMsgPattern: PatternType UnarySelector { $$ = [MsgTranslation receiverType:$1 selector:$2]; } | PatternType ObjcKeywordPattern { $$ = [MsgTranslation receiverType:$1 selector:$2]; } ; ObjcKeywordPattern: Keyword PatternType ParameterDesignator { [$1 type:$2]; [$$=$1 argument:$3]; } | ObjcKeywordPattern Keyword PatternType ParameterDesignator { [$$=$1 add:$2]; [$2 type:$3]; [$2 argument:$3]; } ; ParameterDesignator: VariableName | '%' DIGITS { $$=[NumberConstant sprintf:"%%%s", $2]; } ; /* Token Packaging */ KeywordList: KEYWORD { $$ = [Selector str:$1]; } | KeywordList KEYWORD { $$ = [$1 concatenateSTRAndFreeReceiver:$2]; } ; BinarySelector: '-' { $$ = [Selector str:"-"]; } | SpecialCharacter { char b[2]; b[0]=$1; b[1]=0; $$ = [Selector str:b]; } | DOUBLESPECIAL { $$ = [Selector str:$1]; } ; SpecialCharacter: '+' | '/' | '\\' | '*' | '~' | '<' | '>' | '=' | '@' | '%' | '&' | '?' | ',' | '|' ; PatternType: OptionalType { $$ = $1 ? $1 : types.ID; }; TemplateType: OptionalType { $$ = $1 ? $1 : types.ANY; }; OptionalType: { $$ = 0; } | '(' Type ')' { $$ = $2; }; Type: IDENTIFIER { $$=[Type str:$1]; }; VariableName: IDENTIFIER { $$=[Identifier str:$1]; }; UnarySelector: IDENTIFIER { $$ = [Selector str:$1]; }; CharacterConstant: CHARCON { $$ = [CharConstant str:$1]; }; NumberConstant: DIGITS { $$ = [NumberConstant str:$1]; } | '-' DIGITS %prec KEYWORD { $$ = [NumberConstant sprintf:"-%s", $2]; } ; StringConstant: STRING { $$ = [StringConstant str:$1]; }; Keyword: KEYWORD { $$ = [Selector str:$1]; }; %% \Rogue\Monster\ else echo "will not over write ./src/gram.y" fi if `test ! -s ./src/lex.l` then echo "writting ./src/lex.l" cat > ./src/lex.l << '\Rogue\Monster\' %{ #include "Producer.h" #include "y.tab.h" USE Comment; LOCAL STR collect(); EXPORT BOOL lexFlag = NO; IMPORT BOOL stripCommentsFlag; #ifndef OBJC_COX = CATEGORIES() #endif /* Intrudes between lexer and parser for token-level debugging */ EXPORT int yylex() { STR typeStr, tokenValue; LOCAL char b[2]={0}; int type = rawlexer(); switch(type) { case DIGITS: typeStr="DIGITS"; tokenValue = yylval.S; break; case KEYWORD: typeStr="KEYWORD"; tokenValue = yylval.S; break; case IDENTIFIER: typeStr="IDENTIFIER"; tokenValue = yylval.S; break; case STRING: typeStr="STRING"; tokenValue = yylval.S; break; case CHARCON: typeStr="CHARCON"; tokenValue = yylval.S; break; default: b[0] = yylval.B; typeStr = tokenValue = b; break; } stockToken(tokenValue); if (lexFlag) printf("::%3d=%10s: %s\n", type, typeStr, tokenValue); return type; } LOCAL STR bitsToHex(s) STR s; { LOCAL char buf[80]; LOCAL char charToHex[]="0123456789abcdef"; unsigned short out = 0; STR p; int shiftBy; p = buf; *p++ = '0'; *p++ = 'x'; for (shiftBy = 3; *s; shiftBy--,s++) { out |= (unsigned)(*s != '0') << shiftBy; if (shiftBy == 0) { *p++ = charToHex[out]; out = 0; shiftBy = 3; } } *p = 0; return buf; } GETC(iod) IOD iod; { LOCAL char buf[BUFSIZ] = {0}; LOCAL STR p = buf; if (*p == 0) { p = buf; *p = 0; if (fgets(buf, sizeof(buf), iod) == NULL) { STR q = &buf[strlen(buf)]; *q++ = EOF; *q = 0; } if (!stripCommentsFlag) [Comment str:buf]; } return *p++; } # undef input # define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):GETC(yyin))==10?\ (yylineno++,yytchar):yytchar)==EOF?0:yytchar) # define yylex rawlexer %} %% [ \t\n\f\r]+ { ; } "\"" { yylval.S = collect(yyleng); } "'" { yylval.S = collect(yyleng); return STRING; } [A-Za-z][A-Za-z0-9]* { yylval.S = yytext; return IDENTIFIER; } [A-Za-z][A-Za-z0-9]*: { yylval.S = yytext; return KEYWORD; } [+/\\*~<>=@%|&?][+/\\*~<>=@%|&?] { yylval.S = yytext; return DOUBLESPECIAL; } [0-9]+ | [0-9]*\.[0-9]+ { yylval.S = yytext; return DIGITS; } 2r[0-1]+ { yylval.S = bitsToHex(&yytext[2]); return DIGITS; } \$. { yylval.S = &yytext[1]; return CHARCON; } \$!! { yylval.S = "!"; return CHARCON; } . { return yylval.B = *yytext; } %% /* Collect a "String" 'CharacterConstant', #command or comment * The item to be collected is identified (in `what') from the * first character in yytext. * * To save copying time, we collect as much as will fit directly in yytext[]. * An instance of String is then created to hold any overflows. The str * returned from here will locate the result, whether held * in yytext or aString. */ LOCAL STR collect(len) register short len; { USE ByteArray; register short c; LOCAL id overflow = 0; char what = *yytext; if (overflow) overflow = [overflow free]; /* from last time around */ for(;;) { if (len >= YYLMAX - 1) { yytext[len] = 0; len = 0; if (overflow) overflow = [overflow concatenateSTR:yytext]; else overflow = [ByteArray str:yytext]; } switch(yytext[len++] = c = input()) { case '\'': if (what == '\'') { if ((c = input()) == '\'') { yytext[len++] = c; continue; } else { unput(c); break; } } else continue; case '"': if (what == '"') break; else continue; case '*': if (what != '/') continue; if ((c = input()) == '/') { yytext[len++] = c; break; } unput(c); continue; case '\\': yytext[len++] = input(); continue; case '\n': continue; case 0: wer("Unterminated string or charconst"); break; default: continue; } yytext[len] = 0; return overflow ? [(overflow = [overflow concatenateSTR:yytext]) str] : yytext; } } \Rogue\Monster\ else echo "will not over write ./src/lex.l" fi if `test ! -s ./src/Makefile` then echo "writting ./src/Makefile" cat > ./src/Makefile << '\Rogue\Monster\' # Producer: The Smalltalk to ObjectiveC Translator OCOBJ= Node.o List.o Block.o Comment.o \ ByteArray.o \ Symbol.o \ Constant.o \ Type.o \ CharConstant.o \ NumberConstant.o \ SelectorConstant.o \ StringConstant.o \ Expr.o StArray.o Class.o \ Msg.o Method.o Stmt.o Return.o \ MsgArgPattern.o \ MsgNamePattern.o \ MsgTranslator.o \ Template.o \ Selector.o \ ArgumentList.o \ Identifier.o \ IdentifierTranslation.o \ AbstractTranslation.o \ FunctionTranslation.o \ MsgTranslation.o \ StringTranslation.o \ Scope.o YACCOBJ=gram.o LEXOBJ=lex.o CCOBJ=gen.o main.o OBJ=$(OCOBJ) $(YACCOBJ) $(LEXOBJ) $(CCOBJ) LIBS=/u/ui/sb/Substrate.a OBJC=objcc -q -g MISC=main.m gram.y lex.l gen.m Producer.h SRC=README Makefile $(MISC) `_suffix .m $(OCOBJ) $(CCOBJ) | tr ' ' '\012' | sort` .SUFFIXES: .SUFFIXES: .y .l .m .o .c .me .i .f all: METHODDECLS.o producer producer: $(OBJ); $(OBJC) $(OBJ) && mv a.out producer clean: ; rm -f *.o *.c [cpCP]_* log core gram.m lex.m .y.o: ; yacc -dv $< && mv y.tab.c $*.m && $(OBJC) -c $*.m .l.o: ; lex $< && mv lex.yy.c $*.m && $(OBJC) -c $*.m .m.o: ; $(OBJC) -c -nRetain $< .m.c: ; $(OBJC) -c $< .me.i: ; me -i -t /u/cox/src/mac.me $< .me.f: ; me -t /u/cox/src/mac.me $< >$$$$.f && mv $$$$.f $*.f v.m: v.st ; -producer RULES/* v.st >v.m v.o: v.m ; objc v.m -c -Retain -g -q -I../wg -I../ui $C test: ; rm -f v.m; make v.o wc: ; wc $(SRC) \Rogue\Monster\ else echo "will not over write ./src/Makefile" fi if `test ! -s ./src/design.me` then echo "writting ./src/design.me" cat > ./src/design.me << '\Rogue\Monster\' Smalltalk Syntax consists of identifiers literal constants message expressions Return type Argument types Literal constant typing is explicit from context Integer Floating Point String Character Identifier type inferencing Identifier name transformation: How to specify scope of change? The type of each identifier is the type of the first assignment. Instance variables: generate declaration at tail of file after assignments have provided a type for each? Inherited variables: do these appear in methods of Smalltalk subclasses? Message expression type inferencing (int) do:(TYPE) this:(TYPE) to:(TYPE) that:(TYPE) Return type and argument types are taken from category files after performing any message name transforms Method type inferencing When Objective-C method exists by the same name, use types from that Otherwise the type is determined after inferencing variable types. Static polymorphism When translation of messages should depend on the (static) type of the receiver; e.g. (10@20) extent:(30@40) -> [Rectangle origin:pt(10,20) extent:(30@40)] aRectangle extent:(30@40) -> [Rectangle extent:pt(30@40)] or aPoint x -> X(aPoint) aRectangle origin -> aRectangle->origin aRectangle left -> L(aRectangle) or better yet 2 + 3 -> 2+3 2.3 + 4.8 -> 2.3 + 4.8 aPoint + anotherPoint -> ptPlus(aPoint, anotherPoint) aPoint + 3 -> ptPlusInt(aPoint, 3) Notation: ReceiverType MessageExpression -> ReturnType FormatString \________Source_____________/ \______Target_________/ Default: ReceiverType: matches id ReturnType: id FormatString: message expression Examples: (int)+(int) -> $1 + $2 (float)+(float) -> $1 + $2 (PT)+(PT) -> ptPlus($1, $2) (PT)+(int) -> ptPlusInt($1, $2) \Rogue\Monster\ else echo "will not over write ./src/design.me" fi if `test ! -s ./src/Symbol.m` then echo "writting ./src/Symbol.m" cat > ./src/Symbol.m << '\Rogue\Monster\' /*{ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Symbol My instances in only a single copy, allowing them to be tested for equality via == instead of isEqual: bjc: A prototype; not tested under load. The hash and isEqual: methods have broken since I was here last (library incompatibilities?) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }*/ #include "Producer.h" USE Set; = Symbol:ByteArray CATEGORIES() { } LOCAL id symbolTable; + initialize { if (!symbolTable) symbolTable = [Set new]; } + symbolTable { return symbolTable; } + str:(STR)aStr { return [symbolTable filter:[super str:aStr]]; } + name:aByteArray { return [self str:[aByteArray str]]; } - free { return nil; } - str:(STR)aStr { return [self cannotModifyError]; } - (char)charAt:(unsigned)anOffset put:(char)aChar { return (char)[self cannotModifyError]; } - sort { return [self cannotModifyError]; } - concat:aStr { return [self cannotModifyError]; } - concatSTR:(STR)aCString { return [self cannotModifyError]; } - concatenateAndFreeReceiver:aByteArray { return [self cannotModifyError]; } - concatenateSTRAndFreeReceiver:(STR)aString { return [self cannotModifyError]; } - concatenateSTR:(STR)aString { return [self cannotModifyError]; } - concatenate:aByteArray { return [self cannotModifyError]; } - cannotModifyError { return [self error:"the bytes in a %s are invarient", [self name]]; } #ifdef NOTWORKING - (unsigned)hash { return (unsigned)self; } - (BOOL)isEqual:anObject { return self == anObject; } #endif =: \Rogue\Monster\ else echo "will not over write ./src/Symbol.m" fi if `test ! -s ./src/ByteArray.m` then echo "writting ./src/ByteArray.m" cat > ./src/ByteArray.m << '\Rogue\Monster\' /*{ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ByteArray.m Do not be misled by the superficial resemblance between this class and the BytArray class in functionality, method names, implementation, and usage. They are totally different. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }*/ #include <stdio.h> /* @retain */ #include "Producer.h" #include "vectors.h" /* @retain */ #define badIndex(offset) ((unsigned)(offset) >= (unsigned)capacity) #define goodIndex(offset) (!badIndex(offset)) LOCAL char typeStr[]="\""; IMPORT int strlen(), atoi(); IMPORT long atol(); IMPORT double atof(); IMPORT STR strcpy(), strcat(); IMPORT unsigned _strhash(); = ByteArray:Array CATEGORIES() { } // Indexed variable typing + (unsigned)ndxVarSize { return sizeof(char); } + (STR)ndxVarType { return typeStr; } - (STR)describe { return typeStr; } // Instance creation: Note that capacity records the capacity actually // available for characters. This is one more than the number requested // to guarantee that a null terminator will exist. + new { return [self new:0]; } + new:(unsigned)nExtra { self = (*_alloc)(self, nExtra+1); capacity = nExtra + 1; return self; } + str:(STR)aStr { unsigned n; if (aStr == 0) aStr = ""; n = strlen(aStr); self = (*_alloc)(self, n+1); strcpy(IV(self), aStr); capacity = n+1; return self; } #define VA_TYPE int + sprintf:(STR)fmt; VA_TYPE va_alist; { char buf[BUFSIZ]; #ifdef BOOTSTRAP /* @retain */ typedef int FILE; #endif #include "varargs.h" /* @retain */ #ifdef BOOTSTRAP /* @retain */ typedef char *va_list; #endif #ifdef _IOSTRG /* @retain */ { va_list ap; FILE iod; iod._cnt = sizeof(buf); iod._base = iod._ptr = buf; iod._flag = _IOWRT+_IOSTRG; va_start(ap); _prn(fmt, ap, &iod); va_end(ap); *iod._ptr = '\0'; } #else # define INT int { va_list ap; int ilist[10]; register int *ip = ilist, i; va_start (ap); for (i = 0; i < 10; i++) *ip++ = va_arg(ap, INT); va_end (ap); ip = ilist; sprintf (buf, fmt, ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9]); } #endif return [self str:buf]; } // Convert to indicated type - (int)asInt { return atoi(IV(self)); } - (long)asLong { return atol(IV(self)); } - (double)asFloat { return atof(IV(self)); } // Accessing - (STR)str { return IV(self); } - str:(STR)aStr { strncpy(IV(self), aStr, capacity-1); IV(self)[capacity-1] = '\0'; return self; } - (char)charAt:(unsigned)anOffset { return goodIndex(anOffset) ? IV(self)[anOffset] : (char)[self boundsViolation:anOffset]; } - (char)charAt:(unsigned)anOffset put:(char)aChar { STR p = IV(self)+anOffset; if goodIndex(anOffset) { char tmp= *p; *p=aChar; return tmp; } else return (char)[self boundsViolation:anOffset]; } LOCAL int cmp(a, b) char *a, *b; { return *a-*b; } - sort { qsort(IV(self), capacity-1, sizeof(char), cmp); return self; } #ifdef OBSOLETE // Concatenate aStr to self. Reply 0 if truncation occurred. - concat:aStr { return [self concatSTR:[aStr str]]; } - concatSTR:(STR)aCString { unsigned me, l; if (aCString == 0) return 0; me = strlen(IV(self)); l = capacity-1; if (l > me) strncpy(IV(self)+me, aCString, l-me); IV(self)[capacity-1] = '\0'; return (l > me + strlen(aCString)) ? self : nil; } #endif - concatenateAndFreeReceiver:aByteArray { return [self concatenateSTRAndFreeReceiver:[aByteArray str]]; } - concatenateSTRAndFreeReceiver:(STR)aString { id tmp = [self concatenate:aString]; [self free]; return tmp; } - concatenateSTR:(STR)aString { return [isa sprintf:"%s%s", [self str], aString]; } - concatenate:aByteArray { return [self concatenateSTR:[aByteArray str]]; } // Comparing and hashing: hash must correlate with isEqual: LOCAL int arycmp(obj, str) id obj; register STR str; { register unsigned siz = obj->capacity; register STR p = IV(obj); for (; siz--; p++, str++) if (*p != *str) return *p - *str; return 0; } - (int)compare:aStr { return aStr ? arycmp(self, [aStr str]) : NO; } - (int)compareSTR:(STR)aStr { return aStr ? arycmp(self, aStr) : NO; } - (BOOL)isEqual:aStr { return aStr ? arycmp(self, [aStr str]) == 0 : NO; } - (BOOL)isEqualSTR:(STR)aStr { return aStr ? arycmp(self, aStr) == 0 : NO; } - (BOOL)isCopyOf:anObject { return [super isCopyOf:anObject] && (arycmp(self, IV(anObject)) == 0); } - (unsigned)hash { return _strhash(IV(self)); } =: \Rogue\Monster\ else echo "will not over write ./src/ByteArray.m" fi if `test ! -s ./src/y.output` then echo "writting ./src/y.output" cat > ./src/y.output << '\Rogue\Monster\' state 0 $accept : _ChunkList $end ChunkList : _ (1) . reduce 1 ChunkList goto 1 state 1 $accept : ChunkList_$end ChunkList : ChunkList_Chunk $end accept error shift 5 IDENTIFIER shift 6 { shift 4 . error VariableName goto 3 Chunk goto 2 state 2 ChunkList : ChunkList Chunk_ (2) . reduce 2 state 3 Chunk : VariableName_KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! Chunk : VariableName_KEYWORD STRING ! $$4 Methods Sep Chunk : VariableName_VariableName KEYWORD STRING ! $$6 Methods Sep IDENTIFIER shift 6 KEYWORD shift 7 . error VariableName goto 8 state 4 Chunk : {_InferenceRule } Chunk : {_error } OptionalType : _ (123) error shift 10 # shift 11 ( shift 15 . reduce 123 Template goto 12 InferenceRule goto 9 OptionalType goto 14 TemplateType goto 13 state 5 Chunk : error_! ! shift 16 . error state 6 VariableName : IDENTIFIER_ (126) . reduce 126 state 7 Chunk : VariableName KEYWORD_# VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! Chunk : VariableName KEYWORD_STRING ! $$4 Methods Sep STRING shift 18 # shift 17 . error state 8 Chunk : VariableName VariableName_KEYWORD STRING ! $$6 Methods Sep KEYWORD shift 19 . error state 9 Chunk : { InferenceRule_} } shift 20 . error state 10 Chunk : { error_} } shift 21 . error state 11 InferenceRule : #_PatternType [ ObjcMsgPattern ] InferenceRule : #_VariableName PatternType VariableName OptionalType : _ (123) IDENTIFIER shift 6 ( shift 15 . reduce 123 VariableName goto 23 OptionalType goto 24 PatternType goto 22 state 12 InferenceRule : Template_# PatternType [ ObjcMsgPattern ] InferenceRule : Template_# PatternType ObjcFunctionPattern InferenceRule : Template_# PatternType StringConstant # shift 25 . error state 13 Template : TemplateType_UnarySelector Template : TemplateType_BinarySelector TemplateType Template : TemplateType_KeywordPattern IDENTIFIER shift 29 KEYWORD shift 48 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . error Keyword goto 33 BinarySelector goto 27 UnarySelector goto 26 KeywordPattern goto 28 SpecialCharacter goto 31 state 14 TemplateType : OptionalType_ (122) . reduce 122 state 15 OptionalType : (_Type ) IDENTIFIER shift 50 . error Type goto 49 state 16 Chunk : error !_ (10) . reduce 10 state 17 Chunk : VariableName KEYWORD #_VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! IDENTIFIER shift 6 . error VariableName goto 51 state 18 Chunk : VariableName KEYWORD STRING_! $$4 Methods Sep ! shift 52 . error state 19 Chunk : VariableName VariableName KEYWORD_STRING ! $$6 Methods Sep STRING shift 53 . error state 20 Chunk : { InferenceRule }_ (8) . reduce 8 state 21 Chunk : { error }_ (9) . reduce 9 state 22 InferenceRule : # PatternType_[ ObjcMsgPattern ] [ shift 54 . error state 23 InferenceRule : # VariableName_PatternType VariableName OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 55 state 24 PatternType : OptionalType_ (121) . reduce 121 state 25 InferenceRule : Template #_PatternType [ ObjcMsgPattern ] InferenceRule : Template #_PatternType ObjcFunctionPattern InferenceRule : Template #_PatternType StringConstant OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 56 state 26 Template : TemplateType UnarySelector_ (87) . reduce 87 state 27 Template : TemplateType BinarySelector_TemplateType OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 14 TemplateType goto 57 state 28 Template : TemplateType KeywordPattern_ (89) KeywordPattern : KeywordPattern_Keyword PatternType KEYWORD shift 48 . reduce 89 Keyword goto 58 state 29 UnarySelector : IDENTIFIER_ (127) . reduce 127 state 30 BinarySelector : -_ (104) . reduce 104 state 31 BinarySelector : SpecialCharacter_ (105) . reduce 105 state 32 BinarySelector : DOUBLESPECIAL_ (106) . reduce 106 state 33 KeywordPattern : Keyword_PatternType OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 59 state 34 SpecialCharacter : +_ (107) . reduce 107 state 35 SpecialCharacter : /_ (108) . reduce 108 state 36 SpecialCharacter : \\_ (109) . reduce 109 state 37 SpecialCharacter : *_ (110) . reduce 110 state 38 SpecialCharacter : ~_ (111) . reduce 111 state 39 SpecialCharacter : <_ (112) . reduce 112 state 40 SpecialCharacter : >_ (113) . reduce 113 state 41 SpecialCharacter : =_ (114) . reduce 114 state 42 SpecialCharacter : @_ (115) . reduce 115 state 43 SpecialCharacter : %_ (116) . reduce 116 state 44 SpecialCharacter : &_ (117) . reduce 117 state 45 SpecialCharacter : ?_ (118) . reduce 118 state 46 SpecialCharacter : ,_ (119) . reduce 119 state 47 SpecialCharacter : |_ (120) . reduce 120 state 48 Keyword : KEYWORD_ (132) . reduce 132 state 49 OptionalType : ( Type_) ) shift 60 . error state 50 Type : IDENTIFIER_ (125) . reduce 125 state 51 Chunk : VariableName KEYWORD # VariableName_KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! KEYWORD shift 61 . error state 52 Chunk : VariableName KEYWORD STRING !_$$4 Methods Sep $$4 : _ (4) . reduce 4 $$4 goto 62 state 53 Chunk : VariableName VariableName KEYWORD STRING_! $$6 Methods Sep ! shift 63 . error state 54 InferenceRule : # PatternType [_ObjcMsgPattern ] OptionalType : _ (123) ( shift 15 . reduce 123 ObjcMsgPattern goto 64 OptionalType goto 24 PatternType goto 65 state 55 InferenceRule : # VariableName PatternType_VariableName IDENTIFIER shift 6 . error VariableName goto 66 state 56 InferenceRule : Template # PatternType_[ ObjcMsgPattern ] InferenceRule : Template # PatternType_ObjcFunctionPattern InferenceRule : Template # PatternType_StringConstant IDENTIFIER shift 6 STRING shift 71 [ shift 67 . error VariableName goto 70 ObjcFunctionPattern goto 68 StringConstant goto 69 state 57 Template : TemplateType BinarySelector TemplateType_ (88) . reduce 88 state 58 KeywordPattern : KeywordPattern Keyword_PatternType OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 72 state 59 KeywordPattern : Keyword PatternType_ (90) . reduce 90 state 60 OptionalType : ( Type )_ (124) . reduce 124 state 61 Chunk : VariableName KEYWORD # VariableName KEYWORD_StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! STRING shift 71 . error StringConstant goto 73 state 62 Chunk : VariableName KEYWORD STRING ! $$4_Methods Sep Methods : _ (13) . reduce 13 Methods goto 74 state 63 Chunk : VariableName VariableName KEYWORD STRING !_$$6 Methods Sep $$6 : _ (6) . reduce 6 $$6 goto 75 state 64 InferenceRule : # PatternType [ ObjcMsgPattern_] ] shift 76 . error state 65 ObjcMsgPattern : PatternType_UnarySelector ObjcMsgPattern : PatternType_ObjcKeywordPattern IDENTIFIER shift 29 KEYWORD shift 48 . error Keyword goto 79 UnarySelector goto 77 ObjcKeywordPattern goto 78 state 66 InferenceRule : # VariableName PatternType VariableName_ (86) . reduce 86 state 67 InferenceRule : Template # PatternType [_ObjcMsgPattern ] OptionalType : _ (123) ( shift 15 . reduce 123 ObjcMsgPattern goto 80 OptionalType goto 24 PatternType goto 65 state 68 InferenceRule : Template # PatternType ObjcFunctionPattern_ (84) . reduce 84 state 69 InferenceRule : Template # PatternType StringConstant_ (85) . reduce 85 state 70 ObjcFunctionPattern : VariableName_( ObjcFunctionArgList ) ObjcFunctionPattern : VariableName_( ) ( shift 81 . error state 71 StringConstant : STRING_ (131) . reduce 131 state 72 KeywordPattern : KeywordPattern Keyword PatternType_ (91) . reduce 91 state 73 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant_KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant ! KEYWORD shift 82 . error state 74 Chunk : VariableName KEYWORD STRING ! $$4 Methods_Sep Methods : Methods_Method ! Methods : Methods_error ! error shift 85 IDENTIFIER shift 29 KEYWORD shift 48 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 ! shift 86 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . error Method goto 84 MethodName goto 87 Keyword goto 91 KwdMethodDecl goto 90 BinarySelector goto 89 UnarySelector goto 88 SpecialCharacter goto 31 Sep goto 83 state 75 Chunk : VariableName VariableName KEYWORD STRING ! $$6_Methods Sep Methods : _ (13) . reduce 13 Methods goto 92 state 76 InferenceRule : # PatternType [ ObjcMsgPattern ]_ (82) . reduce 82 state 77 ObjcMsgPattern : PatternType UnarySelector_ (96) . reduce 96 state 78 ObjcMsgPattern : PatternType ObjcKeywordPattern_ (97) ObjcKeywordPattern : ObjcKeywordPattern_Keyword PatternType ParameterDesignator KEYWORD shift 48 . reduce 97 Keyword goto 93 state 79 ObjcKeywordPattern : Keyword_PatternType ParameterDesignator OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 94 state 80 InferenceRule : Template # PatternType [ ObjcMsgPattern_] ] shift 95 . error state 81 ObjcFunctionPattern : VariableName (_ObjcFunctionArgList ) ObjcFunctionPattern : VariableName (_) OptionalType : _ (123) ( shift 15 ) shift 97 . reduce 123 ObjcFunctionArgList goto 96 OptionalType goto 24 PatternType goto 98 state 82 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD_StringConstant KEYWORD StringConstant KEYWORD StringConstant ! STRING shift 71 . error StringConstant goto 99 state 83 Chunk : VariableName KEYWORD STRING ! $$4 Methods Sep_ (5) Sep : Sep_! ! shift 100 . reduce 5 state 84 Methods : Methods Method_! ! shift 101 . error state 85 Methods : Methods error_! ! shift 102 . error state 86 Sep : !_ (11) . reduce 11 state 87 Method : MethodName_LocalVariables $$16 PrimMarker StmtList LocalVariables : _ (23) | shift 104 . reduce 23 LocalVariables goto 103 state 88 MethodName : UnarySelector_ (18) . reduce 18 state 89 MethodName : BinarySelector_VariableName IDENTIFIER shift 6 . error VariableName goto 105 state 90 MethodName : KwdMethodDecl_ (20) KwdMethodDecl : KwdMethodDecl_Keyword VariableName KEYWORD shift 48 . reduce 20 Keyword goto 106 state 91 KwdMethodDecl : Keyword_VariableName IDENTIFIER shift 6 . error VariableName goto 107 state 92 Chunk : VariableName VariableName KEYWORD STRING ! $$6 Methods_Sep Methods : Methods_Method ! Methods : Methods_error ! error shift 85 IDENTIFIER shift 29 KEYWORD shift 48 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 ! shift 86 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . error Method goto 84 MethodName goto 87 Keyword goto 91 KwdMethodDecl goto 90 BinarySelector goto 89 UnarySelector goto 88 SpecialCharacter goto 31 Sep goto 108 state 93 ObjcKeywordPattern : ObjcKeywordPattern Keyword_PatternType ParameterDesignator OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 109 state 94 ObjcKeywordPattern : Keyword PatternType_ParameterDesignator IDENTIFIER shift 6 % shift 112 . error VariableName goto 111 ParameterDesignator goto 110 state 95 InferenceRule : Template # PatternType [ ObjcMsgPattern ]_ (83) . reduce 83 state 96 ObjcFunctionPattern : VariableName ( ObjcFunctionArgList_) ObjcFunctionArgList : ObjcFunctionArgList_, PatternType ParameterDesignator , shift 114 ) shift 113 . error state 97 ObjcFunctionPattern : VariableName ( )_ (93) . reduce 93 state 98 ObjcFunctionArgList : PatternType_ParameterDesignator IDENTIFIER shift 6 % shift 112 . error VariableName goto 111 ParameterDesignator goto 115 state 99 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant_KEYWORD StringConstant KEYWORD StringConstant ! KEYWORD shift 116 . error state 100 Sep : Sep !_ (12) . reduce 12 state 101 Methods : Methods Method !_ (14) . reduce 14 state 102 Methods : Methods error !_ (15) . reduce 15 state 103 Method : MethodName LocalVariables_$$16 PrimMarker StmtList $$16 : _ (16) . reduce 16 $$16 goto 117 state 104 LocalVariables : |_VarList | VarList : _ (25) . reduce 25 VarList goto 118 state 105 MethodName : BinarySelector VariableName_ (19) . reduce 19 state 106 KwdMethodDecl : KwdMethodDecl Keyword_VariableName IDENTIFIER shift 6 . error VariableName goto 119 state 107 KwdMethodDecl : Keyword VariableName_ (27) . reduce 27 state 108 Chunk : VariableName VariableName KEYWORD STRING ! $$6 Methods Sep_ (7) Sep : Sep_! ! shift 100 . reduce 7 state 109 ObjcKeywordPattern : ObjcKeywordPattern Keyword PatternType_ParameterDesignator IDENTIFIER shift 6 % shift 112 . error VariableName goto 111 ParameterDesignator goto 120 state 110 ObjcKeywordPattern : Keyword PatternType ParameterDesignator_ (98) . reduce 98 state 111 ParameterDesignator : VariableName_ (100) . reduce 100 state 112 ParameterDesignator : %_DIGITS DIGITS shift 121 . error state 113 ObjcFunctionPattern : VariableName ( ObjcFunctionArgList )_ (92) . reduce 92 state 114 ObjcFunctionArgList : ObjcFunctionArgList ,_PatternType ParameterDesignator OptionalType : _ (123) ( shift 15 . reduce 123 OptionalType goto 24 PatternType goto 122 state 115 ObjcFunctionArgList : PatternType ParameterDesignator_ (94) . reduce 94 state 116 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD_StringConstant KEYWORD StringConstant ! STRING shift 71 . error StringConstant goto 123 state 117 Method : MethodName LocalVariables $$16_PrimMarker StmtList PrimMarker : _ (21) < shift 125 . reduce 21 PrimMarker goto 124 state 118 LocalVariables : | VarList_| VarList : VarList_VariableName IDENTIFIER shift 6 | shift 126 . error VariableName goto 127 state 119 KwdMethodDecl : KwdMethodDecl Keyword VariableName_ (28) . reduce 28 state 120 ObjcKeywordPattern : ObjcKeywordPattern Keyword PatternType ParameterDesignator_ (99) . reduce 99 state 121 ParameterDesignator : % DIGITS_ (101) . reduce 101 state 122 ObjcFunctionArgList : ObjcFunctionArgList , PatternType_ParameterDesignator IDENTIFIER shift 6 % shift 112 . error VariableName goto 111 ParameterDesignator goto 128 state 123 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant_KEYWORD StringConstant ! KEYWORD shift 129 . error state 124 Method : MethodName LocalVariables $$16 PrimMarker_StmtList AssignmentList : _ (37) error shift 133 ^ shift 131 . reduce 37 StmtList goto 130 Expr goto 132 AssignmentList goto 134 state 125 PrimMarker : <_KEYWORD NumberConstant > KEYWORD shift 135 . error state 126 LocalVariables : | VarList |_ (24) . reduce 24 state 127 VarList : VarList VariableName_ (26) . reduce 26 state 128 ObjcFunctionArgList : ObjcFunctionArgList , PatternType ParameterDesignator_ (95) . reduce 95 state 129 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD_StringConstant ! STRING shift 71 . error StringConstant goto 136 state 130 Method : MethodName LocalVariables $$16 PrimMarker StmtList_ (17) . reduce 17 state 131 StmtList : ^_Expr AssignmentList : _ (37) . reduce 37 Expr goto 137 AssignmentList goto 134 state 132 StmtList : Expr_ (30) StmtList : Expr_. StmtList : Expr_. StmtList . shift 138 . reduce 30 state 133 StmtList : error_. . shift 139 . error state 134 Expr : AssignmentList_Primary Expr : AssignmentList_SimpleMsgExpr Expr : AssignmentList_SimpleMsgExpr CascadedMsgExpr AssignmentList : AssignmentList_VariableName _ IDENTIFIER shift 6 DIGITS shift 157 STRING shift 71 CHARCON shift 156 # shift 152 - shift 158 ( shift 145 [ shift 153 . error Primary goto 140 UnaryObjDesc goto 154 BinaryObjDesc goto 155 SimpleMsgExpr goto 141 UnaryExpr goto 146 BinaryExpr goto 147 KeywordExpr goto 148 Literal goto 143 Block goto 144 VariableName goto 142 CharacterConstant goto 149 NumberConstant goto 151 StringConstant goto 150 state 135 PrimMarker : < KEYWORD_NumberConstant > DIGITS shift 157 - shift 158 . error NumberConstant goto 159 state 136 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant_! ! shift 160 . error state 137 StmtList : ^ Expr_ (29) . reduce 29 state 138 StmtList : Expr ._ (31) StmtList : Expr ._StmtList AssignmentList : _ (37) error shift 133 ! reduce 31 ^ shift 131 ] reduce 31 . reduce 37 StmtList goto 161 Expr goto 132 AssignmentList goto 134 state 139 StmtList : error ._ (33) . reduce 33 state 140 Expr : AssignmentList Primary_ (34) UnaryObjDesc : Primary_ (51) ! reduce 34 . reduce 34 ) reduce 34 ] reduce 34 . reduce 51 state 141 Expr : AssignmentList SimpleMsgExpr_ (35) Expr : AssignmentList SimpleMsgExpr_CascadedMsgExpr ; shift 164 . reduce 35 CascadedMsgExpr goto 162 CascadedMsg goto 163 state 142 AssignmentList : AssignmentList VariableName__ Primary : VariableName_ (44) _ shift 165 . reduce 44 state 143 Primary : Literal_ (45) . reduce 45 state 144 Primary : Block_ (46) . reduce 46 state 145 Primary : (_Expr ) AssignmentList : _ (37) . reduce 37 Expr goto 166 AssignmentList goto 134 state 146 SimpleMsgExpr : UnaryExpr_ (48) UnaryObjDesc : UnaryExpr_ (52) ! reduce 48 . reduce 48 ; reduce 48 ) reduce 48 ] reduce 48 . reduce 52 state 147 SimpleMsgExpr : BinaryExpr_ (49) BinaryObjDesc : BinaryExpr_ (55) ! reduce 49 . reduce 49 ; reduce 49 ) reduce 49 ] reduce 49 . reduce 55 state 148 SimpleMsgExpr : KeywordExpr_ (50) . reduce 50 state 149 Literal : CharacterConstant_ (60) . reduce 60 state 150 Literal : StringConstant_ (61) . reduce 61 state 151 Literal : NumberConstant_ (62) . reduce 62 state 152 Literal : #_( ArrayMemberList ) Literal : #_UnarySelector Literal : #_BinarySelector Literal : #_KeywordList IDENTIFIER shift 29 KEYWORD shift 171 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 ( shift 167 . error BinarySelector goto 169 UnarySelector goto 168 KeywordList goto 170 SpecialCharacter goto 31 state 153 Block : [_BlockVariables StmtList ] Block : [_BlockVariables ] BlockVariables : _ (78) : shift 174 . reduce 78 BlockVarList goto 173 BlockVariables goto 172 state 154 UnaryExpr : UnaryObjDesc_UnarySelector BinaryObjDesc : UnaryObjDesc_ (54) IDENTIFIER shift 29 . reduce 54 UnarySelector goto 175 state 155 BinaryExpr : BinaryObjDesc_BinarySelector UnaryObjDesc KeywordExpr : BinaryObjDesc_KeywordArgList KEYWORD shift 48 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . error Keyword goto 178 KeywordArgList goto 177 BinarySelector goto 176 SpecialCharacter goto 31 state 156 CharacterConstant : CHARCON_ (128) . reduce 128 state 157 NumberConstant : DIGITS_ (129) . reduce 129 state 158 NumberConstant : -_DIGITS DIGITS shift 179 . error state 159 PrimMarker : < KEYWORD NumberConstant_> > shift 180 . error state 160 Chunk : VariableName KEYWORD # VariableName KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant KEYWORD StringConstant !_ (3) . reduce 3 state 161 StmtList : Expr . StmtList_ (32) . reduce 32 state 162 Expr : AssignmentList SimpleMsgExpr CascadedMsgExpr_ (36) CascadedMsgExpr : CascadedMsgExpr_CascadedMsg ; shift 164 . reduce 36 CascadedMsg goto 181 state 163 CascadedMsgExpr : CascadedMsg_ (39) . reduce 39 state 164 CascadedMsg : ;_UnarySelector CascadedMsg : ;_BinarySelector UnaryObjDesc CascadedMsg : ;_KeywordArgList IDENTIFIER shift 29 KEYWORD shift 48 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . error Keyword goto 178 KeywordArgList goto 184 BinarySelector goto 183 UnarySelector goto 182 SpecialCharacter goto 31 state 165 AssignmentList : AssignmentList VariableName __ (38) . reduce 38 state 166 Primary : ( Expr_) ) shift 185 . error state 167 Literal : # (_ArrayMemberList ) ArrayMemberList : _ (67) . reduce 67 ArrayMemberList goto 186 state 168 Literal : # UnarySelector_ (64) . reduce 64 state 169 Literal : # BinarySelector_ (65) . reduce 65 170: shift/reduce conflict (shift 187, red'n 66) on KEYWORD state 170 Literal : # KeywordList_ (66) KeywordList : KeywordList_KEYWORD KEYWORD shift 187 . reduce 66 state 171 KeywordList : KEYWORD_ (102) . reduce 102 state 172 Block : [ BlockVariables_StmtList ] Block : [ BlockVariables_] AssignmentList : _ (37) error shift 133 ^ shift 131 ] shift 189 . reduce 37 StmtList goto 188 Expr goto 132 AssignmentList goto 134 state 173 BlockVariables : BlockVarList_| BlockVarList : BlockVarList_: VariableName | shift 190 : shift 191 . error state 174 BlockVarList : :_VariableName IDENTIFIER shift 6 . error VariableName goto 192 state 175 UnaryExpr : UnaryObjDesc UnarySelector_ (53) . reduce 53 state 176 BinaryExpr : BinaryObjDesc BinarySelector_UnaryObjDesc IDENTIFIER shift 6 DIGITS shift 157 STRING shift 71 CHARCON shift 156 # shift 152 - shift 158 ( shift 145 [ shift 153 . error Primary goto 194 UnaryObjDesc goto 193 UnaryExpr goto 195 Literal goto 143 Block goto 144 VariableName goto 196 CharacterConstant goto 149 NumberConstant goto 151 StringConstant goto 150 state 177 KeywordExpr : BinaryObjDesc KeywordArgList_ (57) KeywordArgList : KeywordArgList_Keyword BinaryObjDesc KEYWORD shift 48 . reduce 57 Keyword goto 197 state 178 KeywordArgList : Keyword_BinaryObjDesc IDENTIFIER shift 6 DIGITS shift 157 STRING shift 71 CHARCON shift 156 # shift 152 - shift 158 ( shift 145 [ shift 153 . error Primary goto 194 UnaryObjDesc goto 154 BinaryObjDesc goto 198 UnaryExpr goto 195 BinaryExpr goto 199 Literal goto 143 Block goto 144 VariableName goto 196 CharacterConstant goto 149 NumberConstant goto 151 StringConstant goto 150 state 179 NumberConstant : - DIGITS_ (130) . reduce 130 state 180 PrimMarker : < KEYWORD NumberConstant >_ (22) . reduce 22 state 181 CascadedMsgExpr : CascadedMsgExpr CascadedMsg_ (40) . reduce 40 state 182 CascadedMsg : ; UnarySelector_ (41) . reduce 41 state 183 CascadedMsg : ; BinarySelector_UnaryObjDesc IDENTIFIER shift 6 DIGITS shift 157 STRING shift 71 CHARCON shift 156 # shift 152 - shift 158 ( shift 145 [ shift 153 . error Primary goto 194 UnaryObjDesc goto 200 UnaryExpr goto 195 Literal goto 143 Block goto 144 VariableName goto 196 CharacterConstant goto 149 NumberConstant goto 151 StringConstant goto 150 state 184 CascadedMsg : ; KeywordArgList_ (43) KeywordArgList : KeywordArgList_Keyword BinaryObjDesc KEYWORD shift 48 . reduce 43 Keyword goto 197 state 185 Primary : ( Expr )_ (47) . reduce 47 state 186 Literal : # ( ArrayMemberList_) ArrayMemberList : ArrayMemberList_ArrayMember IDENTIFIER shift 29 DIGITS shift 157 KEYWORD shift 171 STRING shift 71 CHARCON shift 156 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 210 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 ( shift 209 ) shift 201 . error ArrayMember goto 202 BinarySelector goto 207 UnarySelector goto 206 KeywordList goto 208 CharacterConstant goto 203 NumberConstant goto 205 StringConstant goto 204 SpecialCharacter goto 31 state 187 KeywordList : KeywordList KEYWORD_ (103) . reduce 103 state 188 Block : [ BlockVariables StmtList_] ] shift 211 . error state 189 Block : [ BlockVariables ]_ (77) . reduce 77 state 190 BlockVariables : BlockVarList |_ (79) . reduce 79 state 191 BlockVarList : BlockVarList :_VariableName IDENTIFIER shift 6 . error VariableName goto 212 state 192 BlockVarList : : VariableName_ (80) . reduce 80 state 193 UnaryExpr : UnaryObjDesc_UnarySelector BinaryExpr : BinaryObjDesc BinarySelector UnaryObjDesc_ (56) IDENTIFIER shift 29 . reduce 56 UnarySelector goto 175 state 194 UnaryObjDesc : Primary_ (51) . reduce 51 state 195 UnaryObjDesc : UnaryExpr_ (52) . reduce 52 state 196 Primary : VariableName_ (44) . reduce 44 state 197 KeywordArgList : KeywordArgList Keyword_BinaryObjDesc IDENTIFIER shift 6 DIGITS shift 157 STRING shift 71 CHARCON shift 156 # shift 152 - shift 158 ( shift 145 [ shift 153 . error Primary goto 194 UnaryObjDesc goto 154 BinaryObjDesc goto 213 UnaryExpr goto 195 BinaryExpr goto 199 Literal goto 143 Block goto 144 VariableName goto 196 CharacterConstant goto 149 NumberConstant goto 151 StringConstant goto 150 state 198 BinaryExpr : BinaryObjDesc_BinarySelector UnaryObjDesc KeywordArgList : Keyword BinaryObjDesc_ (58) DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . reduce 58 BinarySelector goto 176 SpecialCharacter goto 31 state 199 BinaryObjDesc : BinaryExpr_ (55) . reduce 55 state 200 CascadedMsg : ; BinarySelector UnaryObjDesc_ (42) UnaryExpr : UnaryObjDesc_UnarySelector IDENTIFIER shift 29 . reduce 42 UnarySelector goto 175 state 201 Literal : # ( ArrayMemberList )_ (63) . reduce 63 state 202 ArrayMemberList : ArrayMemberList ArrayMember_ (68) . reduce 68 state 203 ArrayMember : CharacterConstant_ (69) . reduce 69 state 204 ArrayMember : StringConstant_ (70) . reduce 70 state 205 ArrayMember : NumberConstant_ (71) . reduce 71 state 206 ArrayMember : UnarySelector_ (72) . reduce 72 state 207 ArrayMember : BinarySelector_ (73) . reduce 73 208: shift/reduce conflict (shift 187, red'n 74) on KEYWORD state 208 ArrayMember : KeywordList_ (74) KeywordList : KeywordList_KEYWORD KEYWORD shift 187 . reduce 74 state 209 ArrayMember : (_ArrayMemberList ) ArrayMemberList : _ (67) . reduce 67 ArrayMemberList goto 214 210: shift/reduce conflict (shift 179, red'n 104) on DIGITS state 210 BinarySelector : -_ (104) NumberConstant : -_DIGITS DIGITS shift 179 . reduce 104 state 211 Block : [ BlockVariables StmtList ]_ (76) . reduce 76 state 212 BlockVarList : BlockVarList : VariableName_ (81) . reduce 81 state 213 BinaryExpr : BinaryObjDesc_BinarySelector UnaryObjDesc KeywordArgList : KeywordArgList Keyword BinaryObjDesc_ (59) DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 30 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 . reduce 59 BinarySelector goto 176 SpecialCharacter goto 31 state 214 ArrayMemberList : ArrayMemberList_ArrayMember ArrayMember : ( ArrayMemberList_) IDENTIFIER shift 29 DIGITS shift 157 KEYWORD shift 171 STRING shift 71 CHARCON shift 156 DOUBLESPECIAL shift 32 % shift 43 | shift 47 & shift 44 ? shift 45 , shift 46 + shift 34 - shift 210 / shift 35 \\ shift 36 * shift 37 ~ shift 38 < shift 39 > shift 40 = shift 41 @ shift 42 ( shift 209 ) shift 215 . error ArrayMember goto 202 BinarySelector goto 207 UnarySelector goto 206 KeywordList goto 208 CharacterConstant goto 203 NumberConstant goto 205 StringConstant goto 204 SpecialCharacter goto 31 state 215 ArrayMember : ( ArrayMemberList )_ (75) . reduce 75 37/300 terminals, 53/300 nonterminals 133/600 grammar rules, 216/750 states 3 shift/reduce, 0 reduce/reduce conflicts reported 53/350 working sets used memory: states,etc. 1089/24000, parser 269/12000 109/600 distinct lookahead sets 179 extra closures 341 shift entries, 17 exceptions 139 goto entries 68 entries saved by goto default Optimizer space used: input 909/24000, output 501/12000 501 table entries, 143 zero maximum spread: 262, maximum offset: 260 \Rogue\Monster\ else echo "will not over write ./src/y.output" fi echo "Finished archive 3 of 5" exit ---- Dieter H. Zebbedies ('dee-ter ayech 'zeb-ed-eez) Zebb-Hoff Mach. Tool's Automated Manufacturing Project Cleveland, OH (USnail): 9535 Clinton Rd, Cleveland, OH 44144 (+216 631 6100) (+216 741-5994) (UUCP): ...{decvax,sun,cbosgd}!cwruecmp!zhmti!dieter (CSNET/ARPA/BITNET): dieter@CWRU.EDU
dietz@zhmti.UUCP (Dieter H. Zebbedies) (08/20/87)
"Producer", A package to translate Smalltalk-80 code to your favorite object oriented language, Objective-C. #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./src` then mkdir ./src echo "mkdir ./src" fi if `test ! -s ./src/Substrate.h` then echo "writting ./src/Substrate.h" cat > ./src/Substrate.h << '\Rogue\Monster\' /*{^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Substrate.h: Extensions to the Objective-C Primitive and Collection substrate The macros hide nonPortable `features' of some C compilers (e.g. VMS). ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^}*/ #ifndef SUBSTRATE_H #define SUBSTRATE_H # include "objc.h" # include "assert.h" # undef CATEGORIES # define CATEGORIES() (Substrate, Primitive) /*{ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Stylistic Conventions The IMPORT/EXPORT convention (EXPORT int foo=aValue to export foo, IMPORT int foo to import it) is used instead of of the usual C conventions (int foo=aValue to export foo and extern int foo to import it) to provides a distinctive marker on each global declaration that string search tools key off of to find all global declarations reliably. The convention also provides a convenient way to overcome deficiencies in some C compilers; notably VMS C. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }*/ # define LOCAL static # define USE @requires #ifdef VMS # define IMPORT globalref # define EXPORT globaldef #else # define IMPORT extern # define EXPORT /*export*/ #endif // Obsolete # define FACTORY USE /*{ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Renaming Translate all occurrences of external names that appear in the Primitive or Collection categories to new names defined in the Substrate category. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }*/ #define OrderedCollection OrdCltn /*{ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Bit banging macros ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ }*/ # define RBIT(bits, mask) (bits &= ~mask) # define SBIT(bits, mask) (bits |= mask) # define TBIT(bits, mask) (bits & mask) typedef int *WORD; /* Amorphous typed machine word */ typedef unsigned int WRD; /* amorphous type; `word' */ typedef char BYTE; unsigned _strhash(); IMPORT void put(); #endif \Rogue\Monster\ else echo "will not over write ./src/Substrate.h" fi if `test ! -s ./src/AbstractTranslation.m` then echo "writting ./src/AbstractTranslation.m" cat > ./src/AbstractTranslation.m << '\Rogue\Monster\' #include "Producer.h" = AbstractTranslation : Object CATEGORIES() { id type; } + type:aType { return [[self new] type:aType]; } - type { return type; } - type:aType { type = aType; return self; } - (STR)str { return (STR)[self subclassResponsibility]; } - asTypedByteArray { return (id)[self subclassResponsibility]; } - assignTypesTo:aSelector { id s = [aSelector asByteArray]; info("%s ignoring type assignment of %s\n", NAMEOF(self), [s str]); [s free]; return self; } \Rogue\Monster\ else echo "will not over write ./src/AbstractTranslation.m" fi if `test ! -s ./src/ArgumentList.m` then echo "writting ./src/ArgumentList.m" cat > ./src/ArgumentList.m << '\Rogue\Monster\' #include "Producer.h" = ArgumentList:Node CATEGORIES() { id argumentType, argumentName; } + type:aType name:aName { self = [super new]; argumentType = aType; argumentName = aName; return self; } - argumentType { return argumentType; } - argumentName { return argumentName; } \Rogue\Monster\ else echo "will not over write ./src/ArgumentList.m" fi if `test ! -s ./src/Block.m` then echo "writting ./src/Block.m" cat > ./src/Block.m << '\Rogue\Monster\' #include "Producer.h" IMPORT id symbolScope; USE Set; = Block:Node CATEGORIES() { id blockVariables, statements; } + statements:aStatementList { return [[self new] statements:aStatementList]; } - variables:aVarList { if (!aVarList || [aVarList isEmpty]) return self; [aVarList addContentsTo:blockVariables = [Set new]]; [symbolScope add:blockVariables]; return self; } - statements:aStatementList { statements = aStatementList; return self; } - gen { BOOL needsCompound = blockVariables || [statements size] > 1; if (needsCompound) gc('{'/*}*/); #ifndef COXLIB [blockVariables elementsPerform:@selector(genDeclaration)]; #else [blockVariables eachElementPerform:@selector(genDeclaration)]; #endif [statements genExpr]; if (needsCompound) gc(/*{*/'}'); return self; } - free { [symbolScope remove:blockVariables]; [blockVariables freeContents]; [blockVariables free]; [statements free]; return [super free]; } - type { [statements type]; return types.BLOCK; } \Rogue\Monster\ else echo "will not over write ./src/Block.m" fi if `test ! -s ./src/CharConstant.m` then echo "writting ./src/CharConstant.m" cat > ./src/CharConstant.m << '\Rogue\Monster\' #include "Producer.h" = CharConstant : Constant CATEGORIES() {} - type { return types.CHAR; } - gen { gf("'%s'", [self str]); return self; } \Rogue\Monster\ else echo "will not over write ./src/CharConstant.m" fi if `test ! -s ./src/Class.m` then echo "writting ./src/Class.m" cat > ./src/Class.m << '\Rogue\Monster\' #include "Producer.h" BOOL autoFileFlag; USE OrderedCollection, Identifier; IMPORT id symbolScope; IMPORT STR index(); IMPORT id findSymbol(); = Class:Object CATEGORIES() { id name, superclass, instanceVariables, classVariables, pdn, category; id instanceVariableScope, classVariableScope; } + name:aClass { self = [super new]; name = aClass; if (autoFileFlag) { char buf[80]; sprintf(buf, "%s.m", [name str]); genOpen(buf); } return self; } - superclass:aClass { superclass = aClass; return self; } - instanceVariableNames:aString { STR s = [aString str], end; if (!instanceVariables) instanceVariables = [OrderedCollection new]; if (*s == '\'') s++; while(end = index(s, ' ')) { while(*end == ' ') *end++ = 0; [instanceVariables add:findSymbol([Identifier str:s])]; s = end; } if (end = index(s, '\'')) { *end = 0; [instanceVariables add:findSymbol([Identifier str:s])]; } [symbolScope add:instanceVariableScope=[instanceVariables asSet]]; return self; } - classVariableNames:aString { STR s = [aString str], end; if (!classVariables) classVariables = [OrderedCollection new]; if (*s == '\'') s++; while(end = index(s, ' ')) { while(*end == ' ') *end++ = 0; [classVariables add:findSymbol([Identifier str:s])]; s = end; } if (end = index(s, '\'')) { *end = 0; [instanceVariables add:findSymbol([Identifier str:s])]; } [symbolScope add:classVariableScope=[classVariables asSet]]; return self; } - poolDictionaries:aString { pdn = aString; return self; } - category:aString { category = aString; return self; } - gen { STR start, end, index(); gn(); gs("#include \"st80.h\"\n"); gs("= "); [name gen]; gc(':'); [superclass gen]; gs(" CATEGORIES()"); gc('{'/*}*/); #ifndef COXLIB [instanceVariables elementsPerform:@selector(genDeclaration)]; gc(/*{*/'}'); if (classVariables) [classVariables elementsPerform:@selector(genDeclaration)]; #else [instanceVariables eachElementPerform:@selector(genDeclaration)]; gc(/*{*/'}'); if (classVariables) [classVariables eachElementPerform:@selector(genDeclaration)]; #endif return self; } - free { [symbolScope remove:instanceVariableScope]; [symbolScope remove:classVariableScope]; [classVariables freeContents]; [instanceVariables freeContents]; [classVariables free]; [instanceVariables free]; [name free]; [superclass free]; [pdn free]; [category free]; return [super free]; } \Rogue\Monster\ else echo "will not over write ./src/Class.m" fi if `test ! -s ./src/Comment.m` then echo "writting ./src/Comment.m" cat > ./src/Comment.m << '\Rogue\Monster\' #include "Producer.h" static id head = nil, tail = nil; BOOL stripCommentsFlag = YES; = Comment:Node CATEGORIES() { STR text; } + str:(STR)aString { if (!aString) return nil; self = [super new]; text = (STR)strCopy(aString); if (head == nil) head = self; else tail->successor = self; return tail = self; } + gen { genReset(); [head gen]; [head free]; head = tail = nil; return self; } + free { if (head) [head free]; head = tail = nil; return self; } - (STR)str { return text; } - free { free(text); return [super free]; } - gen { if (!stripCommentsFlag) { gf("// %s", text); [successor gen]; } return self; } \Rogue\Monster\ else echo "will not over write ./src/Comment.m" fi if `test ! -s ./src/Constant.m` then echo "writting ./src/Constant.m" cat > ./src/Constant.m << '\Rogue\Monster\' #include "Producer.h" = Constant:Symbol CATEGORIES() - gen { gs([self str]); return self; } - type { return [self subclassResponsibility]; } - asByteArray { return self; } \Rogue\Monster\ else echo "will not over write ./src/Constant.m" fi if `test ! -s ./src/Expr.m` then echo "writting ./src/Expr.m" cat > ./src/Expr.m << '\Rogue\Monster\' // Expressions: a source (a message or primary) for a value and a list of // targets (variables) to assign values to. Cascaded message expressions // are handled by linking expressions through their successor fields. // // Rewrites cascaded expressions like // Foo new bar gag extent:hack; bletch. // as // cascadeReceiver = [[[Foo new] bar] gag]. // [cascadeReceiver extent:hack]; // [cascadeReceiver ... #include "Producer.h" IMPORT id temporaryVariablePool; USE Msg, List, Identifier; = Expr:Node CATEGORIES() { id assignmentList; id value; } + assign:anAssignmentList value:aValue { return [[[super new] assign:anAssignmentList] value:aValue]; } - assign:aList { assignmentList = aList; return self; } - value:aValue { if (value) info("value of %s reassigned\n", NAMEOF(self)); value = aValue; return self; } - value { return value; } - gen { if (assignmentList) { id s, v; for (s = [assignmentList eachElement]; v = [s next]; ) { [v gen]; gs(" = "); } [s free]; } [value gen]; if (successor) { gc(';'); [successor gen]; } return self; } - type { id type = [value type]; if (successor) [successor type]; #ifndef COXLIB if (assignmentList) [assignmentList elementsPerform:@selector(type:rule:) with:type with:"value assignment"]; #else if (assignmentList) [assignmentList eachElementPerform:@selector(type:rule:) with:type with:"value assignment"]; #endif return type; } - cascade:anExpr { id newReceiver = [Identifier uniqueIdentifier:"tmp"]; if ([value isKindOf:Msg]) { id newValue = [Msg receiver:newReceiver selector:[value selector]]; id newExpr = [Expr assign:assignmentList value:newValue]; value = [value receiver]; assignmentList = [List with:1, newReceiver]; [self successor:newExpr]; [newExpr successor:anExpr]; do { id msg = [newExpr value]; if ([msg isKindOf:Msg]) [msg receiver:newReceiver]; } while (newExpr = [newExpr successor]); } else { if (!assignmentList) assignmentList = [List new]; [assignmentList add:newReceiver]; [self successor:anExpr]; } [temporaryVariablePool add:newReceiver]; return self; } - free { [assignmentList free]; [value free]; return [super free]; } \Rogue\Monster\ else echo "will not over write ./src/Expr.m" fi if `test ! -s ./src/FunctionTranslation.m` then echo "writting ./src/FunctionTranslation.m" cat > ./src/FunctionTranslation.m << '\Rogue\Monster\' #include "Producer.h" = FunctionTranslation : AbstractTranslation CATEGORIES() { id functionName; id functionArgumentList; } + name:aFunctionName args:anArgumentList { self = [super new]; functionName = aFunctionName; functionArgumentList = anArgumentList; return self; } - genReceiver:aReceiver selector:aSelector { id arg; unsigned argNumber = 0; USE Msg; [functionName gen]; gc('('); for (arg = functionArgumentList; arg; arg = [arg successor]) { STR name = [arg str]; if (argNumber != 0) gc(','); if (*name == '%') { unsigned index = atoi(name+1); if (index == 0) [aReceiver gen]; else if (index >= [aSelector size]) wer("argument offset %d out of range", index); else [[[aSelector at:index-1] argument] gen]; } else if (argNumber == 0) [aReceiver gen]; else [[[aSelector at:argNumber-1] argument] gen]; argNumber++; } gc(')'); return self; } - (STR)str { return [functionName str]; } #define MAXARRAY 2048 - asTypedByteArray { char buf[MAXARRAY]; id arg; USE ByteArray; strcpy(buf, [functionName str]); for (arg = functionArgumentList; arg; arg = [arg successor]) { sprintf(buf+strlen(buf), "(%s)%s ", [[arg argumentType] str], [[arg argumentName] str]);; } return [ByteArray str:buf]; } \Rogue\Monster\ else echo "will not over write ./src/FunctionTranslation.m" fi if `test ! -s ./src/Identifier.m` then echo "writting ./src/Identifier.m" cat > ./src/Identifier.m << '\Rogue\Monster\' #include "Producer.h" USE Set, OrdCltn; IMPORT id identifierTranslator; = Identifier:ByteArray CATEGORIES() { id translation, type; } + name:aByteArray { return [self str:[aByteArray str]]; } + str:(STR)aString { self = [super str:aString]; type = types.UNKNOWN; translation = [identifierTranslator find:self]; return self; } + uniqueIdentifier:(STR)aString { static int uniqueness = 0; return [self sprintf:"%s%d", aString, uniqueness++]; } - gen { if (translation) [translation gen]; else gs([self str]); return self; } - genDeclaration { if (translation) [translation genDeclaration]; else { if (type == nil) gs("<nil>"); else [type gen]; gc(' '); gs([self str]); gc(';'); } return self; } - type { if (translation) return type = [translation type]; // dbg("%s: (%s)%s\n", NAMEOF(self), [type str], [self str]); // if (type == types.UNKNOWN) [self type:types.ID rule:"default: first use"]; return type; } - type:aType rule:(STR)aString { if (translation && type != aType) { info("attempt to change type of translated symbol %s ignored (%s)", [self str], aString); return self; } if (aType == nil) return [self error:"nil type"]; if (type != types.UNKNOWN && aType != type) { wer("%s %s; tried to change type from %s to %s ignored (%s)", NAMEOF(self), [self str], [type str], [aType str], aString); } else { info("type of %s is (%s) (%s)\n", [self str], [aType str], aString); type = aType; } return self; } - free { return nil; } =: \Rogue\Monster\ else echo "will not over write ./src/Identifier.m" fi if `test ! -s ./src/IdentifierTranslation.m` then echo "writting ./src/IdentifierTranslation.m" cat > ./src/IdentifierTranslation.m << '\Rogue\Monster\' // type inferencing template #include "Producer.h" #include "assert.h" USE Set, IntArray, Msg; IMPORT id identifierTranslator, globalSymbols; = IdentifierTranslation:ByteArray CATEGORIES() { id type, targetIdentifier; } + sourceName:sourceIdentifier targetType:aType targetName:anIdentifier { id result; self = [super str:[sourceIdentifier str]]; [sourceIdentifier free]; targetIdentifier = anIdentifier; if (aType == 0 || aType == types.UNKNOWN) type = types.ID; else type = aType; [targetIdentifier type:type rule:"explicit rule"]; #ifndef COXLIB if ([identifierTranslator addNTest:self]) result = self; else result = [identifierTranslator find:self]; #else result = [identifierTranslator add:self]; #endif if (result && result != self && result->type != type) { dbg("result=%x result->type=%x\n", result, result->type); wer("incompatible translations for identifier %s. Using %s, ignoring %s", [self str], [type str], [result->type str]); } [globalSymbols add:self]; return self; } - type { return type; } - type:aType rule:(STR)aString { info("IdentifierTranslation %s ignored type change from %s to %s", [self str], [type str], [aType str]); return self; } - gen { gs([targetIdentifier str]); return self; } - genDeclaration { [type gen]; gc(' '); gs([targetIdentifier str]); gc(';'); return self; } - targetIdentifier { return targetIdentifier; } - free { return nil; } - asTypedByteArray { return [ByteArray sprintf:"(%s)%s", [type str], [self str]]; } =: \Rogue\Monster\ else echo "will not over write ./src/IdentifierTranslation.m" fi if `test ! -s ./src/List.m` then echo "writting ./src/List.m" cat > ./src/List.m << '\Rogue\Monster\' #include "Producer.h" = List:OrdCltn CATEGORIES() #ifndef COXLIB - gen { [self elementsPerform:_cmd]; return self; } #else - gen { [self eachElementPerform:_cmd]; return self; } #endif \Rogue\Monster\ else echo "will not over write ./src/List.m" fi if `test ! -s ./src/METHODDECLS.m` then echo "writting ./src/METHODDECLS.m" cat > ./src/METHODDECLS.m << '\Rogue\Monster\' #include "Producer.h" = METHODDECLS:Object CATEGORIES() {} - (BOOL)isEmpty {;} - (BOOL)isEqual:aStr {;} - (BOOL)isEqualSTR:(STR)aStr {;} - (BOOL)isUnary {;} - (STR)str {;} - (unsigned)hash {;} - (unsigned)size {;} - add:aLink {;} - argument {;} - argument:anArgument {;} - argumentType {;} - array:anArray {;} - asByteArray {;} - assign:aList {;} - assign:anAssignmentList value:aValue {;} - at:(unsigned)anInt {;} - cascade:anExpr {;} - category:aString {;} - classVariableNames:aString {;} - comment:aString {;} - elementsPerform:(SEL)aSelector with:arg1 with:arg2 {;} - elementsPerform:(SEL)aSelector with:arg1 {;} - elementsPerform:(SEL)aSelector {;} - eachElementPerform:(SEL)aSelector with:arg1 with:arg2 {;} - eachElementPerform:(SEL)aSelector with:arg1 {;} - eachElementPerform:(SEL)aSelector {;} - expr {;} - expr:anExpr {;} - free {;} - freeContents {;} - gen {;} - genDeclaration {;} - genExpr {;} - genPrivate {;} - genReceiver:aReceiver selector:aSelector {;} - initialize {;} - insert:aLink {;} - install:aTemplate translation:aTranslation {;} - instanceVariableNames:aString {;} - lastElement {;} - name:aByteArray {;} - name:aFunctionName args:anArgumentList {;} - name:aString argument:anArgument {;} - poolDictionaries:aString {;} - predecessorOf:aLink {;} - primitive:aToken {;} - receiver {;} - receiver:anObject selector:aSelector {;} - receiver:anObject {;} - receiverType {;} - receiverType:aType selector:aSelector {;} - remove:aLink {;} - selector {;} - selector:aSelector asFactory:(BOOL)isFactoryMethod {;} - selector:aSelector {;} - sourceName:sourceIdentifier targetType:aType targetName:anIdentifier {;} - statements:aStatementList {;} - str:(STR)aString {;} - successor {;} - successor:aLink {;} - superclass:aClass {;} - template:aTemplate translation:aTranslation {;} - translation {;} - translation:aTranslation {;} - translationFor:aMsg {;} - type {;} - type:aType name:aName {;} - type:aType rule:(STR)aString {;} - type:aType translation:aByteArray {;} - type:aType {;} - uniqueIdentifier:(STR)aString {;} - value {;} - value:aValue {;} - variables:aVarList {;} \Rogue\Monster\ else echo "will not over write ./src/METHODDECLS.m" fi if `test ! -s ./src/Method.m` then echo "writting ./src/Method.m" cat > ./src/Method.m << '\Rogue\Monster\' #include "Producer.h" IMPORT id symbolScope; USE Set, Identifier; EXPORT id temporaryVariablePool = nil; = Method:Object CATEGORIES() { id selector, comment, primitive, statements; id argumentVariables, localVariables; id type, concatenatedSelector; BOOL isFactory; id translation; } + selector:aSelector asFactory:(BOOL)aBoolean { id sel, arg; self = [super new]; selector = aSelector; isFactory = aBoolean; concatenatedSelector = [aSelector asByteArray]; argumentVariables = [Set new]; type = types.UNKNOWN; [argumentVariables add:[[Identifier str:"self"] type:types.ID rule:"hardwired"]]; for (sel = aSelector; sel && (arg = [sel argument]); sel = [sel successor]) [argumentVariables add:arg]; [symbolScope add:argumentVariables]; temporaryVariablePool = localVariables = [Set new]; return self; } - receiverType { return types.ID; } - comment:aString { comment = aString; return self; } - variables:aVarList { [aVarList addContentsTo:localVariables]; [symbolScope add:localVariables]; return self; } - selector { return selector; } - statements:aStmtList { statements = aStmtList; return self; } - primitive:aToken { primitive = aToken; return self; } - gen { USE Return; dbg("//=======================Method gen==================================\n"); [self type]; // this triggers the type inferencing machinery dbg("//-----------------------Method gen----------------------------------\n"); gn(); gc(isFactory ? '+' : '-'); gc(' '); if (type != types.ID) { gc('('); [type gen]; gc(')'); } [selector genDeclaration]; gs(" {"); #ifndef COXLIB [localVariables elementsPerform:@selector(genDeclaration)]; #else [localVariables eachElementPerform:@selector(genDeclaration)]; #endif [primitive gen]; [statements gen]; if (![[statements lastElement] isKindOf:Return]) gs("return self;"); gc('}'); return self; } - type { IMPORT id msgTranslator; id t; STR failReason = "name not found"; id key, sourceStr, msgTranslation, stmt, s; if (translation) return [translation type]; [statements type]; [selector type]; key = [selector asByteArray]; sourceStr = [selector asTypedByteArray]; if (msgTranslation = [msgTranslator find:key]) { unsigned i, n; dbg("translation for method %s\n", [sourceStr str]); if (![selector isUnary]) { id s; for (s = selector; s; s = [s successor]) { id st = [s type]; if (st == types.UNKNOWN) [s type:types.ID rule:"method arg"]; } } for (n = [msgTranslation size], i = 0; i < n; i++) { id s, p, targetPattern = [msgTranslation at:i], targetStr; failReason = "types didn't match"; if (![selector isUnary]) { unsigned offset = 1; for (s = selector; s; s = [s successor]) { id rt = [s type], pt = [targetPattern at:offset++]; dbg(" actualArgType=%s patternArgType=%s\n", [rt str], [pt str]); if ((pt != types.ANY) && (rt != pt)) goto tryAgain; // break out to try next pattern } } translation = [targetPattern translation]; type = [translation type]; targetStr = [translation asTypedByteArray]; info("method %s translated to (%s)%s (type match)\n", [sourceStr str], [type str], [targetStr str]); [targetStr free]; [translation assignTypesTo:selector]; goto succeed; tryAgain:; } } info("method %s translated literally (%s)\n", [sourceStr str], failReason); succeed: if (!type) { for (stmt = statements; stmt; stmt = [stmt successor]) { if ([stmt isKindOf:Return]) [self type:[stmt type] rule:"used type from return stmt"]; } } if (type == types.UNKNOWN) [self type:types.ID rule:"default method type"]; [key free]; [sourceStr free]; return type; } - type:aType rule:(STR)aString { if (aType == nil) return [self error:"nil type"]; if (type != types.UNKNOWN && aType != type) { wer("attempt to change type of method %s from %s to %s ignored (%s)", [self str], [type str], [aType str], aString); } else { id s = [selector asByteArray]; info("type of method %s set to (%s) (%s)\n", [s str], [aType str], aString); [s free]; type = aType; } return self; } - free { [symbolScope remove:argumentVariables]; [symbolScope remove:localVariables]; [primitive free]; [selector free]; [comment free]; [argumentVariables freeContents]; [argumentVariables free]; [localVariables freeContents]; [localVariables free]; [concatenatedSelector free]; [statements free]; return [super free]; } =: \Rogue\Monster\ else echo "will not over write ./src/Method.m" fi if `test ! -s ./src/Msg.m` then echo "writting ./src/Msg.m" cat > ./src/Msg.m << '\Rogue\Monster\' #include "Producer.h" = Msg:Object CATEGORIES() { id receiver; id selector; id translation; } IMPORT id msgTranslator; USE Template; + receiver:anObject { return [[self new] receiver:anObject]; } + receiver:anObject selector:aSelector { return [[[self new] receiver:anObject] selector:aSelector]; } + selector:aSelector { return [[self new] selector:aSelector]; } - receiver { return receiver; } - receiverType { return [receiver type]; } - receiver:anObject { receiver = anObject; return self; } - selector { return selector; } - selector:aSelector { selector = aSelector; return self; } - free { [receiver free]; [selector free]; return [super free]; } // ByteArray Emulation - (STR)str { return [selector str]; } - (unsigned)hash { return _strhash([self str]); } - (BOOL)isEqual:aStr { return strcmp([self str], [aStr str]) == 0; } - (BOOL)isEqualSTR:(STR)aStr { return strcmp([self str], aStr) == 0; } - type { id type; if (!translation) { unsigned i, n; STR failReason = 0; id s, key = [selector asByteArray]; id msgTranslation, receiverType = [receiver type]; id sourceStr = [selector asTypedByteArray]; dbg("translating message [(%s) %s]\n", [[receiver type] str], [sourceStr str]); if (![selector isUnary]) { for (s = selector; s; s = [s successor]) { id st = [s argumentType]; if (st == types.UNKNOWN) [s type:types.ID rule:"msg arg"]; } } if (msgTranslation = [msgTranslator find:key]) { for (n = [msgTranslation size], i = 0; i < n; i++) { unsigned offset = 0; id s, targetStr; id targetPattern = [msgTranslation at:i]; id patternReceiverType = [targetPattern at:offset++]; dbg(" actualReceiverType=%s patternReceiver=%s\n", [receiverType str], [patternReceiverType str]); failReason = "receiver types didn't match"; if (patternReceiverType == types.ANY || patternReceiverType == receiverType) { if (![selector isUnary]) { // if not unary selector failReason = "argument types didn't match"; for (s = selector; s; s = [s successor]) { id rt = [s type], pt = [targetPattern at:offset++]; dbg(" actualArgType=%s patternArgType=%s\n", [rt str], [pt str]); if ((pt != types.ANY) && (rt != pt)) goto fail; // break out to try next pattern } } translation = [targetPattern translation]; targetStr = [translation asTypedByteArray]; info("message [(%s)%s] translated to (%s)%s (type match)\n", [receiverType str], [sourceStr str], [[translation type] str], [targetStr str]); [targetStr free]; goto succeed; } fail:; } } else failReason = "name not found"; info("message [(%s)%s] translated literally (%s)\n", [receiverType str], [sourceStr str], failReason); succeed: [key free]; [sourceStr free]; if ([receiver type] == types.UNKNOWN) [receiver type:types.ID rule:"message receiver"]; } type = translation ? [translation type] : types.ID; return type == types.UNKNOWN ? types.ID : type; } - gen { if (translation) [translation genReceiver:receiver selector:selector]; else { gc('['); [receiver gen]; gc(' '); [selector gen]; gc(']'); } return self; } =: \Rogue\Monster\ else echo "will not over write ./src/Msg.m" fi if `test ! -s ./src/MsgArgPattern.m` then echo "writting ./src/MsgArgPattern.m" cat > ./src/MsgArgPattern.m << '\Rogue\Monster\' #include "Producer.h" = MsgArgPattern : IdArray CATEGORIES() { id translation; } + template:aTemplate translation:aTranslation { id s; unsigned i = 0; self = [self new:[aTemplate size]+1]; [self at:i++ put:[aTemplate receiverType]]; for (s = [aTemplate selector]; s; s = [s successor]) { id t = [s type]; if (t == 0 || t == types.UNKNOWN) t = types.ANY; [self at:i++ put:t]; } return [self translation:aTranslation]; } - type { return [translation type]; } - translation:aTranslation { translation = aTranslation; return self; } - translation { return translation; } \Rogue\Monster\ else echo "will not over write ./src/MsgArgPattern.m" fi if `test ! -s ./src/MsgNamePattern.m` then echo "writting ./src/MsgNamePattern.m" cat > ./src/MsgNamePattern.m << '\Rogue\Monster\' // Each message may have several translations depending on the type of // the receiver and the message's arguments. MsgNamePattern holds the // name of the message (concatenated selector in selectorByteArray) // and an ordered collection of MsgArgPatterns. These are IdArrays // holding the type of the receiver followed by the types of the arguments. // Each MsgArgPattern also holds the translation for the messages that // match in name and argument type. #include "Producer.h" = MsgNamePattern : OrdCltn CATEGORIES() { id selectorByteArray; } + name:aByteArray { self = [super new]; selectorByteArray = aByteArray; return self; } - (unsigned)hash { return [selectorByteArray hash]; } - (BOOL)isEqual:aMsgNamePattern { return [selectorByteArray isEqual:aMsgNamePattern]; } - (STR)str { return [selectorByteArray str]; } \Rogue\Monster\ else echo "will not over write ./src/MsgNamePattern.m" fi if `test ! -s ./src/MsgTranslation.m` then echo "writting ./src/MsgTranslation.m" cat > ./src/MsgTranslation.m << '\Rogue\Monster\' #include "Producer.h" = MsgTranslation : AbstractTranslation CATEGORIES() { id receiverType, selector; } + receiverType:aType selector:aSelector { self = [super type:types.ID]; receiverType = aType ? aType : types.ANY; selector = aSelector; return self; } - selector { return selector; } - receiverType { return receiverType; } - (STR)str { return [selector str]; } - genReceiver:aReceiver selector:aSelector { USE Msg; unsigned argNumber = 0; id sel; gc('['); [aReceiver gen]; for (sel = selector; sel; sel = [sel successor], argNumber++) { STR name = [sel str]; gc(' '); if (*name == '%') { unsigned index = atoi(&name[1]); if (index == 0) wer("%%0 not allowed in MsgPattern rules"); else if (index >= [aSelector size]) { wer("argument offset %d out of range", index); } else { gs([[selector at:index-1] str]); [[[aSelector at:index-1] argument] gen]; } } else { gs([[selector at:argNumber] str]); [[[aSelector at:argNumber] argument] gen]; } } gc(']'); return self; } - asTypedByteArray { return [selector asTypedByteArray]; } - free { return nil; } - assignTypesTo:aSelector { id s = aSelector, p = [self selector]; while(s && p) { [s type:[p type]]; s = [s successor]; p = [p successor]; } return self; } \Rogue\Monster\ else echo "will not over write ./src/MsgTranslation.m" fi if `test ! -s ./src/MsgTranslator.m` then echo "writting ./src/MsgTranslator.m" cat > ./src/MsgTranslator.m << '\Rogue\Monster\' // MsgTranslator: a set of MsgNamePatterns. These hold a string (the // concatenated selector characters) and a collection of MsgArgPatterns // describing one of the types (for receiver and arguments) for which // a translation is known #include "Producer.h" EXPORT id msgTranslator = nil; USE MsgNamePattern, MsgArgPattern, Msg; = MsgTranslator : Set CATEGORIES() + initialize { if (!msgTranslator) msgTranslator = [self new]; return self; } - install:aTemplate translation:aTranslation { id name = [[aTemplate selector] asByteArray]; id msgNamePattern = [self find:name]; if (msgNamePattern) [name free]; else [self add:msgNamePattern=[MsgNamePattern name:name]]; [msgNamePattern add:[MsgArgPattern template:aTemplate translation:aTranslation]]; return self; } \Rogue\Monster\ else echo "will not over write ./src/MsgTranslator.m" fi if `test ! -s ./src/Node.m` then echo "writting ./src/Node.m" cat > ./src/Node.m << '\Rogue\Monster\' #include "Producer.h" = Node:Object CATEGORIES() { id successor; } - successor { return successor; } - successor:aLink { id me = successor; successor = aLink; return me; } - lastElement { while(successor) self = successor; return self; } // Reply the predecessor of the indicated link. - predecessorOf:aLink { do { if (successor == aLink) return self; } while (self = successor); return nil; } // Reply the n'th link in this chain. - at:(unsigned)anInt { register unsigned i = anInt; register id obj = self; while (i-- && obj) obj = obj->successor; return obj ? obj : [self error:"range error: %d", anInt]; } // Append another instance to this chain. - add:aLink { id me = self; while (successor) self = successor; successor = aLink; return me; } // Free this link and all successors - freeContents { register id next; do { next = successor; [super free]; } while (self = next); } // remove - remove:aLink { self =[self predecessorOf:aLink]; if (self == nil) return nil; successor= [ aLink successor]; return aLink; } // insert - insert:aLink { [ aLink successor:successor]; successor= aLink; return self; } - gen { [self show]; [successor show]; return self; } - free { [successor free]; return [super free]; } // Reply the number of linked instances #ifdef OBSOLETE - (unsigned)size { register unsigned n = 1; while(self = successor) n++; return n; } #endif - (unsigned)size { unsigned i; for (i=1; self = successor; i++); return i; } #ifndef COXLIB - elementsPerform:(SEL)aSelector { do { [self perform:aSelector]; } while (self = successor); return self; } - elementsPerform:(SEL)aSelector with:arg1 { do { [self perform:aSelector with:arg1]; } while (self = successor); return self; } - elementsPerform:(SEL)aSelector with:arg1 with:arg2 { do { [self perform:aSelector with:arg1 with:arg2]; } while (self = successor); return self; } #else - eachElementPerform:(SEL)aSelector { do { [self perform:aSelector]; } while (self = successor); return self; } - eachElementPerform:(SEL)aSelector with:arg1 { do { [self perform:aSelector with:arg1]; } while (self = successor); return self; } - eachElementPerform:(SEL)aSelector with:arg1 with:arg2 { do { [self perform:aSelector with:arg1 with:arg2]; } while (self = successor); return self; } #endif =: \Rogue\Monster\ else echo "will not over write ./src/Node.m" fi if `test ! -s ./src/Scope.m` then echo "writting ./src/Scope.m" cat > ./src/Scope.m << '\Rogue\Monster\' // Symbol scoping // A scope is an ordered collection Sets of identifiers #include "Producer.h" USE Set, IntArray, Msg; EXPORT id symbolScope = nil, undefinedSymbols = nil, globalSymbols = nil, identifierTranslator = nil; = Scope:OrderedCollection CATEGORIES() + initialize { if (!symbolScope) { symbolScope = [self new]; undefinedSymbols = [Set new]; globalSymbols = [Set new]; [symbolScope add:globalSymbols]; [symbolScope add:undefinedSymbols]; identifierTranslator = [Set new]; } return self; } =: id findSymbol(aVariable) id aVariable; { int i, n = [symbolScope size]; for (i = n-1; i >= 0; i--) { id hit; if (hit = [[symbolScope at:i] find:aVariable]) return hit; } info("undefined %s %s\n", NAMEOF(aVariable), [aVariable str]); [undefinedSymbols add:aVariable]; return aVariable; } \Rogue\Monster\ else echo "will not over write ./src/Scope.m" fi if `test ! -s ./src/NumberConstant.m` then echo "writting ./src/NumberConstant.m" cat > ./src/NumberConstant.m << '\Rogue\Monster\' #include "Producer.h" = NumberConstant : Constant CATEGORIES() {} - gen { gs([self str]); return self; } + str:(STR)aString { return [super str:aString]; } - type { return index([self str], '.') ? types.FLOAT : types.INT; } \Rogue\Monster\ else echo "will not over write ./src/NumberConstant.m" fi if `test ! -s ./src/Return.m` then echo "writting ./src/Return.m" cat > ./src/Return.m << '\Rogue\Monster\' #include "Producer.h" = Return:Node CATEGORIES() { id body; } + expr:anExpr { self = [super new]; body = anExpr; return self; } - gen { [self genExpr]; gc(';'); return self; } - genExpr { gs("return "); [body gen]; return self; } - free { [body free]; return [super free]; } - type { return [body type]; } \Rogue\Monster\ else echo "will not over write ./src/Return.m" fi if `test ! -s ./src/Selector.m` then echo "writting ./src/Selector.m" cat > ./src/Selector.m << '\Rogue\Monster\' #include "Producer.h" #define strEq(p, q) (strcmp(p, q) == 0) #define strHas(s, c) (index(s, c) != 0) #define MAXSELECTOR 512 STR strCopy(), xlate(); USE ByteArray, IdArray; = Selector:Node CATEGORIES() { STR name; id argument; } + name:aString argument:anArgument { return [[self name:aString] argument:anArgument]; } + name:aString { return [self str:[aString str]]; } + str:(STR)aString { self = [super new]; name = strCopy(aString); if (strlen(name) < 1) return [self error:"nil selector"]; return self; } // Inherited deepCopy seems to not copy the name argument correctly - deepCopy { id t = [[isa str:name] argument:argument]; [t successor:[successor deepCopy]]; return t; } - (BOOL)isUnary { return argument == nil; } - asByteArray { char strBuf[MAXSELECTOR]; strBuf[0] = 0; do { strcat(strBuf, name); } while (self = successor); return [ByteArray str:strBuf]; } - asTypeArray { id typeArray; if ([self isUnary]) typeArray = [IdArray new:0]; else { typeArray = [IdArray new:[self size]]; do { [typeArray add:[argument type]]; } while (self = successor); } return typeArray; } - asTypedByteArray { char strBuf[MAXSELECTOR]; strBuf[0] = 0; do { strcat(strBuf, name); if (argument) sprintf(strBuf+strlen(strBuf), "(%s) ", [[argument type] str]);; } while (self = successor); return [ByteArray str:strBuf]; } // ByteArray emulation - (STR)str { return name; } - (unsigned)hash { return _strhash(name); } - (BOOL)isEqual:anObject { return self == anObject || strcmp(name, [anObject str]) == 0; } - (BOOL)isEqualSTR:(STR)aStr { return strcmp(name, aStr) == 0; } - type { if (successor) [successor type]; return [argument type]; } - type:aType { return [self type:aType rule:"force"]; } - type:aType rule:(STR)aString { [argument type:aType rule:aString]; return self; } - argument:anArgument { argument = anArgument; return self; } - argument { return argument; } - argumentType { return [argument type]; } - free { free(name); [argument free]; return [super free]; } - gen { gs(xlate(name)); [argument gen]; if (successor) { gc(' '); [successor gen]; } return self; } - genDeclaration { gs(xlate(name)); if (argument && [argument type] != types.ID) { gc('('); gs([[argument type] str]); gc(')'); } [argument gen]; if (successor) { gc(' '); [successor genDeclaration]; } return self; } =: // Translate Smalltalk binary selectors to Objective-C keyword static STR xlate(s) STR s; { static STR binarySelectorTbl= // parallel arrays! "+-/\\*~<>=@%&?!,|", objcSelectorStrings[]= { "plus", "minus", "slash", "backslash", "times", "tilde", "lesser", "greater", "equals", "point", "percent", "ampersand", "question", "bang", "comma", "or", "/*@*/", 0}; STR i, index(); static char buf[MAXSELECTOR]; *buf = 0; if (i = index(binarySelectorTbl, s[0])) { strcat(buf, objcSelectorStrings[i-binarySelectorTbl]); if (s[1]) { if (i = index(binarySelectorTbl, s[1])) strcat(buf, objcSelectorStrings[i-binarySelectorTbl]); else wer("bad char in binary selector <%c>", s[1]); if (s[2]) wer("binary selector more than 2 chars long <%s>", s); } strcat(buf, ":"); return buf; } return s; } \Rogue\Monster\ else echo "will not over write ./src/Selector.m" fi if `test ! -s ./src/SelectorConstant.m` then echo "writting ./src/SelectorConstant.m" cat > ./src/SelectorConstant.m << '\Rogue\Monster\' #include "Producer.h" = SelectorConstant : Constant CATEGORIES() {} - type { return types.SELECTOR; } - gen { id t = types.SELECTOR; dbg("%s: (%s)%s\n", NAMEOF(self), [t str], [self str]); gs("@selector("); [super gen]; gc(')'); return self; } =: \Rogue\Monster\ else echo "will not over write ./src/SelectorConstant.m" fi if `test ! -s ./src/StArray.m` then echo "writting ./src/StArray.m" cat > ./src/StArray.m << '\Rogue\Monster\' #include "Producer.h" = StArray:OrdCltn CATEGORIES() { id type; } - gen { id s, m; gs("={"); for (s = [self eachElement]; m = [s next]; ) { [m gen]; gs(", "); } [s free]; gc('}'); return self; } - type { id s, m; if (type) return type; if ([self isEmpty]) return types.ID; type = [[self firstElement] type]; for (s = [self eachElement]; m = [s next]; ) if ([m type] != type) wer("this array holds diverse types"); [s free]; return type; } \Rogue\Monster\ else echo "will not over write ./src/StArray.m" fi if `test ! -s ./src/Stmt.m` then echo "writting ./src/Stmt.m" cat > ./src/Stmt.m << '\Rogue\Monster\' #include "Producer.h" = Stmt:Node CATEGORIES() { id expr, type; } + expr:anExpr { self = [super new]; expr = anExpr; return self; } - expr { return expr; } - free { [expr free]; return [super free]; } - gen { [expr gen]; if (type != types.STMT) gc(';'); [successor gen]; return self; } - genExpr { [expr gen]; if (successor) { if (type != types.STMT) gc(';'); [successor gen]; } return self; } - type { if (type) return type; type = [expr type]; [successor type]; return type; } \Rogue\Monster\ else echo "will not over write ./src/Stmt.m" fi if `test ! -s ./src/StringConstant.m` then echo "writting ./src/StringConstant.m" cat > ./src/StringConstant.m << '\Rogue\Monster\' #include "Producer.h" = StringConstant : Constant CATEGORIES() {} + str:(STR)aString { STR rindex(), p; if (*aString == '\'' && (p = rindex(aString, '\''))) { char c = *p; *p = 0; self = [super str:aString+1]; *p = c; return self; } else return [super str:aString]; } - type { id t = types.CSTRING; return t; } - gen { gc('"'); [super gen]; gc('"'); return self; } =: \Rogue\Monster\ else echo "will not over write ./src/StringConstant.m" fi if `test ! -s ./src/StringTranslation.m` then echo "writting ./src/StringTranslation.m" cat > ./src/StringTranslation.m << '\Rogue\Monster\' // Translate message to string #include "Producer.h" #include "ctype.h" = StringTranslation:AbstractTranslation CATEGORIES() { id translation; } + type:aType translation:aByteArray { return [[self type:aType] translation:aByteArray]; } - translation:aByteArray { translation = aByteArray; return self; } - (STR)str { return [translation str]; } - genReceiver:aReceiver selector:aSelector { STR rindex(), q, p; p = [translation str]; for (; *p; p++) { if (*p == '\\') { gc(*p++); gc(*p); continue; } else if (*p == '%') { unsigned index = atoi(++p); if (index == 0) [aReceiver gen]; else if (--index >= [aSelector size]) wer("bad rule", index+1); else [[[aSelector at:index] argument] gen]; while (isdigit(*p)) p++; p--; } else if (*p == '\n') { while(isspace(*p)) p++; p--; } else gc(*p); } return self; } - asTypedByteArray { return [translation asByteArray]; } =: static verifyArgCount(targetString, sourcePattern) STR targetString; id sourcePattern; { STR p; for (p = targetString; *p; p++) { if (*p == '\\') { *p++; continue; } else if (*p == '%') { unsigned index = atoi(++p); if (index != 0 && --index >= [sourcePattern size]) wer("no such argument"); while (isdigit(*p)) p++; p--; } } } \Rogue\Monster\ else echo "will not over write ./src/StringTranslation.m" fi if `test ! -s ./src/Template.m` then echo "writting ./src/Template.m" cat > ./src/Template.m << '\Rogue\Monster\' #include "Producer.h" #define MAXSELECTOR 5000 = Template : ByteArray CATEGORIES() { id receiverType, selector; } + receiverType:aType selector:aSelector { char strBuf[MAXSELECTOR]; id s = aSelector; strBuf[0] = 0; for (s = aSelector; s; s = [s successor]) strcat(strBuf, [s str]); self = [super str:strBuf]; receiverType = aType; selector = aSelector; return self; } - receiverType { return receiverType; } - selector { return selector; } \Rogue\Monster\ else echo "will not over write ./src/Template.m" fi echo "Finished archive 4 of 5" exit ---- Dieter H. Zebbedies ('dee-ter ayech 'zeb-ed-eez) Zebb-Hoff Mach. Tool's Automated Manufacturing Project Cleveland, OH (USnail): 9535 Clinton Rd, Cleveland, OH 44144 (+216 631 6100) (+216 741-5994) (UUCP): ...{decvax,sun,cbosgd}!cwruecmp!zhmti!dieter (CSNET/ARPA/BITNET): dieter@CWRU.EDU
dietz@zhmti.UUCP (Dieter H. Zebbedies) (08/20/87)
"Producer", A package to translate Smalltalk-80 code to your favorite object oriented language, Objective-C. #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./rules` then mkdir ./rules echo "mkdir ./rules" fi if `test ! -s ./rules/generic.ru` then echo "writting ./rules/generic.ru" cat > ./rules/generic.ru << '\Rogue\Monster\' "ClassNames ===============================================================" { # ArrayedCollection (id) ArrayedCollection } { # Association (id) Assoc } { # Bag (id) Bag } { # Button (id) Button } { # ByteArray (id) ByteArray } { # Character (id) Character } { # Circle (id) Circle } { # Collection (id) Collection } { # Cursor (id) currentCursor } { # Dictionary (id) Dictionary } { # Display (id) currentDisplay } { # Drafting (id) Drafting } { # Form (id) Form } { # IdentityDictionary (id) IdentityDictionary } { # IdentitySet (id) IdentitySet } { # Interval (id) Interval } { # Line (id) Line } { # Link (id) Link } { # LinkedList (id) LinkedList } { # LookupKey (id) LookupKey } { # Magnitude (id) Magnitude } { # MappedCollection (id) MappedCollection } { # Object (id) Object } { # OrderedCollection (id) OrderedCollection } { # PositionableStream (id) PositionableStream } { # ReadStream (id) ReadStream } { # ReadWriteStream (id) ReadWriteStream } { # Rectangle (id) Rectangle } { # RunArray (id) RunArray } { # ScheduledControllers (id) ScheduledControllers } { # SequenceableCollection (id) SequenceableCollection } { # Set (id) Set } { # SortedCollection (id) SortedCollection } { # StandardSystemView (id) StdSysView } { # Stream (id) Stream } { # String (id) String } { # StringHolder (id) StringHolder } { # StringHolderView (id) StringHolderView } { # SwitchController (id) SwitchController } { # SwitchView (id) SwitchView } { # Symbol (id) Symbol } { # Text (id) Text } { # View (id) View } { # WriteStream (id) WriteStream } { # display (id) currentWindow } { # sensor (id) currentWindow } { # Sensor (id) currentWindow } "Globals.rules ===============================================================" { # super (id) super } { # false (BOOL)NO } { # nil (id)nil } { # true (BOOL)YES } { # aPoint (PT) aPoint } { # len (int) len } { species # (id)'%0->isa'} { next:(int) # (id)[next:(int)%1] } { next:(int) put:(id) # (id)[next:(int)%1 put:(id)%2] } { # (id)[copyFrom:(int)anInteger to:(int)readLimitInteger] } { # (id)[new:(int)anInteger] } { # (id)[at:(int)anInteger] } { # (id)[at:(int)anInteger put:anObject] } { # (int)[size] } "Numbers.rules ===============================================================" { & (BLOCK) # (int)'%0 && %1' } { & (BOOL) # (int)'%0 && %1' } { eqv:(BOOL) # (int)'%0 == %1' } { not # (BLOCK)'!%0' } { xor:(BOOL) # (int)'%0 ^ %1' } { | (BOOL) # (int)'%0 | %1' } { and:(BLOCK) # (int)'%0 && %1' } { (int) < (int) # (BOOL) '%0 < %1' } { (int) <= (int) # (BOOL) '%0 <= %1' } { (int) = (int) # (BOOL) '%0 == %1' } { (int) > (int) # (BOOL) '%0 > %1' } { (int) >= (int) # (BOOL) '%0 >= %1' } { (float) < (float) # (BOOL) '%0 < %1' } { (float) <= (float) # (BOOL) '%0 <= %1' } { (float) = (float) # (BOOL) '%0 == %1' } { (float) > (float) # (BOOL) '%0 > %1' } { (float) >= (float) # (BOOL) '%0 >= %1' } { (int) < (float) # (BOOL) '%0 < %1' } { (int) <= (float) # (BOOL) '%0 <= %1' } { (int) = (float) # (BOOL) '%0 == %1' } { (int) > (float) # (BOOL) '%0 > %1' } { (int) >= (float) # (BOOL) '%0 >= %1' } { (float) < (int) # (BOOL) '%0 < %1' } { (float) <= (int) # (BOOL) '%0 <= %1' } { (float) = (int) # (BOOL) '%0 == %1' } { (float) > (int) # (BOOL) '%0 > %1' } { (float) >= (int) # (BOOL) '%0 >= %1' } { ifFalse:(BLOCK) # (int)'if (!%0) %1' } { ifFalse:(BLOCK) ifTrue:(BLOCK) # (int)'(!%0) ? %1 : %2' } { ifFalse:(BLOCK) ifTrue:(BLOCK) # (STMT)'if (!%0) %1 else %2' } { ifTrue:(BLOCK) # (STMT)'if (%0) %1' } { ifTrue:(BLOCK) ifFalse:(BLOCK) # (int)'(%0) ? %1 : %2' } { ifTrue:(BLOCK) ifFalse:(BLOCK) # (STMT)'if (%0) %1 else %2' } { or:(BLOCK) # (BOOL) '%0 | %1' } { (int)+ (int) # (int) '%0 + %1' } { (int)- (int) # (int) '%0 - %1' } { (int)* (int) # (int) '(%0) * (%1)' } { (int)/ (int) # (int) '(%0) / (%1)' } { (int)// (int) # (int) '(%0) // (%1)' } { (int)abs # (int) abs((int)%0) } { (int)negated # (int) '-%0' } { (int)quo:(int) # (int) quo(%0) } { (int)reciprocal # (int) '1/(%0)' } { (int)rem:(int) # (int) '%0 \% %1' } { (int)raisedTo:(float) # (int) '(int)raiseToPower((float)(%0), (float)(%1))' } { (int)raisedToInteger:(int) # (int) '(int)raiseToPowerInt((float)(%0), %1)' } { (int)raisedToInteger:(float) # (int) '(int)raiseToPowerInt((float)(%0), %1)' } { (int)sin # (int) '(int)sin((float)(%0))' } { (int)sqrt # (int) '(int)sqrt((float)(%0))' } { (int)tan # (int) '(int)tan((float)(%0))' } { (int)\\ (int) # (int) '%0 \\ %1' } { (int)raisedTo:(int) # (int) 'raiseToPower(%0, %1)' } { (int)raisedToInteger:(int) # (int) 'raiseToPointInt(%0, %1)' } { (int)squared # (int) '%0*%0' } { (int)even # (int) '(%0 & 1) == 0' } { (int)negative # (int) '-%0' } { (int)odd # (int) '(%0 & 1) != 0' } { (int)positive # (int) '%0 >= 0' } { (int)sign # (int) '%0 < 0' } { (int)strictlyPositive # (int) '%0 > 0' } { (int)rounded # (int) '%0' } { (int)roundTo:(int) # (int) roundTo((int)%0, (int)%1) } { (int)truncated # (int) truncated((int)%0) } { (int)truncateTo:(int) # (int) truncateTo(%0, %1) } { (int)coerce:(int) # (int) coerce(%0, %1) } { (int)generality # (int) generality(%0) } { (int)retry:(BLOCK) coercing:(int) # (int) retryCoercing(%0, %1, %2) } { (int)@ (int) # (PT) pt(%0, %1) } { (int)asInteger # (int) '(int)%0' } { (int)asFloat # (float) '(float)%0' } { (float)asFloat # (float) '%0' } { (int)asPoint # (PT) pt(%0, %0) } { (float)asPoint # (PT) pt(%0, %0) } { (int)to:(int) # (int) to(%0, %1) } { (int)to:(int) by:(int) # (int) toBy(%0, %1, %2) } { (int)to:(int) by:(int) do:(BLOCK) # (STMT) 'for (i = %0; i < %1; i+= %2) %3' } { (int)to:(int) do:(BLOCK) # (STMT) 'for(i = %0; i < %1; i++) %2' } { (int)timesRepeat:(BLOCK) # (STMT) 'for(i = 0; i < %0; i++) %1' } { (float)timesRepeat:(BLOCK) # (STMT) 'for(i = 0; i < %0; i++) %1' } { do:(BLOCK) # (id) 'for(s = [%0 eachElement]; m = [s next];) %1; [s free];' } { whileTrue:(BLOCK) # 'while(%0) %1' } { whileFalse:(BLOCK) # 'while(!%0) %1' } { (int)storeOn:(IOD) # (int) storeOn(%0, %1) } { (int)readFrom:(IOD) # (int) readFrom(%0, %1) } { (float)+ (float) # (float) '%0 + %1' } { (float)- (float) # (float) '%0 - %1' } { (float)* (float) # (float) '(%0) * (%1)' } { (float)/ (float) # (float) '(%0) / (%1)' } { (float)// (float) # (float) '(%0) // (%1)' } { (float)abs # (float) abs((float)%0) } { (float)negated # (float) '-(%0)' } { (float)quo:(float) # (float) 'quo(%0)' } { (float)reciprocal # (float) '1./(%0)' } { (float)rem:(float) # (float) '(%0) \% (%1)' } { (float)\\ (float) # (float) '(%0) \\ (%1)' } { (float)arcCos # (float) arcCos(%0) } { (float)arcSin # (float) arcSin(%0) } { (float)arcTan # (float) arcTan(%0) } { (float)cos # (float) cos(%0) } { (int)cos # (float) 'cos((float)(%0))' } { (float)exp # (fint) exp(%0) } { (int)exp # (float) 'exp((float)(%0))' } { (float)floorLog:(float) # (float) floorLog(%0, %1) } { (float)ln # (float) ln(%0) } { (float)log:(float) # (float) log(%0) } { (float)raisedTo:(float) # (float) raiseToPower(%0, %1) } { (float)raisedToInteger:(float) # (float) raiseToPointInt(%0, %1) } { (float)sin # (float) sin(%0) } { (float)sqrt # (float) sqrt(%0) } { (float)squared # (float) '%0*%0' } { (float)tan # (float) tan(%0) } { (float)even # (float) '(%0 & 1) == 0' } { (float)negative # (float) '-%0' } { (float)odd # (float) '(%0 & 1) != 0' } { (float)positive # (float) '%0 >= 0' } { (float)sign # (float) '%0 < 0' } { (float)strictlyPositive # (float) '%x > 0' } { (float)ceiling # (float) ceil(%0) } { (float)floor # (float) floor(%0) } { (float)rounded # (int) '(int)(%0+.5)' } { (float)roundTo:(float) # (float) roundTo(%0, %1) } { (float)truncated # (int) '((int)%0)' } { (float)truncateTo:(float) # (float) truncateTo(%0, %1) } { (float)coerce:(float) # (float) coerce(%0, %1) } { (float)generality # (int) generality(%0) } { (float)retry:(BLOCK) coercing:(float) # (float) retryCoercing(%0, %1, %2) } { (float)@ (float) # (PT) 'pt((int)%0, (int)%1)' } { (float)asInteger # (int) '(int)(%0)' } { (float)asPoint # (float) 'pt((int)(%0), (int)(%0))' } { (float)degreesToRadians # (float) degreesToRadians(%0) } { (int)degreesToRadians # (float) 'degreesToRadians((float)(%0))' } { (float)radiansToDegrees # (float) radiansToDegrees(%0) } { (int)radiansToDegrees # (float) 'radiansToDegrees((float)(%0))' } { (float)to:(float) # (float) to(%0, %1) } { (float)to:(float) by:(float) # (float) toBy(%0, %1, %2) } { (float)to:(float) by:(float) do:(BLOCK) # (float) 'for (x = %0; x < %1; x += %2) %3' } { (float)to:(float) do:(BLOCK) # (float) 'for(x = %0; x < %1; x++) %2' } { (float)storeOn:(IOD) # (float) storeOn(%0, %1) } { (float)readFrom:(IOD) # (float) readFrom(%0, %1) } "Misc.rules==================================================================" { (id)= (id) # (BOOL) '[%0 isEqual:%1]' } "Which is == and which isEqual?" { (int)= (int) # (BOOL) '%0 == %1' } { (int)= (float) # (BOOL) '%0 == %1' } { (float)= (int) # (BOOL) '%0 == %1' } { (BOOL)= (BOOL) # (BOOL) '%0 == %1' } { (id)== (id) # (BOOL) '%0 == %1' } "Which is == and which isEqual?" { (int)== (int) # (BOOL) '%0 == %1' } { (int)== (float) # (BOOL) '%0 == %1' } { (float)== (float) # (BOOL) '%0 == %1' } { (BOOL)== (BOOL) # (BOOL) '%0 == %1' } { associationsDo:(BLOCK) # 'for(seq = [[%0 associations] eachElement]; obj = [seq next]; ) %1' } "Point.rules==================================================================" { (PT)x # (int) 'ptX(%0)' } { (PT)y # (int) 'ptY(%0)' } { (PT)x: (int) # (PT) 'ptX(%0)=%1' } { (PT)y: (int) # (PT) 'ptY(%0)=%1' } { (PT)< (int) # (BOOL) 'ptIsLess(%0, %1)' } { (PT)<= (int) # (BOOL) 'ptIsLessOrEqual(%0, %1)' } { (PT)= (int) # (BOOL) 'ptIsEqual(%0, %1)' } { (PT)> (int) # (BOOL) 'ptIsGreater(%0, %1)' } { (PT)>= (int) # (BOOL) 'ptIsGreaterOrEqual(%0, %1)' } { (PT) hash # (int) '%0' } { (PT) hashMappedBy:(int) # (PT) '%0' } { (int) max:(int) # (int) 'min(%0, %1)' } { (int) min:(int) # (int) 'max(%0, %1)' } { (float) max:(float) # (float) 'min(%0, %1)' } { (float) min:(float) # (float) 'max(%0, %1)' } { (PT) max:(PT) # (PT) 'ptMax(%0, %1)' } { (PT) min:(PT) # (PT) 'ptMin(%0, %1)' } { (PT) * (PT) # (PT) 'ptTimes(%0, %1)' } { (PT) + (PT) # (PT) 'ptPlus(%0, %1)' } { (PT) - (PT) # (PT) 'ptMinus(%0, %1)' } { (PT) /(PT) # (PT) 'ptSlash(%0, %1)' } { (PT) // (PT) # (PT) 'ptSlashSlash(%0, %1)' } { (PT) * (int) # (PT) 'ptTimesInt(%0, %1)' } { (PT) + (int) # (PT) 'ptPlusInt(%0, %1)' } { (PT) - (int) # (PT) 'ptMinusInt(%0, %1)' } { (PT) / (int) # (PT) 'ptSlashInt(%0, %1)' } { (PT) // (int) # (PT) 'ptSlashSlashInt(%0, %1)' } { (PT) abs # (PT) 'ptAbs(%0)' } { (PT) rounded # (PT) '%0' } { (PT) truncateTo:(PT) # (PT) 'ptTrunc(%0, %1)' } { (PT) r # (PT) 'r(%0)' } { (PT) theta # (int) 'ptTheta(%0)' } { (PT) dist:(PT) # (int) 'ptDist(%0, %1)' } { (PT) dotProduct:(PT) # (PT) 'ptDotProduct(%0, %1)' } { (PT) grid:(PT) # (PT) 'ptGrid(%0, %1)' } { (PT) normal # (PT) 'ptNormal(%0)' } { (PT) pointNearestLine:(PT) to:(PT) # (PT) 'ptNearestLine(%0, %1, %2)' } { (PT) transpose # (PT) 'ptTranspose(%0, %1)' } { (PT) truncatedGrid:(PT) # (PT) 'ptTruncatedGrid(%0, %1)' } { (PT) unitVector # (PT) 'ptUnitVector(%0)' } { (PT) asPoint # (PT) '%0' } { (PT) corner:(PT) # (id) '[Rectangle origin:%0 corner:%1]' } { (PT) extent:(PT) # (id) '[Rectangle origin:%0 extent:%1]' } { (id) extent:(PT) # (id) '[%0 extent:%1]' } { (PT) coerce:(int) # (PT) 'ptCoerce(%0, %1)' } { (PT) generality # (int) 'ptGenerality(%0, %1)' } { (PT) scaleBy:(PT) # (PT) 'ptScaleBy(%0, %1)' } { (PT) translateBy:(PT) # (PT) 'ptPlus(%0, %1)' } { (PT) deepCopy # (PT) '%0' } { (PT) shallowCopy # (PT) '%0' } { (PT) printOn:(PT) # (PT) 'ptPrn(%0)' } { (PT) storeOn:(PT) # (PT) 'ptPrn(%0)' } "DisplayObjects.rules========================================================" { # (float) [direction] } { # (BOOL) [contains:aRectangle] } { # (BOOL) [containsPoint:(PT)aPoint] } { # (BOOL) [cursorEnterView] } { # (BOOL) [cursorExitView] } { # (BOOL) [cursorMove] } { # (BOOL) [cursorStill] } { # (BOOL) [doEvent] } { # (BOOL) [eventStillOcurring] } { # (BOOL) [intersects:aRectangle] } { # (BOOL) [isEqual:aRectangle] } { # (BOOL) [isLocked] } { # (BOOL) [isSelectionSelected] } { # (BOOL) [isTopView] } { # (BOOL) [isUnlocked] } { # (BOOL) [keyboardEvent] } { # (BOOL) [leftButtonDown] } { # (BOOL) [leftButtonUp] } { # (BOOL) [middleButtonDown] } { # (BOOL) [middleButtonUp] } { # (BOOL) [noscale] } { # (BOOL) [rightButtonDown] } { # (BOOL) [rightButtonUp] } { # (BOOL) [timeoutEvent] } { # (BOOL) [windowChangedEvent] } { # (BOOL) [anyButtonChanged] } { # (BOOL) [anyButtonDown] } { # (BOOL) [contains:aRectangle] } { # (BOOL) [containsPoint:(PT)aPoint] } { # (BOOL) [intersects:r] } { # (BOOL) [isContainedBy:aRectangle] } { # (BOOL) [isEqual:aRectangle] } { # (BOOL) [leftButtonChanged] } { # (BOOL) [leftButtonDown] } { # (BOOL) [leftButtonUp] } { # (BOOL) [middleButtonChanged] } { # (BOOL) [middleButtonDown] } { # (BOOL) [middleButtonUp] } { # (BOOL) [noButtonChanged] } { # (BOOL) [noButtonDown] } { # (BOOL) [rightButtonChanged] } { # (BOOL) [rightButtonDown] } { # (BOOL) [rightButtonUp] } { # (BYTE) [readEvent] } { # (id) [asForm] } { # (id) [destinationForm] } { # (id) [form] } { # (id) [mask] } { # (int) [outputMedium] } { # (PT) [amountToTranslateWithin:aRectangle] } { # (PT) [applyInverseTo:(PT)aPoint] } { # (PT) [applyTo:(PT)aPoint] } { # (PT) [bottomCenter] } { # (PT) [bottomLeft] } { # (PT) [bottomRight] } { # (PT) [center] } { # (PT) [centerLeft] } { # (PT) [centerRight] } { # (PT) [corner] } { # (PT) [extent] } { # (PT) [origin] } { # (PT) [scale] } { # (PT) [topCenter] } { # (PT) [topLeft] } { # (PT) [topRight] } { # (PT) [translation] } { # (PT) [amountToTranslateWithin:aRectangle] } { # (PT) [bottomCenter] } { # (PT) [bottomLeft] } { # (PT) [bottomRight] } { # (PT) [center] } { # (PT) [centerLeft] } { # (PT) [centerRight] } { # (PT) [corner] } { # (PT) [extent] } { # (PT) [location] } { # (PT) [origin] } { # (PT) [topCenter] } { # (PT) [topLeft] } { # (PT) [topRight] } { # (PT) [waitButton] } { # (id) [boundingBox] } { # (id) [clippingRectangle] } { # (id) [compositionRectangle] } { # (id) [computeBoundingBox] } { # (id) [frame] } { # (id) [visibleRectangle] } { # (int) [rule] } { # (STR) [string] } { # (STYLE) [alignment] } { # (id) [openAt:p] } { # (id) [takeControl:p] } { # (int) [area] } { # (int) [bottom] } { # (int) [fileDescriptor] } { # (int) [hash] } { # (int) [hashMappedBy:map] } { # (int) [height] } { # (int) [left] } { # (int) [lineGrid] } { # (int) [numberOfLines] } { # (int) [paddingWidth] } { # (int) [right] } { # (int) [top] } { # (int) [valueAt:(PT)aPoint] } { # (int) [width] } { # (int) [baseline] } { # (int) [bottom] } { # (int) [composeAll] } { # (int) [compositionRectangleDelta] } { # (int) [count] } { # (int) [dyForPoint:(PT)pt] } { # (int) [fileDescriptor] } { # (int) [firstIndent] } { # (int) [firstIndex] } { # (int) [hash] } { # (int) [hashMappedBy:map] } { # (int) [height] } { # (int) [indexOf:aSomething] } { # (int) [lastIndex] } { # (int) [left] } { # (int) [leftMarginForCompositionForLine:(int)lineIndex] } { # (int) [leftMarginForDisplayForLine:(int)lineIndex] } { # (int) [leftMarginTabAt:(int)anInt] } { # (int) [lineGrid] } { # (int) [lineIndexOfCharacterIndex:(int)characterIndex] } { # (int) [lineIndexOfTop:(int)top] } { # (int) [lines:anArray] } { # (int) [marginTabsLevel] } { # (int) [numberOfLines] } { # (int) [restIndent] } { # (int) [right] } { # (int) [rightIndent] } { # (int) [rightMarginForComposition] } { # (int) [rightMarginForDisplay] } { # (int) [rightMarginTabAt:(int)huh] } { # (int) [rightX] } { # (int) [senseDelay:(int)msec] } { # (int) [senseDelay:time] } { # (int) [top] } { # (int) [topAtLineIndex:(int)lineIndex] } { # (int) [valueAt:(PT)aPoint] } { # (int) [width] } { # (BITS) [bits] } { # (id) [align:(PT)aPoint1 with:(PT)aPoint2] } { # (id) [append:aLink] } { # (id) [area:aRectangle] } { # (id) [areasDiffering:aRectangle] } { # (id) [areasOutside:aRectangle] } { # (id) [asParagraph] } { # (id) [asString] } { # (id) [asText] } { # (id) [backgroundAt:(PT)aPoint] } { # (id) [beCursor] } { # (id) [black] } { # (id) [black:aRectangle] } { # (id) [border:aRectangle width:(int)borderWidth] } { # (id) [border:aRectangle width:(int)borderWidth mask:aHalfTone] } { # (id) [border:aRectangle widthRectangle:insets mask:aHalfTone] } { # (id) [border:aRectangle widthRectangle:insets mask:aHalfTone clipBy:aClipRectList] } { # (id) [borderWidth:(int)aWidth] } { # (id) [borderWidth:(int)aWidth mask:aMask] } { # (id) [bottom:(int)anInteger] } { # (id) [boundingBox] } { # (id) [centerX:(int)anInteger] } { # (id) [centerY:(int)anInteger] } { # (id) [centered] } { # (id) [characterBlockAtPoint:(PT)aPoint] } { # (id) [characterBlockForIndex:(int)targetIndex] } { # (id) [clearIndents] } { # (id) [clearVisibleRectangle] } { # (id) [clipHeight:(int)anInteger] } { # (id) [clipList] } { # (id) [clipList:aClipRectList] } { # (id) [clipWidth:(int)anInteger] } { # (id) [clipX:(int)anInteger] } { # (id) [clipY:(int)anInteger] } { # (id) [clippingRectangle:aRectangle] } { # (id) [color:ignored] } { # (id) [combinationRule:(int)anInteger] } { # (id) [composeForm] } { # (id) [compositionRectangle:compRectangle] } { # (id) [compositionRectangle:compositionRect text:aText style:aTextStyle offset:(PT)aPoint outputMedium:(int)aSymbol fitWidth:(BOOL)aBoolean] } { # (id) [computeBoundingBox] } { # (id) [convexShapeFill:aMask] } { # (id) [copyBits] } { # (id) [copyBitsAgain] } { # (id) [copyFrom:(PT)sourcePoint to:(PT)destPoint extent:(PT)extentPoint form:sourceForm clipBy:aClipRectList rule:(int)rule mask:halftoneForm] } { # (id) [copyFromArea:destRectangle toPoint:(PT)destPoint form:sourceForm clipBy:aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyFromArea:sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyFromArea:sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:aClipRectList rule:(int)rule mask:aForm] } { # (id) [copyFromArea:sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:aClipRectList rule:(int)rule mask:halftoneForm] } { # (id) [copyLinesFrom:(int)firstIndex to:(int)lastIndex] } { # (id) [copyStr:(STR)sourceString font:aFont at:(PT)destOrigin clipBy:aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:destRectangle fromPoint:(PT)destPoint form:sourceForm rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:destRectangle fromPoint:(PT)sourcePt form:sourceForm rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:destRectangle fromPoint:(PT)sourcePt form:sourceForm rule:(int)rule mask:halftoneForm] } { # (id) [corner:(PT)c1 corner:(PT)c2] } { # (id) [corner:(PT)cornerPoint] } { # (id) [cursorLink:(BOOL)yesno] } { # (id) [darkGray] } { # (id) [darkGray:aRectangle] } { # (id) [deepCopy] } { # (id) [defaultNib:(int)widthInteger] } { # (id) [deltaMarginTabsLevel:(int)anInteger] } { # (id) [destForm:aForm] } { # (id) [destForm:df sourceForm:sf halftoneForm:hf combinationRule:(int)rule destOrigin:(PT)destOriginPoint sourceOrigin:(PT)sourceOriginPoint extent:(PT)anExtent clipList:aClipRectList] } { # (id) [destOrigin:(PT)aPoint] } { # (id) [destRect:aRectangle] } { # (id) [destX:(int)anInteger] } { # (id) [destY:(int)anInteger] } { # (id) [destinationForm:aFormOrRectangle] } { # (id) [display] } { # (id) [displayAt:(PT)aDisplayPoint] } { # (id) [displayAt:(PT)aPoint] } { # (id) [displayCaretAt:(PT)aPoint] } { # (id) [displayCaretAt:(PT)aPoint andClip:clipBox] } { # (id) [displayCaretForBlock:aCharacterBlock] } { # (id) [displayLinesFrom:(int)firstIndex to:(int)lastIndex] } { # (id) [displayOn:aDisplay transformation:displayTransformation clipBy:aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:aClipRectList] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:aClipRectList rule:(int)combinationRule mask:maskForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint rule:(int)ruleInteger] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList fixedPoint:(PT)aPoint] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [dotOfSize:(int)diameter] } { # (id) [down] } { # (id) [dragon:(int)order] } { # (id) [drawFrom:(PT)startPoint to:(PT)stopPoint] } { # (id) [drawLine:sourceForm from:(PT)beginPoint to:(PT)endPoint clipBy:aClipRectList rule:(int)anInteger mask:aForm] } { # (id) [drawLine:sourceForm from:(PT)beginPoint to:(PT)endPoint clipBy:aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [drawLineAround:aRectangle clipBy:cr rule:(int)combinationRule] } { # (id) [drawLineFrom:(PT)bp to:(PT)ep clipBy:aClipRectList rule:(int)combinationRule] } { # (id) [drawLineFrom:(PT)bp to:(PT)ep clipBy:aClipRectList rule:(int)rule] } { # (id) [drawLoopDeltaX:(int)xDelta deltaY:(int)yDelta] } { # (id) [example] } { # (id) [expandByInt:(int)delta] } { # (id) [expandByPoint:(PT)delta] } { # (id) [expandByRectangle:aRectangle] } { # (id) [extent:(PT)aPoint] } { # (id) [extent:(PT)anExtent figureBits:(BITS)figureBits shapeBits:(BITS)shapeBits] } { # (id) [extent:(PT)extentPoint] } { # (id) [extent:(PT)extentPoint bits:(BITS)theBits] } { # (id) [figure] } { # (id) [figure:figureForm shape:shapeForm] } { # (id) [filberts:(int)n side:(int)s] } { # (id) [fill:aRectangle] } { # (id) [fill:aRectangle mask:aForm] } { # (id) [fill:aRectangle rule:(int)anInteger mask:aForm] } { # (id) [fill:aRectangle rule:(int)combinationRule mask:halftoneForm] } { # (id) [fill:aRectangle rule:(int)combinationRule mask:halftoneForm clipBy:aClipRectList] } { # (id) [fillIn:aBlock] } { # (id) [first] } { # (id) [firstIndent:(int)anInteger] } { # (id) [fit] } { # (id) [flash] } { # (id) [flash:aRectangle] } { # (id) [follow:locationBlock while:durationBlock] } { # (id) [form] } { # (id) [frame:aClipRectList] } { # (id) [free] } { # (id) [freeAll] } { # (id) [fromDisplay:aRectangle] } { # (id) [fromUser] } { # (id) [fromUser:(PT)aPoint] } { # (id) [fromUser:(PT)gridPoint] } { # (id) [fromUser:(PT)originPoint] } { # (id) [fromUserAspectRatio:(PT)aspectPoint] } { # (id) [go:distance] } { (id)go:(int) # (id) '[%0 go:(float)(distance)]' } { (id)go:(float) # (id) '[%0 go:distance]' } { # (id) [goto:(PT)aPoint] } { # (id) [gray] } { # (id) [gray:aRectangle] } { # (id) [gridWithLead:(int)leadInteger] } { # (id) [height:(int)anInteger] } { # (id) [height:(int)heightInteger] } { # (id) [hilbert:n side:s] } { # (id) [hilberts:(int)n] } { # (id) [home] } { # (id) [initialize] } { # (id) [insert:aLink] } { # (id) [insetByInt:(int)delta] } { # (id) [insetByPoint:(PT)delta] } { # (id) [insetByRectangle:aRectangle] } { # (id) [insetDisplayBox] } { # (id) [insetOriginBy:(PT)originDeltaPoint cornerBy:(PT)cornerDeltaPoint] } { # (id) [intersect:r] } { # (id) [justified] } { # (id) [last] } { # (id) [left:(int)aLeft top:(int)aTop width:(int)aWidth height:(int)aHeight] } { # (id) [left:(int)anInteger] } { # (id) [left:(int)left top:(int)top right:(int)right bottom:(int)bottom] } { # (id) [left:(int)leftNumber right:(int)rightNumber top:(int)topNumber bottom:(int)bottomNumber] } { # (id) [leftFlush] } { # (id) [lightGray] } { # (id) [lightGray:aRectangle] } { # (id) [lineAt:(int)indexInteger put:aTextLineInterval] } { # (id) [lineAt:(int)lineIndex] } { # (id) [lines] } { # (id) [linkAt:(int)anInt] } { # (id) [lock] } { # (id) [lock:aRectangle] } { # (id) [magnifyBy:(PT)scale] } { # (id) [mandala:(int)npoints diameter:(int)d] } { # (id) [marginTabsLevel:(int)anInteger] } { # (id) [mask:aForm] } { # (id) [mask:maskForm] } { # (id) [merge:r] } { # (id) [mergeWith:r] } { # (id) [mouseSelect:previousStartBlock to:previousStopBlock] } { # (id) [moveBy:(PT)aPoint] } { # (id) [moveBy:(PT)aPoint with:aClipRectList] } { # (id) [moveBy:(int)aPoint] } { # (id) [moveTo:(PT)aPoint] } { # (id) [moveTo:(PT)newLoc restoring:background] } { # (id) [new] } { # (id) [north] } { # (id) [offset:(PT)aPoint] } { # (id) [origin:(PT)aPoint] } { # (id) [origin:(PT)op corner:(PT)cp] } { # (id) [origin:(PT)originPoint] } { # (id) [origin:(PT)originPoint corner:(PT)cornerPoint] } { # (id) [origin:(PT)originPoint extent:(PT)extentPoint] } { # (id) [origin:(PT)originPoint extent:(PT)extentPoint bits:(BITS)theBits] } { # (id) [origin:(PT)originPoint extent:(PT)extentPoint window:(int)aFd] } { # (id) [originFromUser:(PT)extentPoint] } { # (id) [originFromUser:(PT)extentPoint grid:(int)scaleFactor] } { # (id) [outline] } { # (id) [outputMedium:(int)aSymbol] } { # (id) [paintBits] } { # (id) [place:(PT)aPoint] } { # (id) [predecessorOf:aLink] } { # (id) [print] } { # (id) [putEventBack] } { # (id) [receiver:anObject selector:(SEL)aSelector] } { # (id) [recomposeIn:compositionRect clipBy:clippingRect] } { # (id) [relativeRectangle] } { # (id) [remove:aLink] } { # (id) [removeFirstChars:numberOfChars] } { # (id) [replaceFrom:(int)start to:(int)stop with:aText displaying:(BOOL)displayBoolean] } { # (id) [repositionAt:(PT)aPoint clipBy:clippingBox] } { # (id) [resetLocks] } { # (id) [restIndent:(int)anInteger] } { # (id) [reverse] } { # (id) [reverse:aRectangle] } { # (id) [reverse:aRectangle mask:aMask] } { # (id) [reverseFrom:characterBlock1 to:characterBlock2] } { # (id) [reverseFrom:characterBlock1 to:characterBlock2 andClip:clipBox] } { # (id) [reverseRectangle:aRectangle] } { # (id) [right:(int)anInteger] } { # (id) [rightFlush] } { # (id) [rightIndent:(int)anInteger] } { # (id) [rounded] } { # (id) [rule:(int)ruleInteger] } { # (id) [scaleBy:(int)scale] } { # (id) [scrollBy:(int)height grid:(int)grid] } { # (id) [scrollBy:(int)heightToMove] } { # (id) [selectWord:stringIndex] } { # (id) [senseAllButtons:(BOOL)onoff] } { # (id) [senseLeftButton:(BOOL)onoff] } { # (id) [senseMiddleButton:(BOOL)onoff] } { # (id) [senseMove:(BOOL)onoff] } { # (id) [senseMoveWhileButtonDown:(BOOL)onoff] } { # (id) [senseRightButton:(BOOL)onoff] } { # (id) [senseStill:(BOOL)onoff] } { # (id) [senseWindowEnter:(BOOL)onoff] } { # (id) [senseWindowExit:(BOOL)onoff] } { # (id) [setFigure:figureForm shape:shapeForm] } { # (id) [setInputMasks] } { # (id) [setMask:(int)anEventMask to:(BOOL)onoff] } { # (id) [shape] } { # (id) [shape:aSolidForm] } { # (id) [sourceForm:aForm] } { # (id) [sourceOrigin:(PT)aPoint] } { # (id) [sourceRect:aRectangle] } { # (id) [sourceX:(int)anInteger] } { # (id) [sourceY:(int)anInteger] } { # (id) [spiral:(int)n angle:(float)a] } { # (id) [str:(STR)sourceString font:aFont at:(PT)destOrigin rule:(int)combinationRule mask:halftoneForm] } { # (id) [successor] } { # (id) [successor:aLink] } { # (id) [text] } { # (id) [text:aText] } { # (id) [text:aText textStyle:aTextStyle] } { # (id) [text:aText textStyle:aTextStyle offset:(PT)aPoint] } { # (id) [textAt:(int)lineIndex] } { # (id) [textStyle] } { # (id) [textStyle:aTextStyle] } { # (id) [toReverse:aRectangle] } { # (id) [toggleAlignment] } { # (id) [top:(int)anInteger] } { # (id) [trackFunction:(IMP)aTrackingFunction] } { # (id) [translateBy:(PT)aPoint] } { # (id) [translateByInt:(int)factor] } { # (id) [trimLinesTo:(int)lastLineInteger] } { (id)turn:(int) # (id) '[%0 turn:((float)%1)]' } { (id)turn:(float) # (id) '[%0 turn:%1]' } { # (id) [unlock] } { # (id) [up] } { # (id) [updateCompositionHeight] } { # (id) [updateOrigin:(PT)anOrigin extent:(PT)anExtent] } { # (id) [value] } { # (id) [valueAt:(PT)aPoint put:(int)bitValue] } { # (id) [valueAt:(PT)aPoint put:(int)value] } { # (id) [veryLightGray] } { # (id) [veryLightGray:aRectangle] } { # (id) [waitButton] } { # (id) [waitNoButton] } { # (id) [white] } { # (id) [white:aRectangle] } { # (id) [width:(int)anInteger] } { # (id) [width:(int)widthInteger] } { # (id) [windowChanged] } { # (id) [with:aForm] } { # (id) [withText:aText] } { # (id) [withText:aText style:aTextStyle] } { # (id) [withText:aText style:aTextStyle compositionRectangle:compRect clippingRectangle:aClipRectList] } { # (BOOL) [contains:(id)aRectangle] } { # (BOOL) [intersects:(id)aRectangle] } { # (BOOL) [contains:(id)aRectangle] } { # (BOOL) [intersects:(id)r] } { # (BOOL) [isContainedBy:(id)aRectangle] } { # (PT) [amountToTranslateWithin:(id)aRectangle] } { # (id) [boundingBox] } { # (id) [clippingRectangle] } { # (id) [compositionRectangle] } { # (id) [computeBoundingBox] } { # (id) [frame] } { # (id) [visibleRectangle] } { # (id) [area:(id)aRectangle] } { # (id) [areasDiffering:(id)aRectangle] } { # (id) [areasOutside:(id)aRectangle] } { # (id) [black:(id)aRectangle] } { # (id) [border:(id)aRectangle width:(int)borderWidth] } { # (id) [border:(id)aRectangle width:(int)borderWidth mask:aHalfTone] } { # (id) [border:(id)aRectangle widthRectangle:(id)insets mask:aHalfTone] } { # (id) [border:(id)aRectangle widthRectangle:(id)insets mask:aHalfTone clipBy:(id)aClipRectList] } { # (id) [clipList:(id)aClipRectList] } { # (id) [clippingRectangle:(id)aRectangle] } { # (id) [compositionRectangle:(id)compRectangle] } { # (id) [compositionRectangle:(id)compositionRect text:aText style:aTextStyle offset:(PT)aPoint outputMedium:(int)aSymbol fitWidth:(BOOL)aBoolean] } { # (id) [copyFrom:(PT)sourcePoint to:(PT)destPoint extent:(PT)extentPoint form:sourceForm clipBy:(id)aClipRectList rule:(int)rule mask:halftoneForm] } { # (id) [copyFromArea:(id)destRectangle toPoint:(PT)destPoint form:sourceForm clipBy:(id)aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyFromArea:(id)sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:(id)aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyFromArea:(id)sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:(id)aClipRectList rule:(int)rule mask:aForm] } { # (id) [copyFromArea:(id)sourceRect toPoint:(PT)destOrigin form:sourceForm clipBy:(id)aClipRectList rule:(int)rule mask:halftoneForm] } { # (id) [copyStr:(STR)sourceString font:aFont at:(PT)destOrigin clipBy:(id)aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:(id)destRectangle fromPoint:(PT)destPoint form:sourceForm rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:(id)destRectangle fromPoint:(PT)sourcePt form:sourceForm rule:(int)combinationRule mask:halftoneForm] } { # (id) [copyToArea:(id)destRectangle fromPoint:(PT)sourcePt form:sourceForm rule:(int)rule mask:halftoneForm] } { # (id) [darkGray:(id)aRectangle] } { # (id) [destForm:df sourceForm:sf halftoneForm:hf combinationRule:(int)rule destOrigin:(PT)destOriginPoint sourceOrigin:(PT)sourceOriginPoint extent:(PT)anExtent clipList:(id)aClipRectList] } { # (id) [destRect:(id)aRectangle] } { # (id) [displayCaretAt:(PT)aPoint andClip:(id)clipBox] } { # (id) [displayOn:aDisplay transformation:displayTransformation clipBy:(id)aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:(id)aClipRectList] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:(id)aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:(id)aClipRectList rule:(int)combinationRule mask:maskForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:(id)aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium at:(PT)aDisplayPoint clipBy:(id)aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)combinationRule mask:halftoneForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList align:(PT)alignmentPoint with:(PT)relativePoint rule:(int)ruleInteger mask:aForm] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList fixedPoint:(PT)aPoint] } { # (id) [displayOn:aDisplayMedium transformation:displayTransformation clipBy:(id)aClipRectList rule:(int)ruleInteger mask:aForm] } { # (id) [drawLine:sourceForm from:(PT)beginPoint to:(PT)endPoint clipBy:(id)aClipRectList rule:(int)anInteger mask:aForm] } { # (id) [drawLine:sourceForm from:(PT)beginPoint to:(PT)endPoint clipBy:(id)aClipRectList rule:(int)combinationRule mask:halftoneForm] } { # (id) [drawLineAround:(id)aRectangle clipBy:(id)cr rule:(int)combinationRule] } { # (id) [drawLineFrom:(PT)bp to:(PT)ep clipBy:(id)aClipRectList rule:(int)combinationRule] } { # (id) [drawLineFrom:(PT)bp to:(PT)ep clipBy:(id)aClipRectList rule:(int)rule] } { # (id) [expandByRectangle:(id)aRectangle] } { # (id) [fill:(id)aRectangle] } { # (id) [fill:(id)aRectangle mask:aForm] } { # (id) [fill:(id)aRectangle rule:(int)anInteger mask:aForm] } { # (id) [fill:(id)aRectangle rule:(int)combinationRule mask:halftoneForm] } { # (id) [fill:(id)aRectangle rule:(int)combinationRule mask:halftoneForm clipBy:(id)aClipRectList] } { # (id) [flash:(id)aRectangle] } { # (id) [frame:(id)aClipRectList] } { # (id) [fromDisplay:(id)aRectangle] } { # (id) [gray:(id)aRectangle] } { # (id) [insetByRectangle:(id)aRectangle] } { # (id) [intersect:(id)r] } { # (id) [lightGray:(id)aRectangle] } { # (id) [lock:(id)aRectangle] } { # (id) [merge:(id)r] } { # (id) [mergeWith:(id)r] } { # (id) [moveBy:(PT)aPoint with:(id)aClipRectList] } { # (id) [recomposeIn:(id)compositionRect clipBy:(id)clippingRect] } { # (id) [repositionAt:(PT)aPoint clipBy:(id)clippingBox] } { # (id) [reverse:(id)aRectangle] } { # (id) [reverse:(id)aRectangle mask:aMask] } { # (id) [reverseRectangle:(id)aRectangle] } { # (id) [sourceRect:(id)aRectangle] } { # (id) [toReverse:(id)aRectangle] } { # (id) [veryLightGray:(id)aRectangle] } { # (id) [white:(id)aRectangle] } { # (id) [withText:aText style:aTextStyle compositionRectangle:(id)compRect clippingRectangle:(id)aClipRectList] } \Rogue\Monster\ else echo "will not over write ./rules/generic.ru" fi echo "Finished archive 5 of 5" exit ---- Dieter H. Zebbedies ('dee-ter ayech 'zeb-ed-eez) Zebb-Hoff Mach. Tool's Automated Manufacturing Project Cleveland, OH (USnail): 9535 Clinton Rd, Cleveland, OH 44144 (+216 631 6100) (+216 741-5994) (UUCP): ...{decvax,sun,cbosgd}!cwruecmp!zhmti!dieter (CSNET/ARPA/BITNET): dieter@CWRU.EDU