home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / forthmacs / htmldocs / !Forthmacs / docs / html / interprete < prev    next >
Encoding:
Text File  |  1996-02-23  |  17.1 KB  |  421 lines

  1. <!-- Forthmacs Formatter generated HTML output -->
  2. <html>
  3. <head>
  4. <title>Text Interpreter</title>
  5. </head>
  6. <body>
  7. <h1>Text Interpreter</h1>
  8. <hr>
  9. <p>
  10. This chapter describes the workings of the Risc-OS Forthmacs Text Interpreter 
  11. (the interpreter that compiles or executes Forth commands).  The chapter 
  12. contrasts the Risc-OS Forthmacs Text Interpreter with a number of more 
  13. conventional implementations which have been used in other Forth systems.  
  14. <p>
  15. <p>
  16. <h2>FIG-Forth Text Interpreter</h2>
  17. <p>
  18. FIG-Forth used a variable  <code><A href="_smal_AQ#2e0"> state </A></code> whose 
  19. value was 0 when interpreting and (hex) C0 when compiling.  The interpreter was 
  20. coded as a single word  <code><A href="_smal_BJ#231"> interpret </A></code> 
  21. which tested  <code><A href="_smal_AQ#2e0"> state </A></code> to determine 
  22. whether to compile or to interpret.  Here is the code: 
  23. <p>
  24. <p><pre>
  25. : INTERPRET ( -- )
  26.    BEGIN  -FIND
  27.       IF   STATE @  <
  28.            IF  CFA , ELSE  CFA EXECUTE  THEN
  29.       ELSE HERE NUMBER  DPL @  1+
  30.            IF   DROP [COMPILE] LITERAL
  31.            ELSE      [COMPILE] DLITERAL
  32.            THEN
  33.       THEN  ?STACK
  34.    AGAIN ;
  35.   
  36. </pre><p>
  37. <p>
  38. The <strong>state @ <</strong> phrase is pretty clever (or disgusting, 
  39. whatever way you wish to look at it).  Since the value stored in  <code><A href="_smal_AQ#2e0"> state </A></code> 
  40. is (hex) C0 when compiling, and since the length byte of a defined word (which 
  41. is left on the stack by <strong>-find</strong> ) is in the range (hex) 80-BF for 
  42. a non-immediate word and in the (hex) C0-FF for an immediate word, the <strong>state @ <</strong> 
  43. test manages to return  <code><A href="_smal_AE#304"> true </A></code> only if 
  44. the  <code><A href="_smal_AQ#2e0"> state </A></code> is compiling and the word 
  45. is not immediate.  This fact is not crucial to our discussion but is included 
  46. here to avoid confusion.  
  47. <p>
  48.  <code><A href="_smal_AQ#2e0"> state </A></code> is explicitly tested once 
  49. inside this loop, but if you look at the code for the word  <code><A href="_smal_AS#252"> literal </A>,</code> 
  50. it also tests  <code><A href="_smal_AQ#2e0"> state </A></code> to decide whether 
  51. to compile the number or not.  
  52. <p>
  53. To switch between compiling and interpreting, FIG-Forth uses two words [ and ].  
  54. [ is immediate and simply stores 0 into  <code><A href="_smal_AQ#2e0"> state </A>.</code> 
  55. ] is not immediate and stores (hex) C0 into  <code><A href="_smal_AQ#2e0"> state </A>.</code> 
  56. Compilation is typically started with : , which is defined something like: 
  57. <p>
  58. <p><pre>
  59. : :
  60.      <some irrelevant stuff>
  61.      ] ;code 
  62.      <some assembly language stuff>
  63.   end-code
  64. </pre><p>
  65. <p>
  66. The important point here is that when : executes to define a new word, the ] 
  67. just sets the  <code><A href="_smal_AQ#2e0"> state </A></code> to compiling, 
  68. then the  <code><A href="_smal_BN#115"> ;code </A></code> proceeds to execute.  
  69. (The purpose of  <code><A href="_smal_BN#115"> ;code </A></code> is to patch the 
  70. code field of the word defined by : so that it does the appropriate thing for a 
  71. high-level forth word).  The interpret word  <code><A href="_smal_BJ#231"> interpret </A></code> 
  72. doesn't notice that  <code><A href="_smal_AQ#2e0"> state </A></code> is now 
  73. compiling until the  <code><A href="_smal_BN#115"> ;code </A></code> finishes.  
  74. <p>
  75. So we see that [ and ] are fairly innocuous; they just change the value of a 
  76. variable.  
  77. <p>
  78. <p>
  79. <h2>Poly-FORTH Text Interpreter</h2>
  80. <p>
  81. Forth, Inc.  decided that it would be preferable to have two separate loops for 
  82. the two separate functions of compiling and interpreting.  The compiling loop 
  83. was called ], so ] actually executed the compile loop directly, rather than just 
  84. setting a variable.  This has two subtle side effects.  
  85. <p>
  86. If you loop at the previous definition of :, and now pretend that instead of 
  87. just setting a variable, ] actually executes the compiler loop, you will see 
  88. that the  <code><A href="_smal_BN#115"> ;code </A></code> following it doesn't 
  89. actually get executed until after the compiling is finished.  This in itself 
  90. doesn't cause a problem for :, but the use of ] inside programmer-defined words 
  91. sometimes caused unexpected behavior because stuff after the ] would get 
  92. executed after a bunch of stuff had been compiled.  
  93. <p>
  94. The other subtlety relates to how the loops are terminated.  Note that the  <code><A href="_smal_BJ#231"> interpret </A></code> 
  95. loop shown above never terminates! We all know that it really does terminate, 
  96. and the mechanism is fairly kludgey.  What happens is that there is a null 
  97. character at the end of every line of text in the input stream, and at the end 
  98. of every  <code><A href="_smal_AV#165"> block </A></code> of text from mass 
  99. storage.  The text interpreter picks up this null character just like a normal 
  100. word.  The dictionary contains an entry which matches this "null word".  The 
  101. associated code is executed, and it plays around with the return stack in such a 
  102. way that the  <code><A href="_smal_BJ#231"> interpret </A></code> loop is exited 
  103. without its ever knowing about it.  
  104. <p>
  105. The problem with the dual-loop interpreter/compiler is that the end of each line 
  106. of input from the input stream kicks out system out of whichever loop it was in.  
  107. If the user is attempting to compile a multi-line colon definition from the 
  108. input stream, he must start each line after the first with an explicit ], 
  109. because once the compiler loop is exited at the end of the first line, the 
  110. system doesn't remember that it was compiling.  
  111. <p>
  112. One key thing to remember is that the compiler loop (which was named [) is 
  113. executed from within the interpreter loop.  
  114. <p>
  115. <p>
  116. <h2>Coroutines - Patton/Berkey</h2>
  117. <p>
  118. At FORML 83, Bob Berkey presented a paper about using coroutines for the 
  119. interpreter loop and the compiler loop, instead of having the compiler loop run 
  120. inside the interpreter loop.  This means that executing ] kicks out the 
  121. interpreter loop and runs the compiler loop instead; similarly, executing [ 
  122. kicks out the compiler loop and runs the interpreter loop instead.  The 
  123. subroutine versions of these loops are present in his scheme, named <strong>compiler</strong> 
  124. and <strong>interpreter</strong> .  
  125. <p>
  126. Bob feels that this scheme is more symmetrical than the Poly-FORTH approach and 
  127. that it eliminates some of the counter-intuitive behavior.  
  128. <p>
  129. This scheme still requires that multi-line colon definitions compiled from the 
  130. keyboard have a ] at the beginning of each line after the first.  
  131. <p>
  132. <p>
  133. <h2>What is Wrong with all this</h2>
  134. <p>
  135. These different schemes do not at all address what I consider to be the 
  136. fundamental problems with the interpreter/compiler.  
  137. <p>
  138. <p>
  139. <h2>Fundamental Problem #1</h2>
  140. <p>
  141. The compiler/interpreter has a built-in infinite loop.  This means that you 
  142. can't tell it to just compile one word; once you start it, off it goes, and it 
  143. won't stop until it gets to the end of the line or screen.  
  144. <p>
  145. <p>
  146. <h2>Fundamental Problem #2</h2>
  147. <p>
  148. The reading of the next word from the input stream is buried inside this loop.  
  149. This means that you can't hand a string representing a word to the 
  150. interpreter/compiler and have it interpret or compile it for you.  
  151. <p>
  152. <p>
  153. <h2>Fundamental Problem #3</h2>
  154. <p>
  155. The behavior of the interpreter/compiler is hard to change because all the 
  156. behavior is hard-wired into one or two relatively large words.  Changing this 
  157. behavior can be extremely useful for a number of applications, for example 
  158. meta-compiling.  
  159. <p>
  160. <p>
  161. <h2>Fundamental Problem #4</h2>
  162. <p>
  163. If the interpreter/compiler can't figure out what to do with a word (it's not 
  164. defined and it's not a number), it aborts.  Worse yet, the aborting is not done 
  165. directly from within the loop, but inside  <code><A href="_smal_AQ#280"> number </A>.</code> 
  166. This severely limits the usefulness of  <code><A href="_smal_AQ#280"> number </A></code> 
  167. because if the string that  <code><A href="_smal_AQ#280"> number </A></code> 
  168. gets is not recognisable as a number, it will abort on you.  (The 83 standard 
  169. punts this issue by not specifying  <code><A href="_smal_AQ#280"> number </A>,</code> 
  170. except as an uncontrolled reference word).  
  171. <p>
  172. <p>
  173. <h2>Solution</h2>
  174. <p>
  175. As I see it, there are several distinct things that are going on inside the 
  176. interpreter/compiler.  A proper factoring of the interpreter/compiler into 
  177. several words, each of which does one thing, solves all these problems.  
  178. <p>
  179. The outermost word is the loop.  The job of the loop is to repetitively get the 
  180. next word from the input stream and do something with it.  The loop should 
  181. terminate when the input stream is exhausted.  
  182. <p>
  183. <strong>Note:</strong> this will all be changed somewhat with ANSI-Forth coming 
  184. up soon.  
  185. <p>
  186. <p><pre>
  187. : interpret  (S -- )
  188.    begin   bl word   ( str )
  189.            dup c@    ( str flag )
  190.            \ flag is 0 if the input stream is exhausted
  191.    while
  192.            "compile
  193.    repeat
  194.    drop
  195. ;
  196. </pre><p>
  197. <p>
  198. The next level down is the "do something with the word".  This ought to be a 
  199. separate word so that it may be called by other words which would like to 
  200. compile/interpret a single word.  This layer is here called  <code>"<A href="_smal_AN#9d"> compile, </A></code> 
  201. because it takes a string representing a single word and compiles (or 
  202. interprets) it.   <code><A href="_smal_AR#71"> "compile </A>s</code> main job is 
  203. to decide what kind of word it is dealing with.  There are 3 choices.  Either 
  204. the word is already defined, or it is a literal (i.e.  a number), or it is 
  205. neither.  
  206. <p>
  207. <p><pre>
  208. : "compile      ( str -- )
  209.         canonical ?comp-local ?exit
  210.         find ( str 0  |   cfa -1   |   cfa 1 )
  211.         dup
  212.         if      do-defined
  213.         else    drop literal?
  214.                 if      double? if do-dliteral else drop do-literal then
  215.                 else    fliteral? if do-fliteral else do-undefined then
  216.                 then
  217.         then ;
  218. </pre><p>
  219. <p>
  220. Finally, at the lowest layer, there is the code which does the appropriate thing 
  221. for each of these three possibilities.  This level is represented by the words  <code><A href="_smal_AU#1c4"> do-defined </A>,</code>  <code><A href="_smal_AX#1c7"> do-literal </A>,</code>  <code><A href="_smal_AV#1c5"> do-dliteral </A>,</code> 
  222. DO-FLITERALand  <code><A href="_smal_BA#1c8"> do-undefined </A>.</code> It is 
  223. only at this lowest layer that the system cares at all whether it is compiling 
  224. or interpreting.  One of the benefits claimed for the Poly-FORTH scheme is 
  225. speed.  This is due to the elimination of tests of the  <code><A href="_smal_AQ#2e0"> state </A></code> 
  226. variable within the loop.  
  227. <p>
  228. Clearly, my scheme has to do something to distinguish between compiling and 
  229. interpreting.  An obvious solution would be to test  <code><A href="_smal_AQ#2e0"> state </A></code> 
  230. inside each of  <code><A href="_smal_AU#1c4"> do-defined </A>,</code>  <code><A href="_smal_AX#1c7"> do-literal </A>,</code>  <code><A href="_smal_AV#1c5"> do-dliteral </A>,</code> 
  231. DO-FLITERAL, and  <code><A href="_smal_BA#1c8"> do-undefined </A>.</code> This 
  232. would slow down the system, of course.  
  233. <p>
  234. A more interesting alternative is to  <code><A href="_smal_AE#1b4"> defer </A></code> 
  235. each of the words  <code><A href="_smal_AU#1c4"> do-defined </A>,</code>  <code><A href="_smal_AX#1c7"> do-literal </A>,</code>  <code><A href="_smal_AV#1c5"> do-dliteral </A>,</code> 
  236. DO-FLITERAL, and  <code><A href="_smal_BA#1c8"> do-undefined </A>.</code> 
  237. (Deferred words are sometimes called execution vectors.  Basically they are like 
  238. variables which hold the address of a word to execute, except that the @  <code><A href="_smal_AM#1ec"> execute </A></code> 
  239. is done automatically) 
  240. <p>
  241. If these words are deferred, then they can be changed when the system switches 
  242. from compiling to interpreting, and vice versa.  
  243. <p>
  244. <p><pre>
  245. defer literal?      ( str -- n true  |  d true  |  str false )
  246. defer do-defined    ( acf -1 | acf 1  -- ?? )
  247. defer do-literal    ( literal -- ?? )
  248. defer do-dliteral   ( dliteral -- ?? )
  249. defer do-fliteral   ( float -- ?? )
  250. defer do-undefined  ( str -- )
  251. : (literal?  ( str -- str false  | dliteral true )
  252.    >r r@ number?   ( d f )
  253.    if    r> drop  true
  254.    else  2drop r> false
  255.    then
  256. ;
  257. ' (literal? is literal?
  258. : interpret-do-defined  ( acf -1 | acf 1 -- ?? )
  259.    drop execute
  260. ;
  261. : compile-do-defined    ( acf -1 | acf 1 -- )
  262.    0> if   execute   ( if immediate )
  263.       else ,         ( if not immediate )
  264.       then
  265. ;
  266. : interpret-do-literal  ( n -- n ) ;
  267. : compile-do-literal    ( n -- )   postpone literal ;
  268. : interpret-do-dliteral ( d -- d ) ;
  269. : compile-do-dliteral   ( d -- )   postpone 2literal ;
  270. : interpret-do-fliteral ( f -- f ) ;
  271. : compile-do-fliteral   ( f -- )   postpone fliteral ;
  272. : interpret-do-undefined ( str -- )
  273.    count type ."  ?" cr
  274.    quit
  275. ;
  276. : compile-do-undefined   ( str -- )
  277.    count type ."  ?" cr
  278.    postpone lose
  279. ;
  280. then [ and ] would be defined as follows:
  281. : [
  282.    ['] interpret-do-defined   is do-defined
  283.    ['] interpret-do-literal   is do-literal
  284.    ['] interpret-do-dliteral  is do-dliteral
  285.    ['] interpret-do-fliteral  is do-fliteral
  286.    ['] interpret-do-undefined is do-undefined
  287.    state off
  288. ; immediate
  289. : ]
  290.    ['] compile-do-defined     is do-defined
  291.    ['] compile-do-literal     is do-literal
  292.    ['] compile-do-dliteral    is do-dliteral
  293.    ['] compile-do-fliteral    is do-fliteral
  294.    ['] compile-do-undefined   is do-undefined
  295.    state on
  296. ;
  297. </pre><p>
  298. <p>
  299.  <code><A href="_smal_BP#237"> is </A></code> sets the word that a deferred word 
  300. actually executes.  
  301. <p>
  302. Executing a deferred word does not need be slow.  Deferred word are so useful 
  303. that they should be coded in assembler for speed.  In Risc-OS Forthmacs they are 
  304. only very slightly slower than normal colon definitions.  
  305. <p>
  306. So what? 
  307. <p>
  308. This may seem to be more complicated than the schemes it replaces.  It certainly 
  309. does have more words.  On the other hand, each word is individually easy to 
  310. understand, and each word does a very specific job, in contrast to the old 
  311. style, which bundles up a lot of different things in one big word.  The more 
  312. explicit factoring gives you a great deal of control over the interpreter.  
  313. <p>
  314. Here are some interesting things you can do with this new scheme: 
  315. <p>
  316. One of my favorite words,  <code><A href="_smal_AJ#219"> h# </A></code> (for 
  317. Temporary Hex): 
  318. <p>
  319. <p><pre>
  320. : h#  ( --word  ?? )
  321.    base @ >r  hex
  322.    blword "compile
  323.    r> base !
  324. ; immediate
  325. </pre><p>
  326. <p>
  327. This word temporarily sets the base to hexadecimal, interprets a word, and 
  328. restores the base.  It works for numbers or defined words, either interpreting 
  329. or compiling.  
  330. <p>
  331. For example: 
  332. <p>
  333. <p><pre>
  334. decimal
  335. th 10 .  \ system prints-->  16
  336. 10 th .  \ system prints-->  a
  337. : strip-parity ( char -- char-without-parity )
  338.    th 7f and
  339. ;
  340. </pre><p>
  341. <p>
  342. Liberal use of this word markedly reduces the need to switch bases, especially 
  343. in source code, and thus reduces the chance of errors.  
  344. <p>
  345. Here's a common word that is trivial to implement with this kind of interpreter: 
  346. <p>
  347. <p><pre>
  348. : ascii (  --name  char )
  349.    bl word 1+ c@ ( char )
  350.    -1 dpl !     \ don't handle it as a double number
  351.    do-literal
  352. ;
  353. </pre><p>
  354. <p>
  355. Here's a word which allows you to make a new name for an old word.  It is a 
  356. smart word, in the sense when the new word is compiled, the old word will 
  357. actually be compiled instead, eliminating any performance penalty.  Furthermore, 
  358. it even works for old words that are immediate! As you will see, the vectored  <code><A href="_smal_AU#1c4"> do-defined </A></code> 
  359. does exactly the thing we want.  
  360. <p>
  361. <p><pre>
  362. : alias  ( -- )     ( Input stream:  new-name old-word )
  363.    create
  364.       bl word find    ( acf -1 | acf 1  |  str false )
  365.       dup if
  366.              , , immediate
  367.       else
  368.              drop  ." can't find "  count type
  369.       then
  370.    does>  2@   ( acf -1 | acf 1 )
  371.       do-defined
  372. ;
  373. ( Examples )
  374. alias d@ 2@
  375. here d@   ( actually executes 2@ )
  376. : foo here d@ ;  ( actually compiles 2@ )
  377. alias forever  again
  378. \ forever actually executes again, which is immediate
  379. : loop-always begin forever ;
  380. </pre><p>
  381. <p>
  382. Finally, a really neat way to write keyword-driven translators.  Suppose you 
  383. have some kind of a file that contains a bunch of text.  Interspersed throughout 
  384. the text are keywords that you would like to recognise, and the program should 
  385. do something special when it sees a keyword.  For things that aren't keywords, 
  386. it just writes them out unchanged.  Suppose that the keywords are <strong>.paragraph</strong> 
  387. , <strong>.section</strong> , and <strong>.end</strong> .  
  388. <p>
  389. <p><pre>
  390. vocabulary keywords definitions
  391. : .paragraph
  392.    \ whatever you want to happen when you see .paragraph
  393. ;
  394. : .section
  395.    \ whatever you want to happen when you see .section
  396. ;
  397. : keywords-do-undefined ( str -- )
  398.    count type
  399. ;
  400. : .end
  401.    only forth
  402.    ['] (literal?              is  literal?
  403.    ['] interpret-do-undefined is  do-undefined
  404. ;
  405. only forth also keywords
  406. : process-keywords
  407.    ['] false                  is literal?
  408.    ['] keywords-do-undefined  is do-undefined
  409.    only keywords
  410. ;
  411. </pre><p>
  412. <p>
  413. I have used this technique to extract specific information from data base files 
  414. produced by a CAD system.  Instead of outputting unrecognised words, I actually 
  415. just ignore them in this application, but the technique is the same in either 
  416. case.  In fact, the document formatter that formatted this manual is written 
  417. using this technique.  
  418. <p>
  419. </body>
  420. </html>
  421.