#lang s-exp "lang.ss"
(require "rbtree.ss")
(define (list? datum)
(or (empty? datum)
(and
(pair? datum)
(list? (rest datum)))))
(define (symbol< x y)
(string<? (symbol->string x)
(symbol->string y)))
(define (program? datum)
(and (list? datum)
(andmap (lambda (x)
(or (defn? x)
(expression? x)
(test-case? x)
(library-require? x)))
datum)))
(define (expression? an-expr)
(and (not (defn? an-expr))
(not (test-case? an-expr))
(not (library-require? an-expr))))
(define (defn? an-sexp)
(cond
[(stx-begins-with? an-sexp 'define)
true]
[(stx-begins-with? an-sexp 'define-struct)
true]
[else
false]))
(define (string-join strs delim)
(cond
[(empty? strs)
""]
[(empty? (rest strs))
(first strs)]
[else
(string-append
(first strs)
delim
(string-join (rest strs) delim))]))
(define (test-case? an-sexp)
(or (stx-begins-with? an-sexp 'check-expect)
(stx-begins-with? an-sexp 'check-within)
(stx-begins-with? an-sexp 'check-error)))
(define (library-require? an-sexp)
(stx-begins-with? an-sexp 'require))
(define java-identifiers
(foldl (lambda (sym an-rbtree)
(rbtree-insert symbol< an-rbtree sym true))
empty-rbtree
'(abstract continue for new switch
assert default goto package synchronized
boolean do if private this
break double implements protected throw
byte delete else import public throws
case enum instanceof instanceOf return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while null
comment export import in label typeof with false true
debugger)))
(define special-character-mappings
(foldl (lambda (ch+translation an-rbtree)
(rbtree-insert char<? an-rbtree (first ch+translation) (second ch+translation)))
empty-rbtree
'((#\- "_dash_")
(#\_ "_underline_")
(#\? "_question_")
(#\! "_bang_")
(#\. "_dot_")
(#\: "_colon_")
(#\= "_equal_")
(#\@ "_at_")
(#\# "_pound_")
(#\$ "_dollar_")
(#\% "_percent_")
(#\^ "_tilde_")
(#\& "_and_")
(#\* "_star_")
(#\+ "_plus_")
(#\/ "_slash_")
(#\< "_lessthan_")
(#\> "_greaterthan_")
(#\~ "_tilde_"))))
(define (translate-special-character ch)
(cond
[(cons? (rbtree-lookup char<? special-character-mappings ch))
(second (rbtree-lookup char<? special-character-mappings ch))]
[else
(string ch)]))
(define (identifier->munged-java-identifier an-id)
(cond
[(cons? (rbtree-lookup symbol< java-identifiers an-id))
(string->symbol (string-append "_" (symbol->string an-id) "_"))]
[else
(local [(define (maybe-prepend-hyphen chars)
(cond
[(member (first chars) (string->list "0123456789"))
(cons #\- chars)]
[else
chars]))
(define chars (maybe-prepend-hyphen (string->list (symbol->string an-id))))
(define translated-chunks
(map translate-special-character chars))
(define translated-id
(string->symbol
(string-join translated-chunks "")))]
translated-id)]))
(define (remove-leading-whitespace/list chars)
(cond
[(empty? chars)
""]
[(char-whitespace? (first chars))
(remove-leading-whitespace/list (rest chars))]
[else
(list->string chars)]))
(define (remove-leading-whitespace a-str)
(remove-leading-whitespace/list (string->list a-str)))
(define (take a-list n)
(cond
[(= n 0)
empty]
[else
(cons (first a-list)
(take (rest a-list) (sub1 n)))]))
(define (list-tail a-list n)
(cond
[(= n 0)
a-list]
[else
(list-tail (rest a-list)
(sub1 n))]))
(define (range n)
(cond
[(= n 0)
empty]
[else
(append (range (sub1 n))
(list (sub1 n)))]))
(define (case-analyze-definition a-definition
f-function f-regular-definition f-define-struct) (cond
[(and (stx-begins-with? a-definition 'define)
(= (length (stx-e a-definition)) 3)
(stx:list? (second (stx-e a-definition))))
(local [(define id (first (stx-e (second (stx-e a-definition)))))
(define args (rest (stx-e (second (stx-e a-definition)))))
(define body (third (stx-e a-definition)))]
(begin
(check-single-body-stx! (rest (rest (stx-e a-definition))) a-definition)
(f-function id args body)))]
[(and (stx-begins-with? a-definition 'define)
(= (length (stx-e a-definition)) 3)
(symbol? (stx-e (second (stx-e a-definition))))
(stx-begins-with? (third (stx-e a-definition)) 'lambda))
(local [(define id (second (stx-e a-definition)))
(define args (stx-e (second (stx-e (third (stx-e a-definition))))))
(define body (third (stx-e (third (stx-e a-definition)))))]
(begin
(check-single-body-stx! (rest (rest (stx-e (third (stx-e a-definition))))) a-definition)
(f-function id args body)))]
[(and (stx-begins-with? a-definition 'define)
(= (length (stx-e a-definition)) 3)
(symbol? (stx-e (second (stx-e a-definition))))
(not (stx-begins-with? (third (stx-e a-definition)) 'lambda)))
(local [(define id (second (stx-e a-definition)))
(define body (third (stx-e a-definition)))]
(f-regular-definition id body))]
[(and (stx-begins-with? a-definition 'define-struct)
(= (length (stx-e a-definition)) 3)
(symbol? (stx-e (second (stx-e a-definition))))
(or (empty? (stx-e (third (stx-e a-definition))))
(pair? (stx-e (third (stx-e a-definition))))))
(local [(define id (second (stx-e a-definition)))
(define fields (stx-e (third (stx-e a-definition))))]
(f-define-struct id fields))]
[(stx-begins-with? a-definition 'define)
(syntax-error
"define expects an identifier and a body. e.g. (define answer 42)"
a-definition)]
[(stx-begins-with? a-definition 'define-struct)
(syntax-error
"define-struct expects an identifier and a list of fields. i.e. (define-struct pizza (dough sauce toppings))"
a-definition)]))
(define (symbol-stx? x)
(and (stx? x)
(symbol? (stx-e x))))
(define (check-duplicate-identifiers! ids)
(local [(define (loop ids known-ids)
(cond
[(empty? ids)
(void)]
[else
(cond [(member (stx-e (first ids)) known-ids)
(syntax-error (format "found a name that's used more than once: ~s"
(stx->datum (first ids)))
(first ids))]
[(not (symbol? (stx-e (first ids))))
(syntax-error (format "not an identifier: ~s" (stx->datum (first ids)))
(first ids))]
[else
(loop (rest ids)
(cons (stx-e (first ids))
known-ids))])]))]
(loop ids empty)))
(define (check-single-body-stx! stxs original-stx)
(cond
[(empty? stxs)
(syntax-error "There must be a single body expression"
original-stx)]
[(not (empty? (rest stxs)))
(syntax-error "There must be a single body expression"
original-stx)]
[else
(void)]))
(define (mapi f lst)
(local ([define (loop lst i)
(cond
[(empty? lst)
empty]
[else
(cons (f (first lst) i)
(loop (rest lst) (add1 i)))])])
(loop lst 0)))
(provide/contract [symbol< (symbol? symbol? . -> . boolean?)]
[mapi ((any/c number? . -> . any/c) (listof any/c) . -> . (listof any/c))]
[program? (any/c . -> . boolean?)]
[expression? (any/c . -> . boolean?)]
[defn? (any/c . -> . boolean?)]
[test-case? (any/c . -> . boolean?)]
[library-require? (any/c . -> . boolean?)]
[take ((listof any/c) number? . -> . (listof any/c))]
[list-tail ((listof any/c) number? . -> . (listof any/c))]
[remove-leading-whitespace (string? . -> . string?)]
[identifier->munged-java-identifier (symbol? . -> . symbol?)]
[range (number? . -> . (listof number?))]
[check-duplicate-identifiers! ((listof stx?) . -> . any)]
[check-single-body-stx! ((listof stx?) stx? . -> . any)]
[case-analyze-definition (stx?
(symbol-stx? (listof symbol-stx?) stx? . -> . any)
(symbol-stx? any/c . -> . any)
(symbol-stx? (listof symbol-stx?) . -> . any)
. -> . any)]
[string-join ((listof string?) string? . -> . string?)])