;; ;; A parser for I-expressions (based on SRFI-49) ;; ;; Copyright 2010-2012 Ivan Raikov and the Okinawa Institute of ;; Science and Technology. ;; ;; This program 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 3 of the ;; License, or (at your option) any later version. ;; ;; This program 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. ;; ;; A full copy of the GPL license can be found at ;; . ;; (module iexpr (parse tree-empty? tree-level tree-child tree-sibling tree->list) (import scheme chicken ) (require-library srfi-1) (import (only extras fprintf) (only data-structures conc identity compose)) (require-extension datatype) (define-syntax tok (syntax-rules () ((tok line t) (make-lexical-token (quasiquote t) (make-source-location #f line #f #f #f) #f)) ((tok line t l) (make-lexical-token (quasiquote t) (make-source-location #f line #f #f #f) l)) )) (define-record-type line (make-line indent content) line? (indent line-indent) (content line-content)) (define-record-printer (line x out) (fprintf out "#(line ~S ~S)" (line-indent x) (line-content x) )) (include "iexpr.grm.scm") (include "iexpr.l.scm") (define (make-parse-error loc) (lambda (msg #!optional arg) (let ((loc-str (or (and loc (cond ((list? loc) (sprintf "~A" loc)) ((string? loc) (sprintf "(~A)" loc)) (else (sprintf "(~A)" loc)))) ""))) (cond [(not arg) (error loc-str msg)] [(lexical-token? arg) (error (sprintf "~A, line ~A" loc-str (source-location-line (lexical-token-source arg))) (sprintf "~A ~A~A" msg (lexical-token-category arg) (if (lexical-token-value arg) (sprintf " ~A" (lexical-token-value arg)) "")))] [else (error loc-str (sprintf "~A ~A" msg arg))] )))) (define lexer-error error) (define-datatype tree tree? (Empty) (Tree (level integer?) (text identity) (sibling tree?) (child tree?))) (define-record-printer (tree x out) (cases tree x (Empty () (fprintf out "#")) (Tree (l t s c) (fprintf out "#" l t s c)) )) (define (tree-empty? t) (cases tree t (Tree (_ _ _ _) #f) (Empty () #t))) (define (tree-level t) (cases tree t (Tree (l _ _ child) l) (Empty () (error 'tree-level "tree is empty")))) (define (tree-child t) (cases tree t (Tree (_ _ _ child) child) (Empty () (error 'tree-child "tree is empty")))) (define (tree-sibling t) (cases tree t (Tree (_ _ sib _) sib) (Empty () (error 'tree-sibling "tree is empty")))) (define (tree-update-child t child) (cases tree t (Tree (l text sib child1) (Tree l text sib (tree-add-sibling child1 child))) (Empty () (error 'tree-update-child "tree is empty")))) (define (tree-add-sibling t sib) (if (tree-empty? sib) t (cases tree t (Tree (l text sib1 child) (cases tree sib (Tree (sl stext _ schild) (Tree l text (Tree sl stext sib1 schild) child)) )) (Empty () sib)))) (define-datatype zipper zipper? (End) (Zip (level integer?) (tree tree?) (parent zipper?)) ) (define (zip-level z) (cases zipper z (Zip (l _ _ ) l) (End () (error 'zip-level "already at end")))) (define (zip-parent z) (cases zipper z (Zip (_ _ p) p) (End () (error 'zip-parent "already at end")))) (define (zip-tree z) (cases zipper z (Zip (_ t _) t) (End () (error 'zip-tree "already at end")))) (define (zip-end? z) (cases zipper z (Zip (_ _ _) #f) (End () #t))) (define (zip-to-root z) (cases zipper z (Zip (l t p) (if (zip-end? p) z (zip-to-root (zip-up z)))) (End () (error 'zip-to-root "already at end")))) (define (zip-update-tree z t) (cases zipper z (Zip (l _ p) (cases zipper p (End () (Zip l t p)) (Zip (_ t1 _) (Zip l t (zip-update-tree p (tree-update-child t1 t)))) )) (End () z))) (define (zip-up z) (cases zipper z (Zip (_ t p) (cases zipper p (End () z) (Zip (l pt pp) (Zip l (tree-update-child pt t) pp)) )) (End () (error 'zip-up "already at end")))) (define (zip-insert-sibling z level text) (cases zipper z (Zip (l t parent) (if (= l level) (let ((t1 (Tree level text t (Empty)))) (Zip l t1 parent)) (else (error 'zip-insert-sibling "new sibling must have level " l)))) (End () (error 'zip-insert-sibling "already at end")))) (define (zip-insert-child z level text) (cases zipper z (Zip (zl zt zparent) (cases tree zt (Empty () (let* ((child (Tree level text (Empty) (Empty))) (t (Tree 0 '(toplevel) (Empty) child))) (Zip level child (Zip zl t zparent)))) (Tree (_ ztext zsib child) (if (< zl level) (let* ((child1 (Tree level text child (Empty)))) (Zip level child1 z)) (else (error 'zip-insert-child "new child must have level greater than " zl)))) )) (End () (error 'zip-insert-child "already at end")))) (define (initial-line-tree line) (let ((n (line-indent line)) (text (line-content line))) (Zip n (Tree n text (Empty) (Empty)) (End)))) (define (final-line-tree z) (zip-tree (zip-to-root z))) (define (tree-insert-line line z) (cond ((< (line-indent line) (zip-level z)) (tree-insert-line line (zip-up z))) ((= (line-indent line) (zip-level z)) (zip-insert-sibling z (line-indent line) (line-content line))) ((> (line-indent line) (zip-level z)) (zip-insert-child z (line-indent line) (line-content line))) )) (define (tree->list t) (let recur ((t t) (level 0) (ax '())) (cases tree t (Tree (l tt s c) (let ((n (if (tree-empty? c) tt (append tt (tree->list c))))) (recur s l (cons n ax)) )) (Empty () ax)))) (define (parse loc s) (cond ((port? s) (lexer-init 'port s)) ((string? s) (lexer-init 'string s)) (else (error 'parse "bad argument type; not a string or port" s)) ) (parser lexer (make-parse-error loc))) )