;; ---------------------------------------------------------------------- ;; ;; FICHIER : lalr.scm ;; ;; DATE DE CREATION : Mon Jan 22 15:42:32 1996 ;; ;; DERNIERE MODIFICATION : Mon Jun 3 10:24:43 1996 ;; ;; ---------------------------------------------------------------------- ;; ;; Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. ;; ;; (for the Bison source code translated in Scheme) ;; ;; Copyright (C) 1996 Dominique Boucher ;; ;; (for the translation in Scheme) ;; ;; ---------------------------------------------------------------------- ;; ;; An efficient Scheme LALR(1) Parser Generator -- lalr.scm ;; ;; ---------------------------------------------------------------------- ;; ;; This file contains yet another LALR(1) parser generator written in ;; ;; Scheme. In contrast to other such parser generators, this one ;; ;; implements a more efficient algorithm for computing the lookahead sets.;; ;; The algorithm is the same as used in Bison (GNU yacc) and is described ;; ;; in the following paper: ;; ;; ;; ;; "Efficient Computation of LALR(1) Look-Ahead Set", F. DeRemer and ;; ;; T. Pennello, TOPLAS, vol. 4, no. 4, october 1982. ;; ;; ;; ;; As a consequence, it is not written in a fully functional style. ;; ;; The program has been successfully tested on several Scheme ;; ;; interpreters and compilers, including scm4d3, Gambit v2.2, and ;; ;; MIT-Scheme 7.2.0 (microcode 11.127, runtime 14.160). ;; ;; ---------------------------------------------------------------------- ;; ;; HOW TO USE THE PROGRAM ;; ;; ;; ;; To generate a parser for a given grammar, the latter must be first ;; ;; written down in scheme. The next section will describe the syntax ;; ;; of the grammar. Now suppose your grammar is defined like this: ;; ;; ;; ;; (define my-grammar { grammar }) ;; ;; ;; ;; All you need to do is evaluate the expression: ;; ;; ;; ;; (gen-lalr1 my-grammar "file" [prefix]) ;; ;; ;; ;; where "file" is the name of the file (a string) that will contain the ;; ;; tables for LR-parsing. The last argument must be supplied if you want ;; ;; multiple parsers coexist in the same application. It must be a symbol, ;; ;; otherwise it will be ignored. ;; ;; ;; ;; To run the parser, you must first load the LR parsing driver(also part ;; ;; of this distribution): ;; ;; ;; ;; (load "lr-dvr.scm") ;; ;; ;; ;; The interface to the generated parser will be the function ;; ;; ;; ;; ([prefix-]parse lexer errorp) ;; ;; ;; ;; where lexer is the name of the scanner feeding the parser with pairs ;; ;; (token . lval) and errorp is the name of a user-defined error ;; ;; function (the standard error function can be used as well). ;; ;; ;; ;; ;; ;; Here are some notes about the lexer and the error function: ;; ;; ;; ;; - the tokens (which are the first components of the pairs returned ;; ;; by the lexer) must agree with the tokens defined in the grammar. ;; ;; ;; ;; - when the lexer wants to signal the end of the input, it must ;; ;; return the pair '(0) each time it's invoked. ;; ;; ;; ;; - the error function must accept two parameters (the standard error ;; ;; function accepts a variable number of parameters, so it accepts ;; ;; two). ;; ;; ;; ;; ---------------------------------------------------------------------- ;; ;; THE GRAMMAR FORMAT ;; ;; ;; ;; The grammar is specified by first giving the list of terminals and the ;; ;; list of non-terminal definitions. Each non-terminal definition ;; ;; is a list where the first element is the non-terminal and the other ;; ;; elements are the right-hand sides (lists of grammar symbols). In ;; ;; addition to this, each rhs can be followed by a semantic action. ;; ;; By convention, use strings for tokens and atoms for non-terminals. ;; ;; ;; ;; For example, consider the following (yacc) grammar: ;; ;; ;; ;; e : e '+' t ;; ;; | t ;; ;; ; ;; ;; ;; ;; t : t '*' f ;; ;; | f ;; ;; ; ;; ;; ;; ;; f : ID ;; ;; ; ;; ;; ;; ;; The same grammar, written for the scheme parser generator, would look ;; ;; like this (with semantic actions) ;; ;; ;; ;; (define my-grammar ;; ;; '( ;; ;; ; Terminal symbols ;; ;; ID ADD MULT ;; ;; ; Productions ;; ;; (e (e ADD t) : (+ $1 $3) ;; ;; (t) : $1 ;; ;; ) ;; ;; (t (t MULT f) : (* $1 $3) ;; ;; (f) : $1 ;; ;; ) ;; ;; (f (ID) : $1) ;; ;; )) ;; ;; ;; ;; In semantic actions, the symbol $ refers to the synthesized ;; ;; attribute value of the nth symbol in the production. The value ;; ;; associated with the non-terminal on the left is the result of ;; ;; evaluating the semantic action (it defaults to #f). ;; ;; ;; ;; If you evaluate ;; ;; ;; ;; (gen-lalr1 my-grammar "foo.scm" 'my) ;; ;; ;; ;; then the generated parser will be named 'my-parser'. ;; ;; ;; ;; NOTE ON CONFLICT RESOLUTION ;; ;; ;; ;; Conflicts in the grammar are handled in a conventional way. ;; ;; Shift/Reduce conflicts are resolved by shifting, and Reduce/Reduce ;; ;; conflicts are resolved by choosing the rule listed first in the ;; ;; grammar definition. ;; ;; ;; ;; You can print the states of the generated parser by evaluating ;; ;; `(print-states)'. The format of the output is similar to the one ;; ;; produced by bison when given the -v command-line option. ;; ;; ---------------------------------------------------------------------- ;; ;; lalr.scm is free software; you can redistribute it and/or modify ;; ;; it under the terms of the GNU General Public License as published by ;; ;; the Free Software Foundation; either version 2, or (at your option) ;; ;; any later version. ;; ;; ;; ;; lalr.scm is distributed in the hope that it will be useful, ;; ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; ;; GNU General Public License for more details. ;; ;; ;; ;; You should have received a copy of the GNU General Public License ;; ;; along with lalr.scm; see the file COPYING. If not, write to ;; ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ;; ;; ;; ;; Dominique Boucher -- Universite de Montreal ;; ;; ;; ;; Send questions, comments or suggestions to boucherd@iro.umontreal.ca ;; ;; ---------------------------------------------------------------------- ;; ;; 1998/08/16: Tanaka Akira transplants generating code from Scheme to Emacs-Lisp. ;;; ---------- SYSTEM DEPENDENT SECTION ----------------- ;; -------- SCM (begin (defmacro def-macro (args body) `(defmacro ,(car args) ,(cdr args) ,body)) (def-macro (BITS-PER-WORD) 24) (def-macro (logical-or x . y) `(logior ,x ,@y)) ) ;; -------- MIT-Scheme '(begin (declare (usual-integrations)) (define-macro (def-macro form . body) `(DEFINE-MACRO ,form (LET () ,@body))) (def-macro (BITS-PER-WORD) 24) (def-macro (logical-or x . y) `(fix:or ,x ,@y)) ) ;; -------- Gambit '(begin (declare (standard-bindings) (fixnum) (block) (not safe)) (define-macro (def-macro form . body) `(DEFINE-MACRO ,form (LET () ,@body))) (def-macro (BITS-PER-WORD) 28) (def-macro (logical-or x . y) `(,(string->symbol "##logior") ,x ,@y)) ) ;; -------- Bigloo '(begin (define-macro (def-macro form . body) `(DEFINE-MACRO ,form (LET () ,@body))) (def-macro (BITS-PER-WORD) 16) (def-macro (logical-or x . y) `(bit-or ,x ,@y)) ) ;;; ---------- END OF SYSTEM DEPENDENT SECTION ------------ ;; - Macros pour la gestion des vecteurs de bits (def-macro (set-bit v b) `(let ((x (quotient ,b (BITS-PER-WORD))) (y (expt 2 (remainder ,b (BITS-PER-WORD))))) (vector-set! ,v x (logical-or (vector-ref ,v x) y)))) (def-macro (bit-union v1 v2 n) `(do ((i 0 (+ i 1))) ((= i ,n)) (vector-set! ,v1 i (logical-or (vector-ref ,v1 i) (vector-ref ,v2 i))))) ;; - Macro pour les structures de donnees (def-macro (new-core) `(make-vector 4 0)) (def-macro (set-core-number! c n) `(vector-set! ,c 0 ,n)) (def-macro (set-core-acc-sym! c s) `(vector-set! ,c 1 ,s)) (def-macro (set-core-nitems! c n) `(vector-set! ,c 2 ,n)) (def-macro (set-core-items! c i) `(vector-set! ,c 3 ,i)) (def-macro (core-number c) `(vector-ref ,c 0)) (def-macro (core-acc-sym c) `(vector-ref ,c 1)) (def-macro (core-nitems c) `(vector-ref ,c 2)) (def-macro (core-items c) `(vector-ref ,c 3)) (def-macro (new-shift) `(make-vector 3 0)) (def-macro (set-shift-number! c x) `(vector-set! ,c 0 ,x)) (def-macro (set-shift-nshifts! c x) `(vector-set! ,c 1 ,x)) (def-macro (set-shift-shifts! c x) `(vector-set! ,c 2 ,x)) (def-macro (shift-number s) `(vector-ref ,s 0)) (def-macro (shift-nshifts s) `(vector-ref ,s 1)) (def-macro (shift-shifts s) `(vector-ref ,s 2)) (def-macro (new-red) `(make-vector 3 0)) (def-macro (set-red-number! c x) `(vector-set! ,c 0 ,x)) (def-macro (set-red-nreds! c x) `(vector-set! ,c 1 ,x)) (def-macro (set-red-rules! c x) `(vector-set! ,c 2 ,x)) (def-macro (red-number c) `(vector-ref ,c 0)) (def-macro (red-nreds c) `(vector-ref ,c 1)) (def-macro (red-rules c) `(vector-ref ,c 2)) (def-macro (new-set nelem) `(make-vector ,nelem 0)) (def-macro (vector-map f v) `(let ((vm-n (- (vector-length ,v) 1))) (let loop ((vm-low 0) (vm-high vm-n)) (if (= vm-low vm-high) (vector-set! ,v vm-low (,f (vector-ref ,v vm-low) vm-low)) (let ((vm-middle (quotient (+ vm-low vm-high) 2))) (loop vm-low vm-middle) (loop (+ vm-middle 1) vm-high)))))) ;; - Constantes (define STATE-TABLE-SIZE 1009) ;; - Tableaux (define rrhs #f) (define rlhs #f) (define ritem #f) (define nullable #f) (define derives #f) (define fderives #f) (define firsts #f) (define kernel-base #f) (define kernel-end #f) (define shift-symbol #f) (define shift-set #f) (define red-set #f) (define state-table #f) (define acces-symbol #f) (define reduction-table #f) (define shift-table #f) (define consistent #f) (define lookaheads #f) (define LA #f) (define LAruleno #f) (define lookback #f) (define goto-map #f) (define from-state #f) (define to-state #f) (define includes #f) (define F #f) (define action-table #f) ;; - Variables (define nitems #f) (define nrules #f) (define nvars #f) (define nterms #f) (define nsyms #f) (define nstates #f) (define first-state #f) (define last-state #f) (define final-state #f) (define first-shift #f) (define last-shift #f) (define first-reduction #f) (define last-reduction #f) (define nshifts #f) (define maxrhs #f) (define ngotos #f) (define token-set-size #f) (define (gen-larl1 gram output-file header footer . opt) (define (conv-rule-right rr) (if (null? rr) '() (cons (reverse (car rr)) (cons (cadr rr) (cons (caddr rr) (conv-rule-right (cdddr rr))))))) (apply gen-lalr1 (map (lambda (elt) (if (symbol? elt) elt (cons (car elt) (conv-rule-right (cdr elt))))) gram) output-file header footer opt)) (define (gen-lalr1 gram output-file header footer . opt) (initialize-all) (rewrite-grammar gram (lambda (terms vars gram gram/actions) (set! the-terminals (list->vector terms)) (set! the-nonterminals (list->vector vars)) (set! nterms (length terms)) (set! nvars (length vars)) (set! nsyms (+ nterms nvars)) (let ((no-of-rules (length gram/actions)) (no-of-items (let loop ((l gram/actions) (count 0)) (if (null? l) count (loop (cdr l) (+ count (length (caar l)))))))) (pack-grammar no-of-rules no-of-items gram) (set-derives) (set-nullable) (generate-states) (lalr) (build-tables) (compact-action-table) (let* ((parser-name (if (and (pair? opt) (symbol? (car opt))) (car opt) #f)) (prefix (if parser-name (string-append (symbol->string parser-name) ":") "")) (parser-prefix (if parser-name (string-append (symbol->string parser-name) "-") ""))) (with-output-to-file output-file (lambda () (display "; *** Header ***") (newline) (output-header header parser-prefix) (display "; *** Token Definitions ***") (newline) (output-token-defs terms prefix) (display "; *** Action Table ***") (newline) (output-action-table prefix) (display "; *** Goto Table ***") (newline) (output-goto-table prefix) (display "; *** Reduction Table ***") (newline) (output-reduction-table gram/actions prefix) (display "; *** Parser Definition ***") (newline) (output-parser-def parser-prefix prefix) (display "; *** Footer ***") (newline) (output-footer footer) ))))))) (define (initialize-all) (set! rrhs #f) (set! rlhs #f) (set! ritem #f) (set! nullable #f) (set! derives #f) (set! fderives #f) (set! firsts #f) (set! kernel-base #f) (set! kernel-end #f) (set! shift-symbol #f) (set! shift-set #f) (set! red-set #f) (set! state-table (make-vector STATE-TABLE-SIZE '())) (set! acces-symbol #f) (set! reduction-table #f) (set! shift-table #f) (set! consistent #f) (set! lookaheads #f) (set! LA #f) (set! LAruleno #f) (set! lookback #f) (set! goto-map #f) (set! from-state #f) (set! to-state #f) (set! includes #f) (set! F #f) (set! action-table #f) (set! nstates #f) (set! first-state #f) (set! last-state #f) (set! final-state #f) (set! first-shift #f) (set! last-shift #f) (set! first-reduction #f) (set! last-reduction #f) (set! nshifts #f) (set! maxrhs #f) (set! ngotos #f) (set! token-set-size #f)) (define (pack-grammar no-of-rules no-of-items gram) (set! nrules (+ no-of-rules 1)) (set! nitems no-of-items) (set! rlhs (make-vector nrules #f)) (set! rrhs (make-vector nrules #f)) (set! ritem (make-vector (+ 1 nitems) #f)) (let loop ((p gram) (item-no 0) (rule-no 1)) (if (not (null? p)) (let ((nt (caar p))) (let loop2 ((prods (cdar p)) (it-no2 item-no) (rl-no2 rule-no)) (if (null? prods) (loop (cdr p) it-no2 rl-no2) (begin (vector-set! rlhs rl-no2 nt) (vector-set! rrhs rl-no2 it-no2) (let loop3 ((rhs (car prods)) (it-no3 it-no2)) (if (null? rhs) (begin (vector-set! ritem it-no3 (- rl-no2)) (loop2 (cdr prods) (+ it-no3 1) (+ rl-no2 1))) (begin (vector-set! ritem it-no3 (car rhs)) (loop3 (cdr rhs) (+ it-no3 1)))))))))))) ;; Fonction set-derives ;; -------------------- (define (set-derives) (define delts (make-vector (+ nrules 1) 0)) (define dset (make-vector nvars -1)) (let loop ((i 1) (j 0)) ; i = 0 (if (< i nrules) (let ((lhs (vector-ref rlhs i))) (if (>= lhs 0) (begin (vector-set! delts j (cons i (vector-ref dset lhs))) (vector-set! dset lhs j) (loop (+ i 1) (+ j 1))) (loop (+ i 1) j))))) (set! derives (make-vector nvars 0)) (let loop ((i 0)) (if (< i nvars) (let ((q (let loop2 ((j (vector-ref dset i)) (s '())) (if (< j 0) s (let ((x (vector-ref delts j))) (loop2 (cdr x) (cons (car x) s))))))) (vector-set! derives i q) (loop (+ i 1)))))) (define (set-nullable) (set! nullable (make-vector nvars #f)) (let ((squeue (make-vector nvars #f)) (rcount (make-vector (+ nrules 1) 0)) (rsets (make-vector nvars #f)) (relts (make-vector (+ nitems nvars 1) #f))) (let loop ((r 0) (s2 0) (p 0)) (let ((*r (vector-ref ritem r))) (if *r (if (< *r 0) (let ((symbol (vector-ref rlhs (- *r)))) (if (and (>= symbol 0) (not (vector-ref nullable symbol))) (begin (vector-set! nullable symbol #t) (vector-set! squeue s2 symbol) (loop (+ r 1) (+ s2 1) p)))) (let loop2 ((r1 r) (any-tokens #f)) (let* ((symbol (vector-ref ritem r1))) (if (> symbol 0) (loop2 (+ r1 1) (or any-tokens (>= symbol nvars))) (if (not any-tokens) (let ((ruleno (- symbol))) (let loop3 ((r2 r) (p2 p)) (let ((symbol (vector-ref ritem r2))) (if (> symbol 0) (begin (vector-set! rcount ruleno (+ (vector-ref rcount ruleno) 1)) (vector-set! relts p2 (cons (vector-ref rsets symbol) ruleno)) (vector-set! rsets symbol p2) (loop3 (+ r2 1) (+ p2 1))) (loop (+ r2 1) s2 p2))))) (loop (+ r1 1) s2 p)))))) (let loop ((s1 0) (s3 s2)) (if (< s1 s3) (let loop2 ((p (vector-ref rsets (vector-ref squeue s1))) (s4 s3)) (if p (let* ((x (vector-ref relts p)) (ruleno (cdr x)) (y (- (vector-ref rcount ruleno) 1))) (vector-set! rcount ruleno y) (if (= y 0) (let ((symbol (vector-ref rlhs ruleno))) (if (and (>= symbol 0) (not (vector-ref nullable symbol))) (begin (vector-set! nullable symbol #t) (vector-set! squeue s4 symbol) (loop2 (car x) (+ s4 1))) (loop2 (car x) s4))) (loop2 (car x) s4)))) (loop (+ s1 1) s4))))))))) ; Fonction set-firsts qui calcule un tableau de taille ; nvars et qui donne, pour chaque non-terminal X, une liste des ; non-terminaux pouvant apparaitre au debut d'une derivation a ; partir de X. (define (set-firsts) (set! firsts (make-vector nvars '())) ;; -- initialization (let loop ((i 0)) (if (< i nvars) (let loop2 ((sp (vector-ref derives i))) (if (null? sp) (loop (+ i 1)) (let ((sym (vector-ref ritem (vector-ref rrhs (car sp))))) (if (< -1 sym nvars) (vector-set! firsts i (sinsert sym (vector-ref firsts i)))) (loop2 (cdr sp))))))) ;; -- reflexive and transitive closure (let loop ((continue #t)) (if continue (let loop2 ((i 0) (cont #f)) (if (>= i nvars) (loop cont) (let* ((x (vector-ref firsts i)) (y (let loop3 ((l x) (z x)) (if (null? l) z (loop3 (cdr l) (sunion (vector-ref firsts (car l)) z)))))) (if (equal? x y) (loop2 (+ i 1) cont) (begin (vector-set! firsts i y) (loop2 (+ i 1) #t)))))))) (let loop ((i 0)) (if (< i nvars) (begin (vector-set! firsts i (sinsert i (vector-ref firsts i))) (loop (+ i 1)))))) ; Fonction set-fderives qui calcule un tableau de taille ; nvars et qui donne, pour chaque non-terminal, une liste des regles pouvant ; etre derivees a partir de ce non-terminal. (se sert de firsts) (define (set-fderives) (set! fderives (make-vector nvars #f)) (set-firsts) (let loop ((i 0)) (if (< i nvars) (let ((x (let loop2 ((l (vector-ref firsts i)) (fd '())) (if (null? l) fd (loop2 (cdr l) (sunion (vector-ref derives (car l)) fd)))))) (vector-set! fderives i x) (loop (+ i 1)))))) ; Fonction calculant la fermeture d'un ensemble d'items LR0 ; ou core est une liste d'items (define (closure core) ;; Initialization (define ruleset (make-vector nrules #f)) (let loop ((csp core)) (if (not (null? csp)) (let ((sym (vector-ref ritem (car csp)))) (if (< -1 sym nvars) (let loop2 ((dsp (vector-ref fderives sym))) (if (not (null? dsp)) (begin (vector-set! ruleset (car dsp) #t) (loop2 (cdr dsp)))))) (loop (cdr csp))))) (let loop ((ruleno 1) (csp core) (itemsetv '())) ; ruleno = 0 (if (< ruleno nrules) (if (vector-ref ruleset ruleno) (let ((itemno (vector-ref rrhs ruleno))) (let loop2 ((c csp) (itemsetv2 itemsetv)) (if (and (pair? c) (< (car c) itemno)) (loop2 (cdr c) (cons (car c) itemsetv2)) (loop (+ ruleno 1) c (cons itemno itemsetv2))))) (loop (+ ruleno 1) csp itemsetv)) (let loop2 ((c csp) (itemsetv2 itemsetv)) (if (pair? c) (loop2 (cdr c) (cons (car c) itemsetv2)) (reverse itemsetv2)))))) (define (allocate-item-sets) (set! kernel-base (make-vector nsyms 0)) (set! kernel-end (make-vector nsyms #f))) (define (allocate-storage) (allocate-item-sets) (set! red-set (make-vector (+ nrules 1) 0))) ;; -- (define (initialize-states) (let ((p (new-core))) (set-core-number! p 0) (set-core-acc-sym! p #f) (set-core-nitems! p 1) (set-core-items! p '(0)) (set! first-state (list p)) (set! last-state first-state) (set! nstates 1))) (define (generate-states) (allocate-storage) (set-fderives) (initialize-states) (let loop ((this-state first-state)) (if (pair? this-state) (let* ((x (car this-state)) (is (closure (core-items x)))) (save-reductions x is) (new-itemsets is) (append-states) (if (> nshifts 0) (save-shifts x)) (loop (cdr this-state)))))) ;; Fonction calculant les symboles sur lesquels il faut "shifter" ;; et regroupe les items en fonction de ces symboles (define (new-itemsets itemset) ;; - Initialization (set! shift-symbol '()) (let loop ((i 0)) (if (< i nsyms) (begin (vector-set! kernel-end i '()) (loop (+ i 1))))) (let loop ((isp itemset)) (if (pair? isp) (let* ((i (car isp)) (sym (vector-ref ritem i))) (if (>= sym 0) (begin (set! shift-symbol (sinsert sym shift-symbol)) (let ((x (vector-ref kernel-end sym))) (if (null? x) (begin (vector-set! kernel-base sym (cons (+ i 1) x)) (vector-set! kernel-end sym (vector-ref kernel-base sym))) (begin (set-cdr! x (list (+ i 1))) (vector-set! kernel-end sym (cdr x))))))) (loop (cdr isp))))) (set! nshifts (length shift-symbol))) (define (get-state sym) (let* ((isp (vector-ref kernel-base sym)) (n (length isp)) (key (let loop ((isp1 isp) (k 0)) (if (null? isp1) (modulo k STATE-TABLE-SIZE) (loop (cdr isp1) (+ k (car isp1)))))) (sp (vector-ref state-table key))) (if (null? sp) (let ((x (new-state sym))) (vector-set! state-table key (list x)) (core-number x)) (let loop ((sp1 sp)) (if (and (= n (core-nitems (car sp1))) (let loop2 ((i1 isp) (t (core-items (car sp1)))) (if (and (pair? i1) (= (car i1) (car t))) (loop2 (cdr i1) (cdr t)) (null? i1)))) (core-number (car sp1)) (if (null? (cdr sp1)) (let ((x (new-state sym))) (set-cdr! sp1 (list x)) (core-number x)) (loop (cdr sp1)))))))) (define (new-state sym) (let* ((isp (vector-ref kernel-base sym)) (n (length isp)) (p (new-core))) (set-core-number! p nstates) (set-core-acc-sym! p sym) (if (= sym nvars) (set! final-state nstates)) (set-core-nitems! p n) (set-core-items! p isp) (set-cdr! last-state (list p)) (set! last-state (cdr last-state)) (set! nstates (+ nstates 1)) p)) ;; -- (define (append-states) (set! shift-set (let loop ((l (reverse shift-symbol))) (if (null? l) '() (cons (get-state (car l)) (loop (cdr l))))))) ;; -- (define (save-shifts core) (let ((p (new-shift))) (set-shift-number! p (core-number core)) (set-shift-nshifts! p nshifts) (set-shift-shifts! p shift-set) (if last-shift (begin (set-cdr! last-shift (list p)) (set! last-shift (cdr last-shift))) (begin (set! first-shift (list p)) (set! last-shift first-shift))))) (define (save-reductions core itemset) (let ((rs (let loop ((l itemset)) (if (null? l) '() (let ((item (vector-ref ritem (car l)))) (if (< item 0) (cons (- item) (loop (cdr l))) (loop (cdr l)))))))) (if (pair? rs) (let ((p (new-red))) (set-red-number! p (core-number core)) (set-red-nreds! p (length rs)) (set-red-rules! p rs) (if last-reduction (begin (set-cdr! last-reduction (list p)) (set! last-reduction (cdr last-reduction))) (begin (set! first-reduction (list p)) (set! last-reduction first-reduction))))))) ;; -- (define (lalr) (set! token-set-size (+ 1 (quotient nterms (BITS-PER-WORD)))) (set-accessing-symbol) (set-shift-table) (set-reduction-table) (set-max-rhs) (initialize-LA) (set-goto-map) (initialize-F) (build-relations) (digraph includes) (compute-lookaheads)) (define (set-accessing-symbol) (set! acces-symbol (make-vector nstates #f)) (let loop ((l first-state)) (if (pair? l) (let ((x (car l))) (vector-set! acces-symbol (core-number x) (core-acc-sym x)) (loop (cdr l)))))) (define (set-shift-table) (set! shift-table (make-vector nstates #f)) (let loop ((l first-shift)) (if (pair? l) (let ((x (car l))) (vector-set! shift-table (shift-number x) x) (loop (cdr l)))))) (define (set-reduction-table) (set! reduction-table (make-vector nstates #f)) (let loop ((l first-reduction)) (if (pair? l) (let ((x (car l))) (vector-set! reduction-table (red-number x) x) (loop (cdr l)))))) (define (set-max-rhs) (let loop ((p 0) (curmax 0) (length 0)) (let ((x (vector-ref ritem p))) (if x (if (>= x 0) (loop (+ p 1) curmax (+ length 1)) (loop (+ p 1) (max curmax length) 0)) (set! maxrhs curmax))))) (define (initialize-LA) (define (last l) (if (null? (cdr l)) (car l) (last (cdr l)))) (set! consistent (make-vector nstates #f)) (set! lookaheads (make-vector (+ nstates 1) #f)) (let loop ((count 0) (i 0)) (if (< i nstates) (begin (vector-set! lookaheads i count) (let ((rp (vector-ref reduction-table i)) (sp (vector-ref shift-table i))) (if (and rp (or (> (red-nreds rp) 1) (and sp (not (< (vector-ref acces-symbol (last (shift-shifts sp))) nvars))))) (loop (+ count (red-nreds rp)) (+ i 1)) (begin (vector-set! consistent i #t) (loop count (+ i 1)))))) (begin (vector-set! lookaheads nstates count) (let ((c (max count 1))) (set! LA (make-vector c #f)) (do ((j 0 (+ j 1))) ((= j c)) (vector-set! LA j (new-set token-set-size))) (set! LAruleno (make-vector c -1)) (set! lookback (make-vector c #f))) (let loop ((i 0) (np 0)) (if (< i nstates) (if (vector-ref consistent i) (loop (+ i 1) np) (let ((rp (vector-ref reduction-table i))) (if rp (let loop2 ((j (red-rules rp)) (np2 np)) (if (null? j) (loop (+ i 1) np2) (begin (vector-set! LAruleno np2 (car j)) (loop2 (cdr j) (+ np2 1))))) (loop (+ i 1) np)))))))))) (define (set-goto-map) (set! goto-map (make-vector (+ nvars 1) 0)) (let ((temp-map (make-vector (+ nvars 1) 0))) (let loop ((ng 0) (sp first-shift)) (if (pair? sp) (let loop2 ((i (reverse (shift-shifts (car sp)))) (ng2 ng)) (if (pair? i) (let ((symbol (vector-ref acces-symbol (car i)))) (if (< symbol nvars) (begin (vector-set! goto-map symbol (+ 1 (vector-ref goto-map symbol))) (loop2 (cdr i) (+ ng2 1))) (loop2 (cdr i) ng2))) (loop ng2 (cdr sp)))) (let loop ((k 0) (i 0)) (if (< i nvars) (begin (vector-set! temp-map i k) (loop (+ k (vector-ref goto-map i)) (+ i 1))) (begin (do ((i 0 (+ i 1))) ((>= i nvars)) (vector-set! goto-map i (vector-ref temp-map i))) (set! ngotos ng) (vector-set! goto-map nvars ngotos) (vector-set! temp-map nvars ngotos) (set! from-state (make-vector ngotos #f)) (set! to-state (make-vector ngotos #f)) (do ((sp first-shift (cdr sp))) ((null? sp)) (let* ((x (car sp)) (state1 (shift-number x))) (do ((i (shift-shifts x) (cdr i))) ((null? i)) (let* ((state2 (car i)) (symbol (vector-ref acces-symbol state2))) (if (< symbol nvars) (let ((k (vector-ref temp-map symbol))) (vector-set! temp-map symbol (+ k 1)) (vector-set! from-state k state1) (vector-set! to-state k state2)))))))))))))) (define (map-goto state symbol) (let loop ((low (vector-ref goto-map symbol)) (high (- (vector-ref goto-map (+ symbol 1)) 1))) (if (> low high) (begin (display (list "Error in map-goto" state symbol)) (newline) 0) (let* ((middle (quotient (+ low high) 2)) (s (vector-ref from-state middle))) (cond ((= s state) middle) ((< s state) (loop (+ middle 1) high)) (else (loop low (- middle 1)))))))) (define (initialize-F) (set! F (make-vector ngotos #f)) (do ((i 0 (+ i 1))) ((= i ngotos)) (vector-set! F i (new-set token-set-size))) (let ((reads (make-vector ngotos #f))) (let loop ((i 0) (rowp 0)) (if (< i ngotos) (let* ((rowf (vector-ref F rowp)) (stateno (vector-ref to-state i)) (sp (vector-ref shift-table stateno))) (if sp (let loop2 ((j (shift-shifts sp)) (edges '())) (if (pair? j) (let ((symbol (vector-ref acces-symbol (car j)))) (if (< symbol nvars) (if (vector-ref nullable symbol) (loop2 (cdr j) (cons (map-goto stateno symbol) edges)) (loop2 (cdr j) edges)) (begin (set-bit rowf (- symbol nvars)) (loop2 (cdr j) edges)))) (if (pair? edges) (vector-set! reads i (reverse edges)))))) (loop (+ i 1) (+ rowp 1))))) (digraph reads))) (define (add-lookback-edge stateno ruleno gotono) (let ((k (vector-ref lookaheads (+ stateno 1)))) (let loop ((found #f) (i (vector-ref lookaheads stateno))) (if (and (not found) (< i k)) (if (= (vector-ref LAruleno i) ruleno) (loop #t i) (loop found (+ i 1))) (if (not found) (begin (display "Error in add-lookback-edge : ") (display (list stateno ruleno gotono)) (newline)) (vector-set! lookback i (cons gotono (vector-ref lookback i)))))))) (define (transpose r-arg n) (let ((new-end (make-vector n #f)) (new-R (make-vector n #f))) (do ((i 0 (+ i 1))) ((= i n)) (let ((x (list 'bidon))) (vector-set! new-R i x) (vector-set! new-end i x))) (do ((i 0 (+ i 1))) ((= i n)) (let ((sp (vector-ref r-arg i))) (if (pair? sp) (let loop ((sp2 sp)) (if (pair? sp2) (let* ((x (car sp2)) (y (vector-ref new-end x))) (set-cdr! y (cons i (cdr y))) (vector-set! new-end x (cdr y)) (loop (cdr sp2)))))))) (do ((i 0 (+ i 1))) ((= i n)) (vector-set! new-R i (cdr (vector-ref new-R i)))) new-R)) (define (build-relations) (define (get-state stateno symbol) (let loop ((j (shift-shifts (vector-ref shift-table stateno))) (stno stateno)) (if (null? j) stno (let ((st2 (car j))) (if (= (vector-ref acces-symbol st2) symbol) st2 (loop (cdr j) st2)))))) (set! includes (make-vector ngotos #f)) (do ((i 0 (+ i 1))) ((= i ngotos)) (let ((state1 (vector-ref from-state i)) (symbol1 (vector-ref acces-symbol (vector-ref to-state i)))) (let loop ((rulep (vector-ref derives symbol1)) (edges '())) (if (pair? rulep) (let ((*rulep (car rulep))) (let loop2 ((rp (vector-ref rrhs *rulep)) (stateno state1) (states (list state1))) (let ((*rp (vector-ref ritem rp))) (if (> *rp 0) (let ((st (get-state stateno *rp))) (loop2 (+ rp 1) st (cons st states))) (begin (if (not (vector-ref consistent stateno)) (add-lookback-edge stateno *rulep i)) (let loop2 ((done #f) (stp (cdr states)) (rp2 (- rp 1)) (edgp edges)) (if (not done) (let ((*rp (vector-ref ritem rp2))) (if (< -1 *rp nvars) (loop2 (not (vector-ref nullable *rp)) (cdr stp) (- rp2 1) (cons (map-goto (car stp) *rp) edgp)) (loop2 #t stp rp2 edgp))) (loop (cdr rulep) edgp)))))))) (vector-set! includes i edges))))) (set! includes (transpose includes ngotos))) (define (compute-lookaheads) (let ((n (vector-ref lookaheads nstates))) (let loop ((i 0)) (if (< i n) (let loop2 ((sp (vector-ref lookback i))) (if (pair? sp) (let ((LA-i (vector-ref LA i)) (F-j (vector-ref F (car sp)))) (bit-union LA-i F-j token-set-size) (loop2 (cdr sp))) (loop (+ i 1)))))))) (define (digraph relation) (define infinity (+ ngotos 2)) (define INDEX (make-vector (+ ngotos 1) 0)) (define VERTICES (make-vector (+ ngotos 1) 0)) (define top 0) (define R relation) (define (traverse i) (set! top (+ 1 top)) (vector-set! VERTICES top i) (let ((height top)) (vector-set! INDEX i height) (let ((rp (vector-ref R i))) (if (pair? rp) (let loop ((rp2 rp)) (if (pair? rp2) (let ((j (car rp2))) (if (= 0 (vector-ref INDEX j)) (traverse j)) (if (> (vector-ref INDEX i) (vector-ref INDEX j)) (vector-set! INDEX i (vector-ref INDEX j))) (let ((F-i (vector-ref F i)) (F-j (vector-ref F j))) (bit-union F-i F-j token-set-size)) (loop (cdr rp2)))))) (if (= (vector-ref INDEX i) height) (let loop () (let ((j (vector-ref VERTICES top))) (set! top (- top 1)) (vector-set! INDEX j infinity) (if (not (= i j)) (begin (bit-union (vector-ref F i) (vector-ref F j) token-set-size) (loop))))))))) (let loop ((i 0)) (if (< i ngotos) (begin (if (and (= 0 (vector-ref INDEX i)) (pair? (vector-ref R i))) (traverse i)) (loop (+ i 1)))))) ;; -- (define (build-tables) (define (add-action St Sym Act) (let* ((x (vector-ref ACTION-TABLE St)) (y (assv Sym x))) (if y (if (not (= Act (cdr y))) ;; -- there is a conflict (begin (if (and (<= (cdr y) 0) (<= Act 0)) (begin (display "%% Reduce/Reduce conflict ") (display "(reduce ") (display (- Act)) (display ", reduce ") (display (- (cdr y))) (display ") on ") (print-symbol (+ Sym nvars)) (display " in state ") (display St) (newline) (set-cdr! y (max (cdr y) Act))) (begin (display "%% Shift/Reduce conflict ") (display "(shift ") (display Act) (display ", reduce ") (display (- (cdr y))) (display ") on ") (print-symbol (+ Sym nvars)) (display " in state ") (display St) (newline) (set-cdr! y Act))))) (vector-set! ACTION-TABLE St (cons (cons Sym Act) x))))) (set! action-table (make-vector nstates '())) (do ((i 0 (+ i 1))) ; i = state ((= i nstates)) (let ((red (vector-ref reduction-table i))) (if (and red (>= (red-nreds red) 1)) (if (and (= (red-nreds red) 1) (vector-ref consistent i)) (add-action i 'default (- (car (red-rules red)))) (let ((k (vector-ref lookaheads (+ i 1)))) (let loop ((j (vector-ref lookaheads i))) (if (< j k) (let ((rule (- (vector-ref LAruleno j))) (lav (vector-ref LA j))) (let loop2 ((token 0) (x (vector-ref lav 0)) (y 1) (z 0)) (if (< token nterms) (begin (let ((in-la-set? (modulo x 2))) (if (= in-la-set? 1) (add-action i token rule))) (if (= y (BITS-PER-WORD)) (loop2 (+ token 1) (vector-ref lav (+ z 1)) 1 (+ z 1)) (loop2 (+ token 1) (quotient x 2) (+ y 1) z))))) (loop (+ j 1))))))))) (let ((shiftp (vector-ref shift-table i))) (if shiftp (let loop ((k (shift-shifts shiftp))) (if (pair? k) (let* ((state (car k)) (symbol (vector-ref acces-symbol state))) (if (>= symbol nvars) (add-action i (- symbol nvars) state)) (loop (cdr k)))))))) (add-action final-state 0 'accept)) (define (compact-action-table) (define (most-common-action acts) (let ((accums '())) (let loop ((l acts)) (if (pair? l) (let* ((x (cdar l)) (y (assv x accums))) (if (and (number? x) (< x 0)) (if y (set-cdr! y (+ 1 (cdr y))) (set! accums (cons `(,x . 1) accums)))) (loop (cdr l))))) (let loop ((l accums) (max 0) (sym #f)) (if (null? l) sym (let ((x (car l))) (if (> (cdr x) max) (loop (cdr l) (cdr x) (car x)) (loop (cdr l) max sym))))))) (do ((i 0 (+ i 1))) ((= i nstates)) (let ((acts (vector-ref action-table i))) (if (vector? (vector-ref reduction-table i)) (let ((act (most-common-action acts))) (vector-set! action-table i (cons `(default . ,(if act act 'error)) (filter (lambda (x) (not (eq? (cdr x) act))) acts)))) (vector-set! action-table i (cons `(default . *error*) acts)))))) (define (output-action-table prefix) (display "(defconst ") (display prefix) (display "action-table") (newline) (display " [") (newline) (do ((i 0 (+ i 1))) ((= i nstates)) (display " ") (write (vector-ref action-table i)) (newline)) (display " ])") (newline) (newline)) (define (output-goto-table prefix) (display "(defconst ") (display prefix) (display "goto-table") (newline) (display " [") (newline) (do ((i 0 (+ i 1))) ((= i nstates)) (display " ") (let ((shifts (vector-ref shift-table i))) (if shifts (begin (display "(") (let loop ((l (shift-shifts shifts))) (if (null? l) (display ")") (let* ((state (car l)) (symbol (vector-ref acces-symbol state))) (if (< symbol nvars) (display `(,symbol . ,state))) (loop (cdr l)))))) (display '()))) (newline)) (display " ])") (newline) (newline)) (define (output-reduction-table gram/actions prefix) (display "(defconst ") (display prefix) (display "reduction-table") (newline) (display " (vector") (newline) (display " '()") (newline) (for-each (lambda (p) (let ((act (cdr p))) (display " (lambda (stack sp goto-table $look)") (newline) (let* ((nt (caar p)) (rhs (cdar p)) (n (length rhs))) (display " (let* (") (if act (let loop ((i 1) (l rhs)) (if (not (null? l)) (let ((rest (cdr l))) (if (> i 1) (begin (newline) (display " "))) (display "($") (display (+ (- n i) 1)) (display " ") (display "(aref stack (- sp ") (display (- (* i 2) 1)) (display ")))") (loop (+ i 1) rest))))) (display ")") (newline) (display " ") (if (= nt 0) (display "(accept $1)") (begin (display "(lr-push stack (- sp ") (display (* 2 n)) (display ") ") (display nt) (display " goto-table ") (write (cdr p)) (display ")"))) (display "))") (newline)))) gram/Actions) (display " ))") (newline) (newline)) (define (output-header header parser-prefix) (display header) (display "(require 'lr-driver)") (newline) (newline)) (define (output-footer footer) (display footer) (newline) (newline)) (define (output-parser-def parser-prefix prefix) (display "(defun ") (display parser-prefix) (display "parse") (display "(scanner errorhandler)") (newline) (display " (lr-parse scanner errorhandler ") (newline) (display " ") (display prefix) (display "action-table") (newline) (display " ") (display prefix) (display "goto-table") (newline) (display " ") (display prefix) (display "reduction-table") (newline) (display " ") (display prefix) (display "token-defs))") (newline) (newline)) (define (output-token-defs terms prefix) (let loop ((i 0) (l terms)) (if (pair? l) (let ((x (car l))) (display "(defconst ") (display prefix) (write x) (display #\tab) (display i) (display ")") (newline) (loop (+ i 1) (cdr l))))) (newline) (display "(defconst ") (display prefix) (display "token-defs") (newline) (display " (list ") (newline) (let loop ((i 0) (l terms)) (if (pair? l) (begin (display " (cons ") (display i) (display " \"") (display (car l)) (display "\")") (newline) (loop (+ i 1) (cdr l))))) (display " ))") (newline) (newline)) ;; -- (define (rewrite-grammar grammar proc) (define eoi '*EOI*) (if (not (pair? grammar)) (error "Grammar definition must be a non-empty list") (let loop1 ((lst grammar) (rev-terms '())) (if (and (pair? lst) (not (pair? (car lst)))) ; definition d'un terminal? (let ((term (car lst))) (cond ((not (valid-terminal? term)) (error "Invalid terminal:" term)) ((member term rev-terms) (error "Terminal previously defined:" term)) (else (loop1 (cdr lst) (cons term rev-terms))))) (let loop2 ((lst lst) (rev-nonterm-defs '())) (if (pair? lst) (let ((def (car lst))) (if (not (pair? def)) (error "Nonterminal definition must be a non-empty list") (let ((nonterm (car def))) (cond ((not (valid-nonterminal? nonterm)) (error "Invalid nonterminal:" nonterm)) ((or (member nonterm rev-terms) (assoc nonterm rev-nonterm-defs)) (error "Nonterminal previously defined:" nonterm)) (else (loop2 (cdr lst) (cons def rev-nonterm-defs))))))) (let* ((terms (cons eoi (reverse rev-terms))) (nonterm-defs (reverse rev-nonterm-defs)) (nonterms (cons '*start* (map car nonterm-defs)))) (if (= (length nonterms) 1) (error "Grammar must contain at least one nonterminal") (let ((compiled-nonterminals (map (lambda (nonterm-def) (rewrite-nonterm-def nonterm-def terms nonterms)) (cons `(*start* (,(cadr nonterms) ,eoi) : $1) nonterm-defs)))) (proc terms nonterms (map (lambda (x) (cons (caaar x) (map cdar x))) compiled-nonterminals) (apply append compiled-nonterminals))))))))))) (define (rewrite-nonterm-def nonterm-def terms nonterms) (define No-NT (length nonterms)) (define (encode x) (let ((PosInNT (pos-in-list x nonterms))) (if PosInNT PosInNT (let ((PosInT (pos-in-list x terms))) (if PosInT (+ No-NT PosInT) (error "undefined symbol : " x)))))) (if (not (pair? (cdr nonterm-def))) (error "At least one production needed for nonterminal" (car nonterm-def)) (let ((name (symbol->string (car nonterm-def)))) (let loop1 ((lst (cdr nonterm-def)) (i 1) (rev-productions-and-actions '())) (if (not (pair? lst)) (reverse rev-productions-and-actions) (let* ((rhs (car lst)) (rest (cdr lst)) (prod (map encode (cons (car nonterm-def) rhs)))) (for-each (lambda (x) (if (not (or (member x terms) (member x nonterms))) (error "Invalid terminal or nonterminal" x))) rhs) (if (and (pair? rest) (eq? (car rest) ':) (pair? (cdr rest))) (loop1 (cddr rest) (+ i 1) (cons (cons prod (cadr rest)) rev-productions-and-actions)) (let* ((rhs-length (length rhs)) (action (cons 'VECTOR (cons (list 'QUOTE (string->symbol (string-append name "-" (number->string i)))) (let loop-j ((j 1)) (if (> j rhs-length) '() (cons (string->symbol (string-append "$" (number->string j))) (loop-j (+ j 1))))))))) (loop1 rest (+ i 1) (cons (cons prod action) rev-productions-and-actions)))))))))) (define (valid-nonterminal? x) (symbol? x)) (define (valid-terminal? x) (symbol? x)) ; DB ;; ---------------------------------------------------------------------- ;; ;; Miscellaneous ;; ;; ---------------------------------------------------------------------- ;; (define (pos-in-list x lst) (let loop ((lst lst) (i 0)) (cond ((not (pair? lst)) #f) ((equal? (car lst) x) i) (else (loop (cdr lst) (+ i 1)))))) (define (sunion lst1 lst2) ; union of sorted lists (let loop ((L1 lst1) (L2 lst2)) (cond ((null? L1) L2) ((null? L2) L1) (else (let ((x (car L1)) (y (car L2))) (cond ((> x y) (cons y (loop L1 (cdr L2)))) ((< x y) (cons x (loop (cdr L1) L2))) (else (loop (cdr L1) L2)) )))))) (define (sinsert elem lst) (let loop ((l1 lst)) (if (null? l1) (cons elem l1) (let ((x (car l1))) (cond ((< elem x) (cons elem l1)) ((> elem x) (cons x (loop (cdr l1)))) (else l1)))))) (define (filter p lst) (let loop ((l lst)) (if (null? l) '() (let ((x (car l)) (y (cdr l))) (if (p x) (cons x (loop y)) (loop y)))))) ;; ---------------------------------------------------------------------- ;; ;; Debugging tools ... ;; ;; ---------------------------------------------------------------------- ;; (define the-terminals #f) (define the-nonterminals #f) (define (print-item item-no) (let loop ((i item-no)) (let ((v (vector-ref ritem i))) (if (>= v 0) (loop (+ i 1)) (let* ((rlno (- v)) (nt (vector-ref rlhs rlno))) (display (vector-ref the-nonterminals nt)) (display " --> ") (let loop ((i (vector-ref rrhs rlno))) (let ((v (vector-ref ritem i))) (if (= i item-no) (display ". ")) (if (>= v 0) (begin (print-symbol v) (display " ") (loop (+ i 1))) (begin (display " (rule ") (display (- v)) (display ")") (newline)))))))))) (define (print-symbol n) (display (if (>= n nvars) (vector-ref the-terminals (- n nvars)) (vector-ref the-nonterminals n)))) (define (print-states) (define (print-action act) (cond ((eq? act '*error*) (display " : Error")) ((eq? act 'accept) (display " : Accept input")) ((< act 0) (display " : reduce using rule ") (display (- act))) (else (display " : shift and goto state ") (display act))) (newline) #t) (define (print-actions acts) (let loop ((l acts)) (if (null? l) #t (let ((sym (caar l)) (act (cdar l))) (display " ") (cond ((eq? sym 'default) (display "default action")) (else (print-symbol (+ sym nvars)))) (print-action act) (loop (cdr l)))))) (if (not action-table) (begin (display "No generated parser available!") (newline) #f) (begin (display "State table") (newline) (display "-----------") (newline) (newline) (let loop ((l first-state)) (if (null? l) #t (let* ((core (car l)) (i (core-number core)) (items (core-items core)) (actions (vector-ref action-table i))) (display "state ") (display i) (newline) (newline) (for-each (lambda (x) (display " ") (print-item x)) items) (newline) (print-actions actions) (newline) (loop (cdr l))))))))