From 20093edc36998a7e7f9fcedc6e9adc714a0ea305 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Mon, 8 Feb 2016 15:23:55 -0800 Subject: [PATCH] more --- coerce.rkt | 6 +- container.rkt | 46 ---------- define.rkt | 2 +- file.rkt | 6 +- len.rkt | 15 ---- list.rkt | 2 +- main.rkt | 6 -- scribblings/sugar.scrbl | 12 +-- scribblings/xml.scrbl | 2 +- string.rkt | 25 ------ test/main.rkt | 97 +--------------------- unstable/container.rkt | 86 +++++++++++++++++++ include.rkt => unstable/include.rkt | 20 ++++- unstable/len.rkt | 31 +++++++ misc.rkt => unstable/misc.rkt | 0 {test => unstable}/no-lang-line-source.txt | 0 {test => unstable}/source.rkt | 0 unstable/string.rkt | 37 +++++++++ xml.rkt | 11 ++- 19 files changed, 196 insertions(+), 208 deletions(-) delete mode 100644 container.rkt delete mode 100644 len.rkt delete mode 100644 string.rkt create mode 100644 unstable/container.rkt rename include.rkt => unstable/include.rkt (93%) create mode 100644 unstable/len.rkt rename misc.rkt => unstable/misc.rkt (100%) rename {test => unstable}/no-lang-line-source.txt (100%) rename {test => unstable}/source.rkt (100%) create mode 100644 unstable/string.rkt diff --git a/coerce.rkt b/coerce.rkt index 9ad2042..d18c92d 100644 --- a/coerce.rkt +++ b/coerce.rkt @@ -1,6 +1,6 @@ #lang racket/base (require (for-syntax racket/base racket/syntax)) -(require net/url racket/set racket/sequence "len.rkt" "define.rkt") +(require net/url racket/set racket/sequence "unstable/len.rkt" "define.rkt") (define-syntax-rule (make-coercion-error-handler target-format x) (λ(e) (error (string->symbol (format "->~a" target-format)) (format "Can't convert ~s to ~a" x target-format)))) @@ -14,7 +14,7 @@ [(complex? x) (->int (real-part x))] [(string? x) (let ([strnum (string->number x)]) (if (real? strnum) (->int strnum) (error 'ineligible-string)))] - [(or (symbol? x) (path? x)) (->int (->string x))] + [(or (symbol? x) (path? x) (bytes? x)) (->int (->string x))] [(char? x) (char->integer x)] [else (len x)]))) ; covers Lengthable types @@ -29,7 +29,7 @@ [(symbol? x) (symbol->string x)] [(number? x) (number->string x)] [(path? x) (path->string x)] - [(char? x) (format "~a" x)] + [(or (char? x) (bytes? x)) (format "~a" x)] [(url? x) (url->string x)] [else (error 'bad-type)])))) diff --git a/container.rkt b/container.rkt deleted file mode 100644 index 7704eff..0000000 --- a/container.rkt +++ /dev/null @@ -1,46 +0,0 @@ -#lang racket/base -(require "define.rkt" "coerce.rkt" "len.rkt" racket/list racket/set racket/sequence racket/stream racket/dict) - -(define (sliceable-container? x) - (ormap (λ(proc) (proc x)) (list list? string? symbol? vector? path? (λ(i) (and (not (dict? i)) (sequence? i)))))) - -(define (gettable-container? x) - (ormap (λ(proc) (proc x)) (list sliceable-container? dict?))) - - -(define+provide+safe (get container start [end #f]) - ((gettable-container? any/c) ((or/c (and/c integer? positive?) #f)) . ->* . any) - - (define result - ;; use handler to capture error & print localized error message - (with-handlers ([exn:fail? (λ(exn) (error (format "get: couldn't retrieve ~a from ~a" (if end (format "items ~a through ~a" start end) (format "item ~a" start)) container)))]) - (let ([end (if (and (equal? end #f) (sliceable-container? container)) (add1 start) end)]) - (cond - [(list? container) (for/list ([i (in-range start end)]) (list-ref container i))] - [(vector? container) (for/vector ([i (in-range start end)]) (vector-ref container i))] - [(string? container) (substring container start end)] - [(symbol? container) (->symbol (get (->string container) start end))] - [(path? container) (get (explode-path container) start end)] - [(dict? container) (dict-ref container start)] - [(sequence? container) (get (->list container) start end)] - [else (error)])))) - - ;; don't return single-item results inside a list - ;; check for integer because integers don't have length - (if (and (not (integer? result)) (= (len result) 1) (sliceable-container? container)) - (car (->list result)) - result)) - -(define (listlike-container? container) - (ormap (λ(pred) (pred container)) (list vector? set? sequence?))) - -(define+provide+safe (in? item container) - (any/c any/c . -> . boolean?) - (->boolean (cond - [(list? container) (member item container)] - [(dict? container) (dict-has-key? container item)] - [(path? container) (in? (->path item) (explode-path container))] - [(stringish? container) (regexp-match (->string item) (->string container))] - ;; location relevant because dicts and strings are also listlike (= sequences) - [(listlike-container? container) (in? item (->list container))] - [else #f]))) diff --git a/define.rkt b/define.rkt index 715a236..3c15665 100644 --- a/define.rkt +++ b/define.rkt @@ -13,7 +13,7 @@ ;; need to use stx as context to get correct require behavior (datum->syntax stx `(begin (module mod-name typed/racket/base/no-check - (require sugar/include) + (require sugar/unstable/include) (include-without-lang-line ,(syntax->datum #'path-spec))) (require (quote mod-name)))))])) diff --git a/file.rkt b/file.rkt index 0d61f04..3d8b5b9 100644 --- a/file.rkt +++ b/file.rkt @@ -1,5 +1,5 @@ #lang racket/base -(require "define.rkt" racket/set "coerce.rkt" racket/path "string.rkt") +(require "define.rkt" racket/set "coerce.rkt" racket/path "unstable/string.rkt") (define+provide+safe (get-enclosing-dir p) @@ -45,7 +45,7 @@ (coerce/path? . -> . path?) ;; pass through hidden files (those starting with a dot) (let ([x (->path x)]) - (if (x . starts-with? . ".") + (if ((->string x) . starts-with? . ".") x (path-replace-suffix x "")))) @@ -55,7 +55,7 @@ (coerce/path? . -> . path?) ;; pass through hidden files (those starting with a dot) (let ([x (->path x)]) - (if (x . starts-with? . ".") + (if ((->string x) . starts-with? . ".") x (let ([path-with-removed-ext (remove-ext x)]) (if (equal? x path-with-removed-ext) diff --git a/len.rkt b/len.rkt deleted file mode 100644 index 863c944..0000000 --- a/len.rkt +++ /dev/null @@ -1,15 +0,0 @@ -#lang racket/base -(require "define.rkt" racket/set racket/sequence) - -(define+provide+safe (len x) - ((or/c list? vector? set? sequence? string? symbol? path? hash?) . -> . integer?) - (cond - [(list? x) (length x)] - [(string? x) (string-length x)] - [(symbol? x) (len (symbol->string x))] - [(path? x) (len (path->string x))] - [(vector? x) (vector-length x)] - [(hash? x) (len (hash-keys x))] - [(set? x) (len (set->list x))] - [(and (sequence? x) (not (integer? x))) (len (sequence->list x))] - [else (error "len: can't calculate length of" x)])) \ No newline at end of file diff --git a/list.rkt b/list.rkt index c85afd0..a363ac3 100644 --- a/list.rkt +++ b/list.rkt @@ -1,6 +1,6 @@ #lang racket/base (require (for-syntax racket/base) racket/list racket/set racket/function) -(require "len.rkt" "coerce.rkt" "define.rkt") +(require "unstable/len.rkt" "coerce.rkt" "define.rkt") (define (list-of-lists? xs) (and (list? xs) (andmap list? xs))) (define (index? x) (and (integer? x) (not (negative? x)))) diff --git a/main.rkt b/main.rkt index 84a6761..5739942 100644 --- a/main.rkt +++ b/main.rkt @@ -10,14 +10,8 @@ (r+p "cache.rkt") (r+p "coerce.rkt") -(r+p "container.rkt") (r+p "debug.rkt") (r+p "define.rkt") (r+p "file.rkt") -(r+p "include.rkt") -(r+p "len.rkt") (r+p "list.rkt") -(r+p "misc.rkt") -(r+p "string.rkt") (r+p "test.rkt") -(r+p "xml.rkt") \ No newline at end of file diff --git a/scribblings/sugar.scrbl b/scribblings/sugar.scrbl index 006d15a..ca26bc9 100644 --- a/scribblings/sugar.scrbl +++ b/scribblings/sugar.scrbl @@ -12,7 +12,7 @@ @defmodule[#:multi (sugar (submod sugar safe))] -A collection of small functions to help make Racket code simpler & more readable. +A collection of small functions to help make Racket code simpler & more readable. Well, according to me, anyhow. Sugar can be invoked two ways: as an ordinary library, or as a library with contracts (using the @tt{safe} submodule). @@ -25,22 +25,12 @@ Sugar can be invoked two ways: as an ordinary library, or as a library with cont @include-section["coerce.scrbl"] -@include-section["container.scrbl"] - @include-section["debug.scrbl"] @include-section["file.scrbl"] -@include-section["include.scrbl"] - -@include-section["len.scrbl"] - @include-section["list.scrbl"] -@include-section["string.scrbl"] - -@include-section["xml.scrbl"] - @include-section["license.scrbl"] @;index-section[] diff --git a/scribblings/xml.scrbl b/scribblings/xml.scrbl index f84a7cc..78b77f2 100644 --- a/scribblings/xml.scrbl +++ b/scribblings/xml.scrbl @@ -9,7 +9,7 @@ @defmodule[#:multi (sugar/xml (submod sugar/xml safe))] -Making it easier to do the simplest kind of round-trip with XML: convert an XML string to X-expressions, manipulate, and then convert these X-expressions back to an XML string. @bold{This submodule is untyped only.} +Making it easier to do the simplest kind of round-trip with XML: convert an XML string to X-expressions, manipulate, and then convert these X-expressions back to an XML string. @defproc[ (xml-string->xexprs diff --git a/string.rkt b/string.rkt deleted file mode 100644 index d9bd934..0000000 --- a/string.rkt +++ /dev/null @@ -1,25 +0,0 @@ -#lang racket/base -(require "define.rkt" "coerce.rkt") - - -(define+provide+safe (starts-with? str starter) - (string? string? . -> . coerce/boolean?) - (let ([str (->string str)] - [starter (->string starter)]) - (and (<= (string-length starter) (string-length str)) - (equal? (substring str 0 (string-length starter)) starter)))) - - -(define+provide+safe (ends-with? str ender) - (string? string? . -> . coerce/boolean?) - (let ([str (->string str)] - [ender (->string ender)]) - (and (<= (string-length ender) (string-length str)) - (equal? (substring str (- (string-length str) (string-length ender)) (string-length str)) ender)))) - - -(define+provide+safe (capitalized? str) - (string? . -> . coerce/boolean?) - (let ([str (->string str)]) - (char-upper-case? (car (string->list str))))) - diff --git a/test/main.rkt b/test/main.rkt index 99d3188..fbc4dd7 100644 --- a/test/main.rkt +++ b/test/main.rkt @@ -30,6 +30,7 @@ (check-equal? (->int (make-list 42 null)) 42) (check-equal? (->string "foo") "foo") + (check-equal? (->string #"foo") "foo") (check-equal? (->string '()) "") (check-equal? (->string (void)) "") (check-equal? (->string 'foo) "foo") @@ -40,6 +41,7 @@ (check-equal? (->string #\¶) "¶") (check-equal? (->path "foo") (string->path "foo")) + (check-equal? (->path #"foo") (string->path "foo")) (check-equal? (->path 'foo) (string->path "foo")) (check-equal? (->path 123) (string->path "123")) (check-equal? (->path (string->url "foo/bar.html")) (string->path "foo/bar.html")) @@ -55,30 +57,6 @@ (check-true (->boolean "foo")) (check-true (->boolean '())) (check-true (->boolean '(1 2 3))) - - (check-equal? (len '(1 2 3)) 3) - (check-not-equal? (len '(1 2)) 3) ; len 2 - (check-equal? (len "foo") 3) - (check-not-equal? (len "fo") 3) ; len 2 - (check-equal? (len 'foo) 3) - (check-not-equal? (len 'fo) 3) ; len 2 - (check-equal? (len (list->vector '(1 2 3))) 3) - (check-not-equal? (len (list->vector '(1 2))) 3) ; len 2 - (check-equal? (len (set 1 2 3)) 3) - (check-not-equal? (len (set 1 2)) 3) ; len 2 - (check-equal? (len (make-hash '((a . 1) (b . 2) (c . 3)))) 3) - (check-not-equal? (len (make-hash '((a . 1) (b . 2)))) 3) ; len 2 - - (check-true ("foobar" . starts-with? . "foo")) - (check-true ("foobar" . starts-with? . "f")) - (check-true ("foobar" . starts-with? . "foobar")) - (check-false ("foobar" . starts-with? . "bar")) - (check-false ("foobar" . starts-with? . ".")) - (check-true ("foobar" . ends-with? . "bar")) - (check-true ("foobar" . ends-with? . "r")) - (check-true ("foobar" . ends-with? . "foobar")) - (check-false ("foobar" . ends-with? . "foo")) - (check-true (members-unique? '(a b c))) (check-false (members-unique? '(a b c c))) (check-true (members-unique? "zoey")) @@ -122,17 +100,6 @@ (check-true (has-binary-ext? "foo.MP3")) (check-false (has-binary-ext? "foo.py")) - (check-true (starts-with? "foobar" "foo")) - (check-true (starts-with? "foobar" "foobar")) - (check-false (starts-with? "foobar" "zam")) - (check-false (starts-with? "foobar" "foobars")) - (check-true (ends-with? "foobar" "bar")) - (check-false (ends-with? "foobar" "zam")) - (check-true (ends-with? "foobar" "foobar")) - (check-false (ends-with? "foobar" "foobars")) - (check-true (capitalized? "Brennan")) - (check-false (capitalized? "foobar")) - (check-equal? (slice-at (range 5) 1) '((0) (1) (2) (3) (4))) (check-equal? (slice-at (range 5) 2) '((0 1) (2 3) (4))) (check-equal? (slice-at (range 5) 2 #t) '((0 1) (2 3))) @@ -183,68 +150,10 @@ (check-exn exn:fail? (λ _ (slice-at (range 5) 0))) ; needs a positive integer as second arg (check-exn exn:fail? (λ _ (slicef-at (range 5) 3))) ; needs a procedure as second arg - (check-equal? (get '(0 1 2 3 4 5) 2) 2) - (check-exn exn:fail? (λ() (get '(0 1 2 3 4 5) 100))) ; index too big - (check-equal? (get `(0 1 ,(list 2) 3 4 5) 2) (list 2)) - (check-equal? (get '(0 1 2 3 4 5) 0 2) '(0 1)) - (check-equal? (get (list->vector '(0 1 2 3 4 5)) 2) 2) - (check-equal? (get (list->vector'(0 1 2 3 4 5)) 0 2) (list->vector '(0 1))) - (check-equal? (get "purple" 2) "r") - (check-equal? (get "purple" 0 2) "pu") - (check-equal? (get 'purple 2) 'r) - (check-equal? (get 'purple 0 2) 'pu) - (check-equal? (get (string->path "/root/foo/bar/file.txt") 2) (string->path "foo")) - (check-equal? (get (string->path "/root/foo/bar/file.txt") 0 2) (list (string->path "/") (string->path "root"))) - (check-equal? (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'a) (list 1)) - (check-exn exn:fail? (λ() (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'z))) ; nonexistent key - - (check-equal? (get (string->path "/root/foo/bar/file.txt") 1) (string->path "root")) - (check-equal? (get (string->path "/root/foo/bar/file.txt") 0 3) - (map string->path '("/" "root" "foo"))) - - (check-equal? (get (make-hash '((a . 1) (b . 2) (c . 3))) 'b) 2) - - (check-true (2 . in? . '(1 2 3))) - (check-false (4 . in? . '(1 2 3))) - (check-true (2 . in? . (list->vector '(1 2 3)))) - (check-false (4 . in? . (list->vector '(1 2 3)))) - (check-true ('a . in? . (make-hash '((a . 1) (b . 2) (c . 3))))) - (check-false ('x . in? . (make-hash '((a . 1) (b . 2) (c . 3))))) - (check-true ("o" . in? . "foobar")) - (check-false ("z" . in? . "foobar")) - (check-true ('o . in? . 'foobar)) - (check-false ('z . in? . 'foobar)) - (check-true ("F" . in? . #\F)) - - (check-true (in? "foo" (string->path "/root/foo/bar/file.txt"))) - (check-false (in? "zam" (string->path "/root/foo/bar/file.txt"))) (define ys (range 5)) (check-equal? (values->list (shift/values ys -1 'boing)) '(1 2 3 4 boing)) - (check-equal? (values->list (shift/values ys '(-1 0 1) 'boing)) `((1 2 3 4 boing) ,xs (boing 0 1 2 3))) - - (require xml) - (define str "\nhello world") - (define-values (str-prolog str-doc) (xml-string->xexprs str)) - (check-equal? str-prolog (prolog (list (p-i (location 1 0 1) (location 1 38 39) 'xml "version=\"1.0\" encoding=\"utf-8\"")) #f null)) - (check-equal? str-doc '(root () "hello world")) - (check-equal? (xexprs->xml-string str-prolog str-doc) str) - - (module include-test racket/base - (require sugar/include) - (include-without-lang-line "source.rkt") - (provide included-symbol)) - - (require 'include-test) - (check-equal? included-symbol 'bar) - - (module no-lang-line-include-test racket/base - (require sugar/include) - (include-without-lang-line "no-lang-line-source.txt") - (provide no-lang-symbol)) - - (require 'no-lang-line-include-test) - (check-equal? no-lang-symbol 'bar)) + (check-equal? (values->list (shift/values ys '(-1 0 1) 'boing)) `((1 2 3 4 boing) ,xs (boing 0 1 2 3)))) diff --git a/unstable/container.rkt b/unstable/container.rkt new file mode 100644 index 0000000..d5902d7 --- /dev/null +++ b/unstable/container.rkt @@ -0,0 +1,86 @@ +#lang racket/base +(require "../define.rkt" "../coerce.rkt" "len.rkt" racket/list racket/set racket/sequence racket/stream racket/dict) + +(define (sliceable-container? x) + (ormap (λ(proc) (proc x)) (list list? string? symbol? vector? path? (λ(i) (and (not (dict? i)) (sequence? i)))))) + +(define (gettable-container? x) + (ormap (λ(proc) (proc x)) (list sliceable-container? dict?))) + + +(define+provide+safe (get container start [end #f]) + ((gettable-container? any/c) ((or/c (and/c integer? positive?) #f)) . ->* . any) + + (define result + ;; use handler to capture error & print localized error message + (with-handlers ([exn:fail? (λ(exn) (error (format "get: couldn't retrieve ~a from ~a" (if end (format "items ~a through ~a" start end) (format "item ~a" start)) container)))]) + (let ([end (if (and (equal? end #f) (sliceable-container? container)) (add1 start) end)]) + (cond + [(list? container) (for/list ([i (in-range start end)]) (list-ref container i))] + [(vector? container) (for/vector ([i (in-range start end)]) (vector-ref container i))] + [(string? container) (substring container start end)] + [(symbol? container) (->symbol (get (->string container) start end))] + [(path? container) (get (explode-path container) start end)] + [(dict? container) (dict-ref container start)] + [(sequence? container) (get (->list container) start end)] + [else (error)])))) + + ;; don't return single-item results inside a list + ;; check for integer because integers don't have length + (if (and (not (integer? result)) (= (len result) 1) (sliceable-container? container)) + (car (->list result)) + result)) + +(define (listlike-container? container) + (ormap (λ(pred) (pred container)) (list vector? set? sequence?))) + +(define+provide+safe (in? item container) + (any/c any/c . -> . boolean?) + (->boolean (cond + [(list? container) (member item container)] + [(dict? container) (dict-has-key? container item)] + [(path? container) (in? (->path item) (explode-path container))] + [(stringish? container) (regexp-match (->string item) (->string container))] + ;; location relevant because dicts and strings are also listlike (= sequences) + [(listlike-container? container) (in? item (->list container))] + [else #f]))) + + + +(module+ test + (require rackunit) + (check-equal? (get '(0 1 2 3 4 5) 2) 2) + (check-exn exn:fail? (λ() (get '(0 1 2 3 4 5) 100))) ; index too big + (check-equal? (get `(0 1 ,(list 2) 3 4 5) 2) (list 2)) + (check-equal? (get '(0 1 2 3 4 5) 0 2) '(0 1)) + (check-equal? (get (list->vector '(0 1 2 3 4 5)) 2) 2) + (check-equal? (get (list->vector'(0 1 2 3 4 5)) 0 2) (list->vector '(0 1))) + (check-equal? (get "purple" 2) "r") + (check-equal? (get "purple" 0 2) "pu") + (check-equal? (get 'purple 2) 'r) + (check-equal? (get 'purple 0 2) 'pu) + (check-equal? (get (string->path "/root/foo/bar/file.txt") 2) (string->path "foo")) + (check-equal? (get (string->path "/root/foo/bar/file.txt") 0 2) (list (string->path "/") (string->path "root"))) + (check-equal? (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'a) (list 1)) + (check-exn exn:fail? (λ() (get (make-hash `((a . ,(list 1)) (b . ,(list 2)) (c . ,(list 3)))) 'z))) ; nonexistent key + + (check-equal? (get (string->path "/root/foo/bar/file.txt") 1) (string->path "root")) + (check-equal? (get (string->path "/root/foo/bar/file.txt") 0 3) + (map string->path '("/" "root" "foo"))) + + (check-equal? (get (make-hash '((a . 1) (b . 2) (c . 3))) 'b) 2) + + (check-true (2 . in? . '(1 2 3))) + (check-false (4 . in? . '(1 2 3))) + (check-true (2 . in? . (list->vector '(1 2 3)))) + (check-false (4 . in? . (list->vector '(1 2 3)))) + (check-true ('a . in? . (make-hash '((a . 1) (b . 2) (c . 3))))) + (check-false ('x . in? . (make-hash '((a . 1) (b . 2) (c . 3))))) + (check-true ("o" . in? . "foobar")) + (check-false ("z" . in? . "foobar")) + (check-true ('o . in? . 'foobar)) + (check-false ('z . in? . 'foobar)) + (check-true ("F" . in? . #\F)) + + (check-true (in? "foo" (string->path "/root/foo/bar/file.txt"))) + (check-false (in? "zam" (string->path "/root/foo/bar/file.txt")))) diff --git a/include.rkt b/unstable/include.rkt similarity index 93% rename from include.rkt rename to unstable/include.rkt index 72d2acf..83304ef 100644 --- a/include.rkt +++ b/unstable/include.rkt @@ -4,7 +4,25 @@ racket/private/increader compiler/cm-accomplice racket/match racket/function) - "define.rkt") + "../define.rkt") + +(module+ test + (require rackunit) + (module include-test racket/base + (require sugar/include) + (include-without-lang-line "source.rkt") + (provide included-symbol)) + + (require 'include-test) + (check-equal? included-symbol 'bar) + + (module no-lang-line-include-test racket/base + (require sugar/include) + (include-without-lang-line "no-lang-line-source.txt") + (provide no-lang-symbol)) + + (require 'no-lang-line-include-test) + (check-equal? no-lang-symbol 'bar)) (provide+safe include-without-lang-line) diff --git a/unstable/len.rkt b/unstable/len.rkt new file mode 100644 index 0000000..1d948c4 --- /dev/null +++ b/unstable/len.rkt @@ -0,0 +1,31 @@ +#lang racket/base +(require "../define.rkt" racket/set racket/sequence) + +(define+provide+safe (len x) + ((or/c list? vector? set? sequence? string? symbol? path? hash?) . -> . integer?) + (cond + [(list? x) (length x)] + [(string? x) (string-length x)] + [(symbol? x) (len (symbol->string x))] + [(path? x) (len (path->string x))] + [(vector? x) (vector-length x)] + [(hash? x) (len (hash-keys x))] + [(set? x) (len (set->list x))] + [(and (sequence? x) (not (integer? x))) (len (sequence->list x))] + [else (error "len: can't calculate length of" x)])) + + +(module+ test + (require rackunit) + (check-equal? (len '(1 2 3)) 3) + (check-not-equal? (len '(1 2)) 3) ; len 2 + (check-equal? (len "foo") 3) + (check-not-equal? (len "fo") 3) ; len 2 + (check-equal? (len 'foo) 3) + (check-not-equal? (len 'fo) 3) ; len 2 + (check-equal? (len (list->vector '(1 2 3))) 3) + (check-not-equal? (len (list->vector '(1 2))) 3) ; len 2 + (check-equal? (len (set 1 2 3)) 3) + (check-not-equal? (len (set 1 2)) 3) ; len 2 + (check-equal? (len (make-hash '((a . 1) (b . 2) (c . 3)))) 3) + (check-not-equal? (len (make-hash '((a . 1) (b . 2)))) 3)) ; len 2 \ No newline at end of file diff --git a/misc.rkt b/unstable/misc.rkt similarity index 100% rename from misc.rkt rename to unstable/misc.rkt diff --git a/test/no-lang-line-source.txt b/unstable/no-lang-line-source.txt similarity index 100% rename from test/no-lang-line-source.txt rename to unstable/no-lang-line-source.txt diff --git a/test/source.rkt b/unstable/source.rkt similarity index 100% rename from test/source.rkt rename to unstable/source.rkt diff --git a/unstable/string.rkt b/unstable/string.rkt new file mode 100644 index 0000000..4052764 --- /dev/null +++ b/unstable/string.rkt @@ -0,0 +1,37 @@ +#lang racket/base +(require "../define.rkt" "../coerce.rkt") + + +(define+provide+safe (starts-with? str starter) + (string? string? . -> . coerce/boolean?) + (define pat (regexp (format "^~a" (regexp-quote starter)))) + (and (regexp-match pat (->string str)) #t)) + + +(define+provide+safe (ends-with? str ender) + (string? string? . -> . coerce/boolean?) + (define pat (regexp (format "~a$" (regexp-quote ender)))) + (and (regexp-match pat (->string str)) #t)) + + +(define+provide+safe (capitalized? str-in) + (string? . -> . coerce/boolean?) + (define str (->string str-in)) + (and (positive? (string-length str)) + (char-upper-case? (car (string->list (car (regexp-match "." str))))))) + + +(module+ test + (require rackunit) + (check-true (starts-with? "foobar" "foo")) + (check-true (starts-with? "foobar" "foobar")) + (check-false (starts-with? "foobar" "zam")) + (check-false (starts-with? "foobar" "foobars")) + (check-false (starts-with? "foo" ".")) + (check-true (ends-with? "foobar" "bar")) + (check-false (ends-with? "foobar" "zam")) + (check-true (ends-with? "foobar" "foobar")) + (check-false (ends-with? "foobar" "foobars")) + (check-true (capitalized? "Brennan")) + (check-false (capitalized? "foobar"))) + diff --git a/xml.rkt b/xml.rkt index 0dbd260..4ed4a5f 100644 --- a/xml.rkt +++ b/xml.rkt @@ -9,4 +9,13 @@ (define+provide+safe (xexprs->xml-string prolog-xexpr root-xexpr) (xexpr? xexpr? . -> . string?) - (with-output-to-string (λ _ (write-xml (document (xexpr->xml prolog-xexpr) (xexpr->xml root-xexpr) null))))) \ No newline at end of file + (with-output-to-string (λ _ (write-xml (document (xexpr->xml prolog-xexpr) (xexpr->xml root-xexpr) null))))) + +(module+ test + (require rackunit) + (require xml) + (define str "\nhello world") + (define-values (str-prolog str-doc) (xml-string->xexprs str)) + (check-equal? str-prolog (prolog (list (p-i (location 1 0 1) (location 1 38 39) 'xml "version=\"1.0\" encoding=\"utf-8\"")) #f null)) + (check-equal? str-doc '(root () "hello world")) + (check-equal? (xexprs->xml-string str-prolog str-doc) str)) \ No newline at end of file