#cs (module class mzscheme (require "data-defs.scm" (lib "contract.ss") (lib "etc.ss") (lib "list.ss") (lib "string.ss" "srfi" "13") (only (lib "1.ss" "srfi") zip)) ;; ------------------------------------------------------------------------ (provide/contract [make-class ((Class) (boolean? boolean? string? Fields (listof Method)) . opt-> . string?)] [method (Method . -> . string?)] ) #| Usage: (make-class a-class toString? template? language? Fields Methods) create a Java class from the class specification (a-class) and add Fields from super and Methods from the implementing interface. The first optional boolean parameter specifies whether we want toString in the class hiearchy. Default: true The second optional boolean parameter specifies whether we want a draft method template in comments. Default: true The third optinal determines the language level. |# ;; ------------------------------------------------------------------------ ;; Construct individual Java classes ;; create class definition (as string) from name, super and list of fields (define make-class (opt-lambda (the-class [toString? #t][template? #t][language BEGINNER][super-fields '()][method-sigs '()]) (define no-public (or (eq? language BEGINNER) (or (eq? language INTERMEDIATE)))) (let ([type (car the-class)] [super (cadr the-class)] [fields (caddr the-class)] [prps (class-purpose the-class)]) (define templates (if (or (not template?) (eq? language BEGINNER)) "" (enumerate-with-separator (map (lambda (ms) (apply string-append (make-template ms super fields template? (not no-public)))) method-sigs) "\n"))) (apply string-append `( ,@(purpose-statement prps) ,(format classf type (implements super)) ;; fields ,@(map (lambda (f) (format declaf (car f) (cadr f))) fields) "\n" ;; constructor ,(class-constructor type fields super-fields) ;; optional template draft: ,templates ;; optional toString method: ,@(toString type fields no-public toString?) ,endclf))))) ;; String -> String (define (implements super) (if (string=? "" super) "" (format implementsf super))) ;; String Fields Fields -> String (define (class-constructor type fields super-fields) (apply string-append `( ,(format constf type (parameters (append super-fields fields))) #| When we switch to super in beginner, this will need to change. ;; call to super(super-fields) ,@(if (null? super-fields) '() (list (format superf (commas (map cadr super-fields))))) |# ;; init for fields ,@(map (lambda (f) (format initif (cadr f) (cadr f))) ;; When we switch to super in beginner, ... (append super-fields fields)) ,endMet))) ;; Fields -> String ;; create a paremeter list from a field specifications (define (parameters fs) (enumerate-with-separator (map (lambda (f) (format paraf (car f) (cadr f))) fs))) ;; String String Fields Boolean -> (listof String) (define (make-template sig super fields template? public?) (define head (format contef (method sig))) (if (not template?) (list "") `("\n" ;; template method ,cmnt/* ,warnin ,(if public? (string-append " public " (string-trim head)) head) ,@(map (lambda (f) (if (string=? (car f) super) (format temprf (cadr f) (cadr sig)) (format tempsf (cadr f)))) fields) ,endMet ,cmnt*/))) ;; Method -> String (define (method m) (define m+n (zip (cddr m) PARAS)) (define sig (map (lambda (x) (format "~a ~a" (car x) (cadr x))) m+n)) (format intff (car m) (cadr m) (enumerate-with-separator sig ","))) (define PARAS '("x" "y" "z" "u" "v" "w" "s" "t")) ;; String Fields -> (cons String (listof String)) ;; create a toString method for class type with _fields_ (define toString (opt-lambda (type fields [special? #f][toString? #t]) (if (not toString?) (list "") (list "\n" (string-append (if special? toStrf pbStrf) (format prefix type) ; (apply string-append) (if (null? fields) " + " (format " + ~a + " (enumerate-with-separator (map addToString fields) infix))) postfix endMet))))) ;(provide toString) ;; Field -> String ;; create a call to toString, if necessary (define (addToString f) (let ([t (car f)] [s (cadr f)]) (if (member t StringNative) s (format "~a.toString()" s)))) ;; String -> (list String) (provide/contract [purpose-statement (-> string? (listof string?))]) (define (purpose-statement prps) (if (string=? "" prps) '("") (list (format "// ~a~n" prps)))) ;; identifiers ending in f are format strings, and require ~n for newline ;; Abstract Template (define purpos " // purpose statement \n") ;; Class (define classf "class ~a ~a{~n") (define implementsf "implements ~a ") ;; Fields (define declaf " ~a ~a;~n") ;; Constructor (define constf " ~a(~a) {~n") (define paraf "~a ~a") (define superf " super(~a);~n") (define initif " this.~a = ~a;~n") (define endMet " }\n") (provide endclf cmnt/* cmnt*/ warnin purpos) (define endclf "}\n") ;; Concrete Template (define warnin " // ** DRAFT TEMPLATE ** Edit as needed.\n") (define intff "~a ~a(~a)") (define contef " ~a {\n") (define tempsf " ... this.~a ...\n") (define temprf " ... this.~a.~a(...) ...\n") ;; toString (define pbStrf " public String toString() {\n") (define toStrf " String toString() {\n") (define prefix " return \"new ~a(\"") (define infix " + \",\" + ") (define postfix "\")\";\n") (define StringNative '("int" "boolean" "String" "double")) ;; and others ;; Comments (define cmnt/* "/*\n") (define cmnt*/ "*/\n") ;; ------------------------------------------------------------------------ ;; Library (provide/contract [enumerate-with-separator (opt-> ((listof string?)) (string?) string?)]) ;; (Listof String) -> String ;; create a comma-separated string from a list of strings (define (enumerate-with-separator l . comma) (let ([comma (if (null? comma) ", " (car comma))]) (if (null? l) "" (apply string-append (car l) (map (lambda (x) (string-append comma x)) (cdr l)))))) ;; ------------------------------------------------------------------------ ;; TESTS: (define empty-template `("/*\n" ,warnin " ??? mmm() {\n" " }\n" "*/\n" "\n")) (define public-empty-template `("/*\n" ,warnin " public ??? mmm() {\n" " }\n" "*/\n" "\n")) #| Tests : (require (lib "testing.scm" "testing")) (test== (enumerate-with-separator '()) "") (test== (enumerate-with-separator '("x" "y") ) "x, y") (test== (enumerate-with-separator '("x" "y") " + ") "x + y") (test== (parameters '()) "") (test== (parameters '(("int" "x"))) "int x") (test== (parameters '(("int" "x") ("foo" "y"))) "int x, foo y") (test== (enumerate-with-separator '("x" "y") " + ") "x + y") (test== (enumerate-with-separator '("x" "y" "z") " + ") "x + y + z") (test== (cadr (toString "foo" '())) (string-append pbStrf " return \"new foo(\" + \")\";\n" endMet)) (test== (cadr (toString "foo" '() #t)) (string-append toStrf " return \"new foo(\" + \")\";\n" endMet)) (test== (cadr (toString "Foo" '(("Foo" "x") ("Moo" "y")))) (string-append pbStrf " return \"new Foo(\" + x.toString() + \",\" + y.toString() + \")\";\n" endMet)) (test== (cadr (toString "Foo" '(("Foo" "x") ("Moo" "y")) #t)) (string-append toStrf " return \"new Foo(\" + x.toString() + \",\" + y.toString() + \")\";\n" endMet)) (test== (cadr (toString "Foo" '(("int" "x")))) (string-append pbStrf " return \"new Foo(\" + x + \")\";\n" endMet)) (test== (cadr (toString "Foo" '(("Boolean" "x")))) (string-append pbStrf " return \"new Foo(\" + x.toString() + \")\";\n" endMet)) (test== (cadr (toString "Foo" '(("Foo" "x") ("int" "y") ("Z" "z")))) (string-append pbStrf " return \"new Foo(\" + x.toString() + \",\" + y + \",\" + z.toString() + \")\";\n" endMet)) (test== (toString "Foo" '(("Foo" "x") ("int" "y") ("Z" "z")) #f #f) (list "")) (test== (class-constructor "Node" '(("int" "x")) '(("Info" "i") ("ATree" "parent"))) (string-append " Node(Info i, ATree parent, int x) {\n" " this.i = i;\n" " this.parent = parent;\n" " this.x = x;\n" " }\n" ) "class constructor with super fields") (test== (make-class (list "foo" "" '()) #t #t INTERMEDIATE '() '(("???" "mmm"))) (apply string-append `( "class foo {\n" "\n" " foo() {\n" " }\n" "\n" ,@empty-template ,toStrf " return \"new foo(\" + \")\";\n" " }\n" "}\n")) "class with template in INTERMEDIATE") (test== (make-class (list "moo" "foo" '()) #t #t PROFESSIONAL '() '(("???" "mmm"))) (apply string-append `("class moo implements foo {\n" "\n" " moo() {\n" " }\n" "\n" ,@public-empty-template ,pbStrf " return \"new moo(\" + \")\";\n" " }\n" "}\n"))) (test== (make-class (list "moo" "foo" '(("int" "x") ("foo" "f"))) #t #t PROFESSIONAL '() '(("???" "mmm"))) (apply string-append `("class moo implements foo {\n" " int x;\n" " foo f;\n" "\n" " moo(int x, foo f) {\n" " this.x = x;\n" " this.f = f;\n" " }\n" "\n" ,cmnt/* ,warnin " public ??? mmm() {\n" " ... this.x ...\n" " ... this.f.mmm(...) ...\n" " }\n" ,cmnt*/ "\n" ,pbStrf " return \"new moo(\" + x + \",\" + f.toString() + \")\";\n" " }\n" "}\n"))) (test== (make-class (list "CartPt" "" '(("int" "x") ("int" "y"))) #t #t PROFESSIONAL '() '(("???" "mmm"))) (apply string-append `("class CartPt {\n" " int x;\n" " int y;\n" "\n" " CartPt(int x, int y) {\n" " this.x = x;\n" " this.y = y;\n" " }\n" "\n" ,cmnt/* ,warnin " public ??? mmm() {\n" " ... this.x ...\n" " ... this.y ...\n" " }\n" ,cmnt*/ "\n" ,pbStrf " return \"new CartPt(\" + x + \",\" + y + \")\";\n" " }\n" "}\n"))) (test== (make-class (list "foo" "" '() "hello world") #t #t PROFESSIONAL '() '(("???" "mmm"))) (apply string-append `( "// hello world\n" "class foo {\n" "\n" " foo() {\n" " }\n" "\n" ,@public-empty-template ,pbStrf " return \"new foo(\" + \")\";\n" " }\n" "}\n"))) (test== (make-class (list "foo" "" '() "hello world") #t #t INTERMEDIATE '() '(("???" "mmm"))) (apply string-append `( "// hello world\n" "class foo {\n" "\n" " foo() {\n" " }\n" "\n" ,@empty-template ,toStrf " return \"new foo(\" + \")\";\n" " }\n" "}\n"))) |# )