The only way to learn a programming language is to write something in it. So, I decided it was time to dig into Arc and my first project is a very simple Wiki.
Here's the source (wiki.arc):
It loads two helpers. The first contains common utilities that aren't really Wiki-related (util.arc):
And the second contains enhancement to Arc's web/HTML handling (web.arc):
In web.arc there are a couple of bits of syntax to make accessing form/URL arguments easier: ($ "p") (which gets the value of the argument p) and (w/$ p ...) which sets a variable called p to the value of the argument p and then evaluates the rest of the expression.
All this is released under the same license as Arc. (Since I have never programmed in Arc before, and it's been almost 20 years since I stopped coding in LISP or ML, I'd appreciate constructive comments).
Here's the source (wiki.arc):
; A wiki written in Arc (arc0)
;
; Copyright (c) 2008 John Graham-Cumming
;
; (load "wiki.arc")
; (wsv)
;
; Then go to http://localhost:8080/show
(load "web.arc")
(load "util.arc")
(= pagedir* "wiki/")
(def histfiles (page)
(sort > (map [coerce _ 'int] (rem [is "current" _] (dir (pagepath page))))))
(def nexthist (page)
(let h (histfiles page)
(if h (++ (car h)) 0)))
(def pagepath (page)
(string pagedir* (page 0) "/" (page 0) (page 1) "/" page ))
(def pagefile (page (o file))
(string (pagepath page) "/" (or file "current")))
(def slurp (page (o file))
(if
(let p (pagefile page file)
(if (file-exists p) (readfile p)))))
(def upperlen (word)
(len (keep upper word)))
(def is-wikilink (word)
(if (alphas word)
(if (~is (word 0) (downcase (word 0)))
(>= (upperlen word) 2))))
(mac url-show (page)
`(string "show?p=" ,page))
(mac url-edit (page)
`(string "edit?p=" ,page))
(mac link-show (page text)
`(link ,text (url-show ,page)))
(mac link-edit (page text)
`(link ,text (url-edit ,page)))
(def wikify (word)
(if (is-wikilink word)
(if (file-exists (pagefile word))
(link-show word word)
(pr word)(link-edit word "?"))
(pr word))
(ws))
(mac spew-raw (page)
`(spew ,page [pr _ " "]))
(mac spew-wiki (page (o file))
`(spew ,page [wikify (string _)] ,file))
(def spew (page f (o file))
(let p (pagepath page)
(if (dir-exists p)
(map f (flat (map tokens (slurp page file))))
(pr "This page does not yet exist."))))
(def squash (file body)
(writefile1 body file))
(def save-page (req)
(w/$ p
(w/$ t
(squash (pagefile p) t)
(squash (string (pagepath p) "/" (nexthist p)) t))
(url-show p)))
(mac mtime (f)
`(datetime (file-mtime ,f)))
(def last-modified (page)
(let f (pagefile page)
(if (file-exists f)
(pr "Last modified: " (mtime f)))))
(mac show-page (page)
`(whitepage
(tag h1 (link-show ,page ,page))
(spew-wiki ,page)
(br 2)
(hr)
(last-modified ,page)
(br)
(link-edit ,page "[edit]")
(ws)
(link "[history]" (string "history?p=" ,page))))
(mac edit-page (page)
`(whitepage
(tag h1 (pr (string "Editing " ,page)))
(arform save-page
(textarea "t" 25 80 (spew-raw ,page))
(hidden "p" ,page)
(br)
(submit "Save"))
(link-show ,page "[cancel]")
(br 2)))
(def revision (page rev)
(tag li
(pr "Revision: " )
(link rev (string "revision?p=" page "&r=" rev))
(pr " modified " (mtime (string (pagepath page) "/" rev)))))
(mac history-page (page)
`(whitepage
(tag h1 (pr (string "History of " ,page)))
(tag ul (map [revision ,page _] (histfiles ,page)))
(hr)
(link-show ,page (string "Back to " ,page))))
(mac revision-page (page rev)
`(whitepage
(tag h1 (pr "Showing revision " ,rev " of " ,page))
(spew-wiki ,page ,rev)
(br 2)
(hr)
(last-modified ,page)
(br)
(link-show ,page (string "Back to " ,page))))
(defop show req
(w/$ p
(if p
(show-page ($ "p"))
(show-page "HomePage"))))
(defop edit req
(w/$ p
(ensure-dir (pagepath p))
(edit-page p)))
(defop history req
(history-page ($ "p")))
(defop revision req
(revision-page ($ "p") ($ "r")))
(def wsv ()
(ensure-dir pagedir*)
(asv))
It loads two helpers. The first contains common utilities that aren't really Wiki-related (util.arc):
(def alpha (c)
(or (<= #\a c #\z) (<= #\A c #\Z)))
(def alphas (str)
(is (keep alpha str) str))
(def upper (c)
(is (upcase c) c))
(def datetime ((o time (seconds)))
(let val (tostring
(system (string "date -u -r " time " \"+%Y-%m-%d %H:%M\"")))
(subseq val 0 (- (len val) 1))))
And the second contains enhancement to Arc's web/HTML handling (web.arc):
(mac hidden (name val)
`(gentag input type 'hidden name ,name value ,val))
(mac hr ()
`(gentag hr))
(mac ws ()
`(pr " "))
(mac $ (r)
`(arg req ,r))
(mac w/$ (r . body)
`(with (,r ($ (string ',r))) ,@body))
In web.arc there are a couple of bits of syntax to make accessing form/URL arguments easier: ($ "p") (which gets the value of the argument p) and (w/$ p ...) which sets a variable called p to the value of the argument p and then evaluates the rest of the expression.
All this is released under the same license as Arc. (Since I have never programmed in Arc before, and it's been almost 20 years since I stopped coding in LISP or ML, I'd appreciate constructive comments).
Comments