Permalink
Browse files

Initial Commit. Tnetstrings all seem to work. Could use more test cas…

…es (as always)
  • Loading branch information...
0 parents commit f3b16744a894c8c4d29321e836e9c2758fa8d046 @alexgartrell committed Apr 13, 2011
Showing with 145 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +20 −0 README
  3. +3 −0 project.clj
  4. +90 −0 src/tnetstrings/core.clj
  5. +27 −0 test/tnetstrings/test/core.clj
@@ -0,0 +1,5 @@
+pom.xml
+*jar
+/lib/
+/classes/
+.lein-deps-sum
20 README
@@ -0,0 +1,20 @@
+# tnetstrings-clj
+### native tnetstrings in clojure
+
+see: http://www.tnetstrings.org
+
+## Usage
+
+ user=> (tnetstrings.core/dumps "Hello, World!")
+ "13:Hello, World!,"
+
+ user=> (tnetstrings.core/loads "13:Hello, World!,")
+ "Hello, World!"
+
+ user=> (tnetstrings.core/dumps (list "Hello" "World" "!!!" 111))
+ "28:5:Hello,5:World,3:!!!,3:111#]"
+
+ user=> (tnetstrings.core/loads "28:5:Hello,5:World,3:!!!,3:111#]")
+ ("Hello" "World" "!!!" 111)
+
+Copyright (C) 2011 Alex Gartrell
@@ -0,0 +1,3 @@
+(defproject tnetstrings "1.0.0-SNAPSHOT"
+ :description "FIXME: write description"
+ :dependencies [[org.clojure/clojure "1.2.1"]])
@@ -0,0 +1,90 @@
+(ns tnetstrings.core
+ (:require [clojure.string :as str]))
+
+(defn- lstrip [s]
+ (.replaceAll s "^\\s*" ""))
+
+(defn- explode-tnetstring [s]
+ (let [[len-str data-plus] (.split (lstrip s) ":" 2)
+ len (Integer. len-str)
+ ; data-plus.length >= len + 1
+ data (.substring data-plus 0 len)
+ type (.charAt data-plus len)
+ rest (.substring data-plus (+ len 1))]
+ [data type rest]))
+
+(defn- parse-list [data] nil)
+(defn- parse-dict [data] nil)
+
+(defn- parse [data type]
+ (let [tis (fn [t] (= type t))] ; Abbreviating (= type t) to (tis t)
+ (cond (tis \,) data ; String
+ (tis \#) (Integer. data) ; Integer
+ (tis \!) (= data "true") ; Boolean
+ (tis \~) nil ; Null
+ (tis \}) (parse-dict data) ; Dictionary
+ (tis \]) (parse-list data) ; List
+ :else :NOTMATCHED)))
+
+(defn- parse-list [data]
+ (loop [data data accum []]
+ (if (str/blank? data)
+ (reverse accum)
+ (let [[data type rest] (explode-tnetstring data)
+ item (parse data type)]
+ (if (= item :NOTMATCHED)
+ :NOTMATCHED
+ (recur rest (cons (parse data type) accum)))))))
+
+(defn- parse-dict [data]
+ (loop [data data accum {}]
+ (if (str/blank? data)
+ accum
+ (let [[kdata ktype val-plus] (explode-tnetstring data)
+ [vdata vtype rest] (explode-tnetstring val-plus)
+ key (parse kdata ktype)
+ val (parse vdata vtype)]
+ (if (or (= key :NOTMATCHED) (= val :NOTMATCHED))
+ :NOTMATCHED
+ (recur rest (assoc accum key val)))))))
+
+
+
+(defn loads [s]
+ (let [[data type rest] (explode-tnetstring s)]
+ (parse data type)))
+
+;prototype
+(defn dump-item [item] nil)
+
+(defn- dump-string [s]
+ (str (.length s) \: s \,))
+
+(defn- dump-integer [x]
+ (let [s (str x)]
+ (str (.length s) \: s \#)))
+
+(defn- dump-list [lst]
+ (loop [lst lst accum ""]
+ (if (empty? lst)
+ (str (.length accum) \: accum \])
+ (recur (rest lst) (str accum (dump-item (first lst)))))))
+
+(defn- dump-map [dict]
+ (loop [ks (keys dict) accum ""]
+ (if (empty? ks)
+ (str (.length accum) \: accum \})
+ (let [k (first ks) v (get dict k)]
+ (recur (rest ks)
+ (str accum (dump-item k) (dump-item v)))))))
+
+(defn- dump-item [item]
+ (cond (nil? item) "0:~"
+ (list? item) (dump-list item)
+ (map? item) (dump-map item)
+ (string? item) (dump-string item)
+ (integer? item) (dump-integer item)
+ :else :NOTMATCHED))
+
+(defn dumps [data]
+ (dump-item data))
@@ -0,0 +1,27 @@
+(ns tnetstrings.test.core
+ (:use tnetstrings.core clojure.test))
+
+(deftest simple-loads
+ (is (= 1337 (loads "4:1337#")))
+ (is (= "1337" (loads "4:1337,")))
+ (is (nil? (loads "0:~")))
+ (is (= (list) (loads "0:]")))
+ (is (= (list 1 2 3 4 5) (loads "20:1:1#1:2#1:3#1:4#1:5#]")))
+ (is (= {} (loads "0:}")))
+ (is (= {"foo" "bar" "biz" "bang"} (loads "25:3:foo,3:bar,3:biz,4:bang,}"))))
+
+(deftest simple-dumps
+ (is (= "4:1337#" (dumps 1337)))
+ (is (= "4:1337," (dumps "1337")))
+ (is (= "0:~" (dumps nil)))
+ (is (= "0:]" (dumps (list))))
+ (is (= "20:1:1#1:2#1:3#1:4#1:5#]" (dumps (list 1 2 3 4 5))))
+ (is (= "0:}" (dumps {})))
+ (is (= "25:3:foo,3:bar,3:biz,4:bang,}" (dumps {"foo" "bar" "biz" "bang"}))))
+
+(defn roundtrip-item [item]
+ (is (= item (loads (dumps item)))))
+
+(deftest roundtrip
+ (roundtrip-item {nil 10 "foo" (list "bar")})
+ (roundtrip-item (list 1 2 nil "" nil "foo" {"bah" "bang"} (list 1 2))))

0 comments on commit f3b1674

Please sign in to comment.