1 \ recursive descent parser generator )
3 \ Copyright (C) 1995,1996,1997,2000,2003 Free Software Foundation, Inc.
4 \ Copyright 1990, 1991, 1994 Martin Anton Ertl
6 \ This program is free software; you can redistribute it and/or modify
7 \ it under the terms of the GNU General Public License as published by
8 \ the Free Software Foundation; either version 2 of the License, or
9 \ (at your option) any later version.
11 \ This program is distributed in the hope that it will be useful,
12 \ but WITHOUT ANY WARRANTY; without even the implied warranty of
13 \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 \ GNU General Public License for more details.
16 \ You should have received a copy of the GNU General Public License
17 \ along with this program; if not, write to the Free Software
18 \ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
22 : defined? ( "word" -- flag ) bl word find nip ;
29 \ end of ANS FORTH prolog
32 .( Loading Gray ... Copyright 1990-1994 Martin Anton Ertl; NO WARRANTY ) cr
39 s" address-unit-bits" environment? 0=
42 cr .( environmental attribute address-units-bits unknown, computing... ) cr
44 \ if your machine has more bits/au, this assumption wastes space
45 \ if your machine has fewer bits/au, gray will not work
46 : (bits/cell) ( -- n ) 1 0 invert dup 1 rshift xor
47 BEGIN dup 1 = 0= WHILE 1 rshift swap 1+ swap REPEAT drop ;
50 .( You seem to have ) dup 1 cells / . .( bits/address unit) cr
55 constant bits/cell \ !! implementation dependent )
58 postpone 0= ; immediate
61 here 2 cells allot 2! ;
63 : endif postpone then ; immediate
66 ( aborts, if the numbers are not equal )
67 = ?not? abort" mismatched parenthesis" ;
69 : ', \ -- ) ( use: ', name )
75 \ stack administration )
76 \ this implementation is completely unsafe )
80 \ creates a stack called word with n cells )
81 \ the first cell is the stackpointer )
82 create here , cells allot ;
91 [ -1 cells ] literal swap +! ;
93 : clear? \ stack -- f )
100 \ sets - represented as bit arrays )
101 \ bits that represent no elements, must be 0 )
102 \ all operations assume valid parameters )
103 \ elements must be unsigned numbers )
104 \ the max. element size must be declared with max-member )
105 \ no checking is performed )
106 \ set operations allot memory )
109 \ returns a cell with bit# u set and everyting else clear )
112 variable cells/set 0 cells/set !
113 variable empty-ptr 0 empty-ptr ! \ updatd by max-member )
117 : max-member \ u -- )
118 \ declares u to be the maximum member of sets generated afterwards )
119 \ must be called before using any set word except member?, add-member )
122 here empty-ptr ! \ make empty set )
125 : copy-set \ set1 -- set2 )
126 \ makes a copy of set1 )
133 : normalize-bit-addr \ addr1 u1 -- addr2 u2 )
134 \ addr1*bits/cell+u1=addr2*bits/cell+u2, u2<bits/cell )
138 \ the /mod could be optimized into a RSHIFT and an AND, if bits/cell is
139 \ a power of 2, but in an interpreted implementation this would only be
140 \ faster if the machine has very slow division and in a native code
141 \ implementation the compiler should be intelligent enough to optimize
144 : add-member \ u set -- )
145 \ changes set to include u )
146 swap normalize-bit-addr
150 : singleton \ u -- set )
151 \ makes a set that contains u and nothing else )
152 empty copy-set swap over add-member ;
154 : member? \ set u -- f )
155 \ returns true if u is in set )
161 : binary-set-operation \ set1 set2 [w1 w2 -- w3] -- set )
162 \ creates set from set1 and set2 by applying [w1 w2 -- w3] on members )
163 \ e.g. ' or binary-set-operation is the union operation )
166 over @ over @ r@ execute ,
167 cell+ swap cell+ swap
171 : union1 \ set1 set2 -- set )
172 ['] or binary-set-operation ;
174 : intersection \ set1 set2 -- set )
175 ['] and binary-set-operation ;
177 : binary-set-test? \ set1 set2 [w1 w2 -- w3] -- f )
178 \ returns true, if [w1 w2 -- w3] binary-set-operation returns empty )
179 \ e.g. set1 set2 ' and binary-set-test? is true, if set1 and set2
180 \ are disjoint, i.e. they contain no common members )
183 over @ over @ r@ execute 0= ?not? if
184 rot drop false rot rot
186 cell+ swap cell+ swap
190 : notb&and \ w1 w2 -- w3 )
193 : subset? \ set1 set2 -- f )
194 \ returns true if every member of set1 is in set2 )
195 ['] notb&and binary-set-test? ;
197 : disjoint? \ set1 set2 -- f )
198 \ returns true if set1 and set2 heve no common members )
199 ['] and binary-set-test? ;
201 : apply-to-members \ set [ u -- ] -- )
202 \ executes [ u -- ] for every member of set )
203 cells/set @ bits/cell * 0 do
210 : union \ set1 set2 -- set )
211 \ just a little more space-efficient )
214 else 2dup swap subset? if
222 variable test-vector ' abort test-vector !
223 \ here you should store the execution address of a word ( set -- f )
224 \ that returns true if the token of the current symbol is in set )
226 : compile-test \ set -- )
228 test-vector @ compile, ;
231 \ context management )
232 500 stack context-stack
233 \ this stack holds the syntax-exprs currently being treated )
234 \ enlarge it, if your grammar is large and complex )
237 : this \ -- syntax-expr )
238 \ get current syntax-expr )
241 : new-context \ syntax-expr -- )
249 : <builds-field \ n1 n2 -- n3 ) ( defining-word )
250 \ n1 is the offset of the field, n2 its length, n3 the offset of the
251 \ next field; creates a word that contains the offset )
257 : context-var \ use: < offset > size context-var name < offset2 > )
258 \ name returns the address of the offset field of "this" )
259 <builds-field \ n1 n2 -- n3 )
263 : context-const \ use: < offset > context-const name < offset2 > )
264 \ name returns the contents of the field of this at offset )
265 cell <builds-field \ n1 -- n2 )
272 aligned context-const methods
273 \ table of words applicable to the syntax-expr (a map)
274 1 context-var mark-propagate \ used to ensure that "propagate" is
275 \ called at least once for each syntax-expr )
276 1 context-var mark-pass2
277 \ make sure pass2 is called exactly once )
278 aligned cell context-var first-set
279 \ all tokens a nonempty path may begin with )
280 \ if it's equal to 0, the first-set has not been computed yet )
281 1 context-var maybe-empty
282 \ true if the syntax-expr can derive eps )
283 aligned cell context-var follow-set
284 \ the tokens of the terminals that can follow the syntax-expr )
285 s" gforth" environment?
286 [IF] 2drop \ clear gforth's version numbers )
287 aligned 2 cells context-var source-location \ for error msgs )
289 s" bigFORTH" environment?
290 [IF] 2drop \ clear bigFORTH' version numbers )
291 aligned cell context-var source-location
294 \ !! replace the stuff until constant with something working on your system
295 aligned 3 cells context-var source-location
297 80 chars context-var error-info
300 aligned constant syntax-expr \ length of a syntax-expr )
302 : make-syntax-expr \ map -- syntax-expr )
303 \ allocate a syntax-expr and initialize it )
304 here swap , false c, false c,
305 align 0 , false c, align empty ,
306 \ source location. !! replace the stuff until `;' with your stuff
307 \ if you use blocks, use:
309 \ the following is just a dummy
310 [ s" gforth" environment? ]
314 [ s" bigFORTH" environment? ]
318 source 80 min >r here 3 cells + r@ cmove
319 here 3 cells + , r@ , >in @ 80 min , r> chars allot align
324 \ warnings and errors )
326 \ !! implementation dependent )
327 \ prints the info stored in source-location in a usable way )
328 \ prints where the error happened )
329 [ s" gforth" environment? ]
331 source-location 2@ ." line" . drop ." :" ;
333 [ s" bigFORTH" environment? ]
335 source-location dup w@ $3FF and scr ! 2+ w@ r# ! ;
337 source-location 2@ swap cr type cr
338 error-info @ 2 - spaces ." ^" cr ." ::: " ;
344 cr .in ." you found a bug" gray-error ;
346 variable print-token ' . print-token !
347 \ contains execution address of a word < token -- > to print a token )
349 : check-conflict \ set1 set2 -- )
350 \ print the intersection of set1 and set2 if it isn't empty )
351 2dup disjoint? ?not? warnings @ and if
353 intersection print-token @ apply-to-members
360 : method \ use: < offset > method name < offset2 > )
361 \ executes the word whose execution address is stored in the field
362 \ at offset of a table pointed to by the "methods" field of "this" )
363 cell <builds-field \ n1 -- n2 )
365 @ methods + @ execute ;
367 \ method table for syntax-exprs
369 method compute-method
370 method propagate-method
371 method generate-method
373 constant syntax-expr-methods
377 : compute \ syntax-expr -- first-set maybe-empty )
378 \ compute the first-set and maybe-empty of a syntax-expr )
379 \ a bit of memoization is used here )
386 first-set @ maybe-empty c@
389 : get-first \ syntax-expr -- first-set )
392 : check-cycle \ syntax-expr -- )
393 \ just check for left recursion )
396 : propagate \ follow-set syntax-expr -- )
397 \ add follow-set to the follow set of syntax-expr and its children )
399 dup follow-set @ subset? ?not? \ would everything stay the same
400 mark-propagate c@ ?not? or if \ and was propagate here already
401 true mark-propagate c! \ NO, do propagate
402 follow-set @ union dup follow-set !
409 : generate \ syntax-expr -- )
410 \ this one gets things done )
411 new-context generate-method old-context ;
413 : pass2 \ syntax-expr -- )
414 \ computes all necessary first sets, checks for left recursions
415 \ and conflicts and generates code for rules )
417 mark-pass2 c@ ?not? if
426 : parser \ syntax-expr -- )
427 \ use: syntax-expr parser xxx )
431 \ : should not be immediate
432 >r : r> generate postpone ; ;
435 \ eps - empty syntax-expr )
444 \ the eps syntax-expr proper
445 eps-map make-syntax-expr
449 : eps \ -- syntax-expr )
450 \ just adjusts eps1 and returns it
452 empty first-set ! ( empty changes due to max-member )
460 \ a terminal is a syntax-expr with an extra field )
462 context-const check&next
463 \ contains address of a word < f -- > that checks
464 \ if f is true and reads the next terminal symbol )
465 constant terminal-syntax-expr
467 : generate-terminal \ -- )
468 this get-first compile-test
469 check&next compile, ;
477 : make-terminal \ first-set cfa -- syntax-expr )
478 terminal-map make-syntax-expr
484 : terminal \ first-set cfa -- )
485 create make-terminal drop ;
488 \ binary syntax-exprs )
490 context-const operand1
491 context-const operand2
492 constant binary-syntax-expr
494 : make-binary \ syntax-expr1 syntax-expr2 map -- syntax-expr )
495 make-syntax-expr rot , swap , ;
503 : compute-concatenation \ -- first maybe-empty )
504 operand1 compute dup if
510 : propagate-concatenation \ follow-set -- )
513 endif \ follow follow1 )
517 : generate-concatenation \ -- )
521 create concatenation-map
522 ', compute-concatenation
523 ', propagate-concatenation
524 ', generate-concatenation
527 : concat \ syntax-expr1 syntax-expr2 -- syntax-expr )
528 concatenation-map make-binary ;
529 \ this is the actual concatenation operator )
530 \ but for safety and readability the parenthesised notation )
535 : compute-alternative \ -- first maybe-empty )
538 rot 2dup and warnings @ and if
539 cr .in ." warning: two branches may be empty" endif
542 : propagate-alternative \ follow -- )
543 dup operand1 propagate
546 : generate-alternative1 \ -- )
547 operand1 get-first compile-test
554 : generate-alternative2 \ -- )
555 operand1 get-first compile-test postpone ?not?
556 operand2 get-first compile-test postpone and
563 : generate-alternative \ -- )
565 generate-alternative2
567 generate-alternative1
571 : pass2-alternative \ -- )
573 follow-set @ check-conflict
577 operand1 get-first operand2 get-first check-conflict
580 create alternative-map
581 ', compute-alternative
582 ', propagate-alternative
583 ', generate-alternative
586 : alt \ syntax-expr1 syntax-expr2 -- syntax-expr )
587 alternative-map make-binary ;
588 \ this is the actual alternative operator )
589 \ but for safety and readability the parenthesised notation )
593 \ unary syntax-exprs )
595 context-const operand
596 constant unary-syntax-expr
598 : make-unary \ syntax-expr1 map -- syntax-expr2 )
599 make-syntax-expr swap , ;
602 \ options and repetitions )
603 : pass2-option&repetition \ -- )
604 follow-set @ operand get-first check-conflict
609 : compute-option \ -- set f )
610 operand compute warnings @ and if
611 cr .in ." warning: unnessesary option" endif
614 : propagate-option \ follow -- )
617 : generate-option \ -- )
618 operand get-first compile-test
627 ', pass2-option&repetition
629 : ?? \ syntax-expr1 -- syntax-expr2 )
630 option-map make-unary ;
634 : propagate-repetition \ follow-set -- )
635 operand get-first union operand propagate ;
639 : compute-*repetition \ -- set f )
640 operand compute warnings @ and if
641 cr .in ." warning: *repetition of optional term" endif
644 : generate-*repetition \ -- )
646 operand get-first compile-test
651 create *repetition-map
652 ', compute-*repetition
653 ', propagate-repetition
654 ', generate-*repetition
655 ', pass2-option&repetition
657 : ** \ syntax-expr1 -- syntax-expr2 )
658 *repetition-map make-unary ;
662 : compute-+repetition \ -- set f )
665 : generate-+repetition \ -- )
668 operand get-first compile-test
669 postpone ?not? postpone until ;
671 create +repetition-map
672 ', compute-+repetition
673 ', propagate-repetition
674 ', generate-+repetition
675 ', pass2-option&repetition
677 : ++ \ syntax-expr1 -- syntax-expr2 )
678 +repetition-map make-unary ;
684 constant action-syntax-expr
686 : generate-action \ syntax-expr -- )
695 : {{ \ -- syntax-expr addr colon-sys )
696 action-map make-syntax-expr
705 : }} \ syntax-expr addr colon-sys -- syntax-expr )
714 1 context-var mark-compute
715 aligned cell context-var rule-body \ in forth left side of rule )
716 cell context-var exec \ cfa of code for rule )
717 constant nt-syntax-expr
719 : get-body \ -- syntax-expr )
720 \ get the body of the rule for the nt in "this" )
724 cr .in ." no rule for nonterminal" gray-error
727 : compute-nt \ -- set f )
729 cr .in ." left recursion" gray-error
735 : propagate-nt \ follow-set -- )
739 \ generates the code for a rule )
746 \ generates a call to the code for the rule )
747 \ since the code needs not be generated yet, an indirect call is used )
748 exec postpone literal
753 \ apart from the usual duties, this pass2 also has to code-nt )
763 : make-nt \ syntax-expr -- nt )
764 nt-map make-syntax-expr
765 false c, align swap , 0 , ;
767 : <- \ use: syntax-expr <- xxx )
768 \ xxx: -- syntax-expr )
769 create make-nt drop ;
771 : nonterminal \ use: nonterminal xxx )
772 0 <- ; \ forward declaration )
774 : rule \ syntax-expr nt -- )
778 .in ." multiple rules for nonterminal" gray-error endif
784 : reduce \ 0 x1 ... [x2 x3 -- x4] -- x )
785 \ e.g. 0 5 6 7 ' + reduce = 5 6 7 + + = 18 )
795 7 constant concatenation-id
798 : -) \ n 0 syntax-expr1 syntax-expr2 .. -- syntax-expr )
800 swap concatenation-id ?pairs ;
802 8 constant alternative-id
805 : |) \ n 0 syntax-expr1 syntax-expr2 .. -- syntax-expr )
807 swap alternative-id ?pairs ;