pasterack/pasterack-parsing-utils.rkt
2015-05-13 22:59:21 +00:00

111 lines
4.1 KiB
Racket

#lang racket
(require syntax/stx)
(require "pasterack-utils.rkt")
;; parsing utility functions used by pasterack.org
(provide (all-defined-out))
;; lang regexp patterns
(define hashlang-pat #px"^\\#lang ([\\w/\\-\\+]+)\\s*(.*)")
(define weblang-pat #px"^web-server.*")
(define scribblelang-pat #px"^scribble/.*")
(define htdplang-pat #px"^htdp/(.*)")
(define TRlang-pat #px"^typed/racket.*")
(define plai-pat #px"^plai.*")
(define (hashlang? code)
(define in (open-input-string code))
(begin0 (read-language in (lambda () #f)) (close-input-port in)))
;; Returns #t if str has "#lang" somewhere.
(define (has-hashlang? str)
(regexp-match "#lang" str))
;; ie maps htdp/bsl -> lang/htdp-beginner
(define (htdplang->modulename lang)
(match (cadr (regexp-match htdplang-pat lang))
["bsl" "lang/htdp-beginner"]
["bsl+" "lang/htdp-beginner-abbr"]
["isl" "lang/htdp-intermediate"]
["isl+" "lang/htdp-intermediate-lambda"]
["asl" "lang/htdp-advanced"]
[_ "racket"]))
;; returns two string values, one for lang and one for the rest of the program
(define (hashlang-split code)
(match (regexp-match hashlang-pat code)
[(list _ lang rst) (values lang rst)]
[_ (values "racket" code)]))
(define (scribble-lang? lang) (regexp-match scribblelang-pat lang))
(define (htdp-lang? lang) (regexp-match htdplang-pat lang))
(define (TR-lang? lang) (regexp-match TRlang-pat lang))
(define (web-lang? lang) (regexp-match weblang-pat lang))
(define (plai-lang? lang) (regexp-match plai-pat lang))
;; htdp form patterns
(define provide-pat #px"^\\(provide (.*)\\)$")
(define require-pat #px"^\\(require (.*)\\)$")
(define define-pat #px"^\\(define(.*)\\)$")
(define check-pat #px"^\\(check-(.*)\\)$")
(define (require-datum? e) (get-require-spec e))
(define (provide-datum? e) (regexp-match provide-pat (to-string/s e)))
(define (define-datum? e) (regexp-match define-pat (to-string e)))
(define (check-datum? e) (regexp-match check-pat (to-string e)))
(define (get-require-spec e) (regexp-match require-pat (to-string/s e)))
;; for now, only accept certain forms
;; (ie reject strings)
(define (valid-req? r)
(or (symbol? r)
(and (pair? r)
(let ([form (car r)])
(define (symeq? x) (eq? x form))
(or
(and (ormap symeq? '(only-in except-in rename-in))
(valid-req? (second r)))
(and (ormap symeq? '(prefix-in))
(valid-req? (third r)))
(and (ormap symeq? '(combine-in))
(andmap valid-req? (cdr r))))))))
(define (not-htdp-expr? e) (or (require-datum? e) (provide-datum? e)
(check-datum? e) (define-datum? e)))
;; wont work if s has a #lang line
;; returns list of datums
(define (string->datums s)
(with-handlers ([exn:fail? (lambda () null)])
(with-input-from-string s (lambda () (for/list ([e (in-port)]) e)))))
;; stx predicates
(define (not-expr? d [out (current-output-port)])
(with-handlers ([exn:fail:syntax? (lambda (e) (displayln (exn-message e)) #t)])
(define expanded (expand-to-top-form d))
(define hd (and (stx-pair? expanded)
;; not identifier always means %#app, %#datum, or %#top (?)
;; ie, an expression?
(identifier? (stx-car expanded))
(stx-car expanded)))
;; (fprintf out "expanded: ~a\n" (syntax->datum expanded))
;; (fprintf out "hd: ~a\n" hd)
(and hd
;; check for begin
(or (and (free-identifier=? hd #'begin)
(for/and ([s (syntax->list (stx-cdr (expand d)))])
(not-expr? s out)))
(and
;; (when (or (free-identifier=? hd #'define-syntaxes)
;; (free-identifier=? hd #'begin-for-syntax)
;; (free-identifier=? hd #'#%require))
;; (eval d))
(for/or ([form
(syntax->list
;; ok to do define-values from interactions prompt
;; (but set! must be classified same as define-values)
#'(module module* begin-for-syntax
#%provide #%require define-syntaxes))])
(free-identifier=? hd form)))))))