diff options
author | cormullion <cormullion@mac.com> | 2012-02-05 16:59:53 +0000 |
---|---|---|
committer | cormullion <cormullion@mac.com> | 2012-02-05 16:59:53 +0000 |
commit | 0af9aeea0bff0b5af3a8801140e641ac38b01ced (patch) | |
tree | d6d7653ebecd3c2f32d154639b20f7522c061a3c | |
parent | 8c9b997e0c1a8ceaf692b7f48107936fcc0baef9 (diff) | |
download | pygments-0af9aeea0bff0b5af3a8801140e641ac38b01ced.tar.gz |
added test files *.lsp
-rwxr-xr-x | tests/examplefiles/irc.lsp | 214 | ||||
-rwxr-xr-x | tests/examplefiles/markdown.lsp | 679 | ||||
-rw-r--r-- | tests/examplefiles/newlisp-parser.lsp | 298 | ||||
-rw-r--r-- | tests/examplefiles/reversi.lsp | 427 |
4 files changed, 1618 insertions, 0 deletions
diff --git a/tests/examplefiles/irc.lsp b/tests/examplefiles/irc.lsp new file mode 100755 index 00000000..6f45976a --- /dev/null +++ b/tests/examplefiles/irc.lsp @@ -0,0 +1,214 @@ +#!/usr/bin/env newlisp + +;; @module IRC +;; @description a basic irc library +;; @version early alpha! 0.1 2011-10-31 14:21:26 +;; @author cormullion +;; Usage: +;; (IRC:init "newlithper") ; a username/nick (not that one obviously :-) +;; (IRC:connect "irc.freenode.net" 6667) ; irc/server +;; (IRC:join-channel {#newlisp}) ; join a room +;; either (IRC:read-irc-loop) ; loop - monitor only, no input +;; or (IRC:session) ; a command-line session, end with /QUIT + +(context 'IRC) + (define Inickname) + (define Ichannels) + (define Iserver) + (define Iconnected) + (define Icallbacks '()) + (define Idle-time 400) ; seconds + (define Itime-stamp) ; time since last message was processed + +(define (register-callback callback-name callback-function) + (println {registering callback for } callback-name { : } (sym (term callback-function) (prefix callback-function))) + (push (list callback-name (sym (term callback-function) (prefix callback-function))) Icallbacks)) + +(define (do-callback callback-name data) + (when (set 'func (lookup callback-name Icallbacks)) ; find first callback + (if-not (catch (apply func (list data)) 'error) + (println {error in callback } callback-name {: } error)))) + +(define (do-callbacks callback-name data) + (dolist (rf (ref-all callback-name Icallbacks)) + (set 'callback-entry (Icallbacks (first rf))) + (when (set 'func (last callback-entry)) + (if-not (catch (apply func (list data)) 'error) + (println {error in callback } callback-name {: } error))))) + +(define (init str) + (set 'Inickname str) + (set 'Iconnected nil) + (set 'Ichannels '()) + (set 'Itime-stamp (time-of-day))) + +(define (connect server port) + (set 'Iserver (net-connect server port)) + (net-send Iserver (format "USER %s %s %s :%s\r\n" Inickname Inickname Inickname Inickname)) + (net-send Iserver (format "NICK %s \r\n" Inickname)) + (set 'Iconnected true) + (do-callbacks "connect" (list (list "server" server) (list "port" port)))) + +(define (identify password) + (net-send Iserver (format "PRIVMSG nickserv :identify %s\r\n" password))) + +(define (join-channel channel) + (when (net-send Iserver (format "JOIN %s \r\n" channel)) + (push channel Ichannels) + (do-callbacks "join-channel" (list (list "channel" channel) (list "nickname" Inickname))))) + +(define (part chan) + (if-not (empty? chan) + ; leave specified + (begin + (net-send Iserver (format "PART %s\r\n" chan)) + (replace channel Ichannels) + (do-callbacks "part" (list (list "channel" channel)))) + ; leave all + (begin + (dolist (channel Ichannels) + (net-send Iserver (format "PART %s\r\n" channel)) + (replace channel Ichannels) + (do-callbacks "part" (list (list "channel" channel))))))) + +(define (do-quit message) + (do-callbacks "quit" '()) ; chance to do stuff before quit... + (net-send Iserver (format "QUIT :%s\r\n" message)) + (sleep 1000) + (set 'Ichannels '()) + (close Iserver) + (set 'Iconnected nil)) + +(define (privmsg user message) + (net-send Iserver (format "PRIVMSG %s :%s\r\n" user message))) + +(define (notice user message) + (net-send Iserver (format "NOTICE %s :%s\r\n" user message))) + +(define (send-to-server message (channel nil)) + (cond + ((starts-with message {/}) ; default command character + (set 'the-message (replace "^/" (copy message) {} 0)) ; keep original + (net-send Iserver (format "%s \r\n" the-message)) ; send it + ; do a quit + (if (starts-with (lower-case the-message) "quit") + (do-quit { enough}))) + (true + (if (nil? channel) + ; say to all channels + (dolist (c Ichannels) + (net-send Iserver (format "PRIVMSG %s :%s\r\n" c message))) + ; say to specified channel + (if (find channel Ichannels) + (net-send Iserver (format "PRIVMSG %s :%s\r\n" channel message)))))) + (do-callbacks "send-to-server" (list (list "channel" channel) (list "message" message)))) + +(define (process-command sender command text) + (cond + ((= sender "PING") + (net-send Iserver (format "PONG %s\r\n" command))) + ((or (= command "NOTICE") (= command "PRIVMSG")) + (process-message sender command text)) + ((= command "JOIN") + (set 'username (first (clean empty? (parse sender {!|:} 0)))) + (set 'channel (last (clean empty? (parse sender {!|:} 0)))) + (println {username } username { joined } channel) + (do-callbacks "join" (list (list "channel" channel) (list "username" username)))) + (true + nil))) + +(define (process-message sender command text) + (let ((username {} target {} message {})) + (set 'username (first (clean empty? (parse sender {!|:} 0)))) + (set 'target (trim (first (clean empty? (parse text {!|:} 0))))) + (set 'message (slice text (+ (find {:} text) 1))) + (cond + ((starts-with message "\001") + (process-ctcp username target message)) + ((find target Ichannels) + (cond + ((= command {PRIVMSG}) + (do-callbacks "channel-message" (list (list "channel" target) (list "username" username) (list "message" message)))) + ((= command {NOTICE}) + (do-callbacks "channel-notice" (list (list "channel" target) (list "username" username) (list "message" message)))))) + ((= target Inickname) + (cond + ((= command {PRIVMSG}) + (do-callbacks "private-message" (list (list "username" username) (list "message" message)))) + ((= command {NOTICE}) + (do-callbacks "private-notice" (list (list "username" username) (list "message" message)))))) + (true + nil)))) + +(define (process-ctcp username target message) + (cond + ((starts-with message "\001VERSION\001") + (net-send Iserver (format "NOTICE %s :\001VERSION %s\001\r\n" username version))) + ((starts-with message "\001PING") + (set 'data (first (rest (clean empty? (parse message { } 0))))) + (set 'data (trim data "\001" "\001")) + (net-send Iserver (format "NOTICE %s :\001PING %s\001\r\n" username data))) + ((starts-with message "\001ACTION") + (set 'data (first (rest (clean empty? (parse message { } 0))))) + (set 'data (join data { })) + (set 'data (trim data "\001" "\001")) + (if (find target Ichannels) + (do-callbacks "channel-action" (list (list "username" username) (list "message" message)))) + (if (= target Inickname) + (do-callbacks "private-action" (list (list "username" username) (list "message" message))))) + ((starts-with message "\001TIME\001") + (net-send Iserver (format "NOTICE %s:\001TIME :%s\001\r\n" username (date)))))) + +(define (parse-buffer raw-buffer) + (let ((messages (clean empty? (parse raw-buffer "\r\n" 0))) + (sender {} command {} text {})) + ; check for elapsed time since last activity + (when (> (sub (time-of-day) Itime-stamp) (mul Idle-time 1000)) + (do-callbacks "idle-event") + (set 'Itime-stamp (time-of-day))) + (dolist (message messages) + (set 'message-parts (parse message { })) + (unless (empty? message-parts) + (set 'sender (first message-parts)) + (catch (set 'command (first (rest message-parts))) 'error) + (catch (set 'text (join (rest (rest message-parts)) { })) 'error)) + (process-command sender command text)))) + +(define (read-irc) + (let ((buffer {})) + (when (!= (net-peek Iserver) 0) + (net-receive Iserver buffer 8192 "\n") + (unless (empty? buffer) + (parse-buffer buffer))))) + +(define (read-irc-loop) ; monitoring + (let ((buffer {})) + (while Iconnected + (read-irc) + (sleep 1000)))) + +(define (print-raw-message data) ; example of using a callback + (set 'raw-data (lookup "message" data)) + (set 'channel (lookup "channel" data)) + (set 'message-text raw-data) + (println (date (date-value) 0 {%H:%M:%S }) username {> } message-text)) + +(define (print-outgoing-message data) + (set 'raw-data (lookup "message" data)) + (set 'channel (lookup "channel" data)) + (set 'message-text raw-data) + (println (date (date-value) 0 {%H:%M:%S }) Inickname {> } message-text)) + +(define (session); interactive terminal + ; must add callbacks to display messages + (register-callback "channel-message" 'print-raw-message) + (register-callback "send-to-server" 'print-outgoing-message) + (while Iconnected + (while (zero? (peek 0)) + (read-irc)) + (send-to-server (string (read-line 0)))) + (println {finished session } (date)) + (exit)) + +; end of IRC code + diff --git a/tests/examplefiles/markdown.lsp b/tests/examplefiles/markdown.lsp new file mode 100755 index 00000000..8159082b --- /dev/null +++ b/tests/examplefiles/markdown.lsp @@ -0,0 +1,679 @@ +#!/usr/bin/env newlisp + +;; @module markdown +;; @author cormullion +;; @description a port of John Gruber's Markdown to newLISP +;; @location http://unbalanced-parentheses.nfshost.com/ +;; @version of date 2011-10-02 22:36:02 +;; version history: at the end +;; a port of John Gruber's Markdown.pl (http://daringfireball.net/markdown) script to newLISP... +;; see his original Perl script for explanations of the fearsome regexen and +;; byzantine logic, etc... +;; TODO: +;; the following Markdown tests fail: +;; Inline HTML (Advanced) ... FAILED +;; Links, reference style ... FAILED -- nested brackets +;; Links, shortcut references ... FAILED +;; Markdown Documentation - Syntax ... FAILED +;; Ordered and unordered lists ... FAILED -- a nested ordered list error +;; parens in url : ![this is a stupid URL](http://example.com/(parens).jpg) see (Images.text) +;; Add: email address scrambling + +(context 'Hash) +(define HashTable:HashTable) + +(define (build-escape-table) + (set '*escape-chars* [text]\`*_{}[]()>#+-.![/text]) + (dolist (c (explode *escape-chars*)) + (HashTable c (hash c)))) + +(define (init-hash txt) + ; finds a hash identifier that doesn't occur anywhere in the text + (set 'counter 0) + (set 'hash-prefix "HASH") + (set 'hash-id (string hash-prefix counter)) + (do-while (find hash-id txt) + (set 'hash-id (string hash-prefix (inc counter)))) + (Hash:build-escape-table)) + +(define (hash s) + (HashTable s (string hash-id (inc counter)))) + +(context 'markdown) + +(define (markdown:markdown txt) + (initialize) + (Hash:init-hash txt) + (unescape-special-chars + (block-transforms + (strip-link-definitions + (protect + (cleanup txt)))))) + +(define (initialize) + (set '*escape-pairs* '( + ({\\\\} {\}) + ({\\`} {`}) + ({\\\*} {*}) + ({\\_} {_}) + ([text]\\\{[/text] [text]{[/text]) + ([text]\\\}[/text] [text]}[/text]) + ({\\\[} {[}) + ({\\\]} {]}) + ({\\\(} {(}) + ({\\\)} {)}) + ({\\>} {>}) + ({\\\#} {#}) + ({\\\+} {+}) + ({\\\-} {-}) + ({\\\.} {.}) + ({\\!} {!}))) + (set '*hashed-html-blocks* '()) + (set '*list-level* 0)) + +(define (block-transforms txt) + (form-paragraphs + (protect + (block-quotes + (code-blocks + (lists + (horizontal-rules + (headers txt)))))))) + +(define (span-transforms txt) + (line-breaks + (emphasis + (amps-and-angles + (auto-links + (anchors + (images + (escape-special-chars + (escape-special-chars (code-spans txt) 'inside-attributes))))))))) + +(define (tokenize-html xhtml) +; return list of tag/text portions of xhtml text + (letn ( + (tag-match [text]((?s:<!(-- .*? -- \s*)+>)| +(?s:<\?.*?\?>)| +(?:<[a-z/!$](?:[^<>]| +(?:<[a-z/!$](?:[^<>]| +(?:<[a-z/!$](?:[^<>]| +(?:<[a-z/!$](?:[^<>]| +(?:<[a-z/!$](?:[^<>]| +(?:<[a-z/!$](?:[^<>])*>))*>))*>))*>))*>))*>))[/text]) ; yeah, well... + (str xhtml) + (len (length str)) + (pos 0) + (tokens '())) + (while (set 'tag-start (find tag-match str 8)) + (if (< pos tag-start) + (push (list 'text (slice str pos (- tag-start pos))) tokens -1)) + (push (list 'tag $0) tokens -1) + (set 'str (slice str (+ tag-start (length $0)))) + (set 'pos 0)) + ; leftovers + (if (< pos len) + (push (list 'text (slice str pos (- len pos))) tokens -1)) + tokens)) + +(define (escape-special-chars txt (within-tag-attributes nil)) + (let ((temp (tokenize-html txt)) + (new-text {})) + (dolist (pair temp) + (if (= (first pair) 'tag) + ; 'tag + (begin + (set 'new-text (replace {\\} (last pair) (HashTable {\\}) 0)) + (replace [text](?<=.)</?code>(?=.)[/text] new-text (HashTable {`}) 0) + (replace {\*} new-text (HashTable {*}) 0) + (replace {_} new-text (HashTable {_} ) 0)) + ; 'text + (if within-tag-attributes + (set 'new-text (last pair)) + (set 'new-text (encode-backslash-escapes (last pair))))) + (setf (temp $idx) (list (first pair) new-text))) + ; return as text + (join (map last temp)))) + +(define (encode-backslash-escapes t) + (dolist (pair *escape-pairs*) + (replace (first pair) t (HashTable (last pair)) 14))) + +(define (encode-code s) + ; encode/escape certain characters inside Markdown code runs + (replace {&} s "&" 0) + (replace {<} s "<" 0) + (replace {>} s ">" 0) + (replace {\*} s (HashTable {\\}) 0) + (replace {_} s (HashTable {_}) 0) + (replace "{" s (HashTable "{") 0) + (replace {\[} s (HashTable {[}) 0) + (replace {\]} s (HashTable {]}) 0) + (replace {\\} s (HashTable "\\") 0)) + +(define (code-spans s) + (replace + {(?<!\\)(`+)(.+?)(?<!`)\1(?!`)} + s + (string {<code>} (encode-code (trim $2)) {</code>}) + 2)) + +(define (encode-alt s) + (replace {&} s "&" 0) + (replace {"} s """ 0)) + +(define (images txt) + (let ((alt-text {}) + (url {}) + (title {}) + (ref-regex {(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])}) + (inline-regex {(!\[(.*?)\]\([ \t]*<?(\S+?)>?[ \t]*((['"])(.*?)\5[ \t]*)?\))}) + (whole-match {}) + (result {}) + (id-ref {}) + (url {})) + ; reference links ![alt text][id] + (replace + ref-regex + txt + (begin + (set 'whole-match $1 'alt-text $2 'id-ref $3) + (if alt-text + (replace {"} alt-text {"} 0)) + (if (empty? id-ref) + (set 'id-ref (lower-case alt-text))) + (if (lookup id-ref *link-database*) + (set 'url (first (lookup id-ref *link-database*))) + (set 'url nil)) + (if url + (begin + (replace {\*} url (HashTable {*}) 0) + (replace {_} url (HashTable {_}) 0) + )) + (if (last (lookup id-ref *link-database*)) + ; title + (begin + (set 'title (last (lookup id-ref *link-database*))) + (replace {"} title {"} 0) + (replace {\*} title (HashTable {*}) 0) + (replace {_} title (HashTable {_}) 0)) + ; no title + (set 'title {}) + ) + (if url + (set 'result (string + {<img src="} + (trim url) + {" alt="} + alt-text {" } + (if (not (empty? title)) + (string { title="} title {"}) {}) + { />})) + (set 'result whole-match)) + ) + 0 + ) + ; inline image refs: ![alt text](url "optional title") + (replace + inline-regex + txt + (begin + (set 'whole-match $1) + (set 'alt-text $2) + (set 'url $3) + (set 'title $6) + (if alt-text + (replace {"} alt-text {"} 0) + (set 'alt-text {})) + (if title + (begin + (replace {"} title {"} 0) + (replace {\*} title (HashTable {*}) 0) + (replace {_} title (HashTable {_}) 0)) + (set 'title {})) + (replace {\*} url (HashTable {*}) 0) + (replace {_} url (HashTable {_}) 0) + (string + {<img src="} + (trim url) + {" alt="} + alt-text {" } + (if title (string {title="} title {"}) {}) { />}) + ) + 0 + ) + ; empty ones are possible + (set '$1 {}) + (replace {!\[(.*?)\]\([ \t]*\)} + txt + (string {<img src="" alt="} $1 {" title="" />}) + 0))) + +(define (make-anchor link-text id-ref ) +; Link defs are in the form: ^[id]: url "optional title" +; stored in link db list as (id (url title)) +; params are text to be linked and the id of the link in the db +; eg bar 1 for [bar][1] + + (let ((title {}) + (id id-ref) + (url nil)) + (if link-text + (begin + (replace {"} link-text {"} 0) + (replace {\n} link-text { } 0) + (replace {[ ]?\n} link-text { } 0))) + (if (null? id ) (set 'id (lower-case link-text))) + (if (not (nil? (lookup id *link-database*))) + (begin + (set 'url (first (lookup id *link-database*))) + (replace {\*} url (HashTable {*}) 0) + (replace {_} url (HashTable {_}) 0) + (if (set 'title (last (lookup id *link-database*))) + (begin + (replace {"} title {"} 0) + (replace {\*} title (HashTable {*}) 0) + (replace {_} title (HashTable {_}) 0)) + (set 'title {}))) + (set 'url nil)) + (if url + (string {<a href="} (trim url) + {"} + (if (not (= title {})) (string { title="} (trim title) {"}) {}) + {>} link-text {</a>}) + (string {[} link-text {][} id-ref {]})))) + +(define (anchors txt) + (letn ((nested-brackets {(?>[^\[\]]+)*}) + (ref-link-regex (string {(\[(} nested-brackets {)\][ ]?(?:\n[ ]*)?\[(.*?)\])})) + (inline-regex {(\[(.*?)\]\([ ]*<?(.*?\)?)>?[ ]*((['"])(.*?)\5[ \t]*)?\))}) + (link-text {}) + (url {}) + (title {})) + ; reference-style links: [link text] [id] + (set '$1 {} '$2 {} '$3 {} '$4 {} '$5 {} '$6 {}) ; i still don't think I should have to do this... + + ; what about this regex instead? + (set 'ref-link-regex {(\[(.*?)\][ ]?\[(.*?)\])}) + + (replace ref-link-regex txt (make-anchor $2 $3) 8) ; $2 is link text, $3 is id + ; inline links: [link text](url "optional title") + (set '$1 {} '$2 {} '$3 {} '$4 {} '$5 {} '$6 {}) + (replace + inline-regex + txt + (begin + (set 'link-text $2) + (set 'url $3) + (set 'title $6) + (if link-text (replace {"} link-text {"} 0)) + (if title + (begin + (replace {"} title {"} 0) + (replace {\*} title (HashTable {*}) 0) + (replace {_} title (HashTable {_}) 0)) + (set 'title {})) + (replace {\*} url (HashTable {*}) 0) + (replace {_} url (HashTable {_}) 0) + (replace {^<(.*)>$} url $1 0) + (string + {<a href="} + (trim url) + {"} + (if (not (= title {})) + (string { title="} (trim title) {"}) + {}) + {>} link-text {</a>} + )) + 8 + ) ; replace + ) txt) + +(define (auto-links txt) + (replace + [text]<((https?|ftp):[^'">\s]+)>[/text] + txt + (string {<a href="} $1 {">} $1 {</a>}) + 0 + ) + ; to-do: email ... +) + +(define (amps-and-angles txt) +; Smart processing for ampersands and angle brackets + (replace + [text]&(?!\#?[xX]?(?:[0-9a-fA-F]+|\w+);)[/text] + txt + {&} + 10 + ) + (replace + [text]<(?![a-z/?\$!])[/text] + txt + {<} + 10)) + +(define (emphasis txt) + ; italics/bold: strong first + (replace + [text] (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 [/text] + txt + (string {<strong>} $2 {</strong>}) + 8 + ) + (replace + [text] (\*|_) (?=\S) (.+?) (?<=\S) \1 [/text] + txt + (string {<em>} $2 {</em>}) + 8 + )) + +(define (line-breaks txt) + ; handles line break markers + (replace " {2,}\n" txt " <br/>\n" 0)) + +(define (hex-str-to-unicode-char strng) + ; given a five character string, assume it's "U" + 4 hex chars and convert + ; return the character... + (char (int (string "0x" (1 strng)) 0 16))) + +(define (ustring s) + ; any four digit string preceded by U + (replace "U[0-9a-f]{4,}" s (hex-str-to-unicode-char $0) 0)) + +(define (cleanup txt) + ; cleanup the text by normalizing some possible variations + (replace "\r\n|\r" txt "\n" 0) ; standardize line ends + (push "\n\n" txt -1) ; end with two returns + (set 'txt (detab txt)) ; convert tabs to spaces + + ; convert inline Unicode: + (set 'txt (ustring txt)) + (replace "\n[ \t]+\n" txt "\n\n" 0) ; lines with only spaces and tabs + ) + +(define (protect txt) + ; protect or "hash html blocks" + (letn ((nested-block-regex [text](^<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b(.*\n)*?</\2>[ \t]*(?=\n+|\Z))[/text]) + (liberal-tag-regex [text](^<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b(.*\n)*?.*</\2>[ \t]*(?=\n+|\Z))[/text]) + (hr-regex [text](?:(?<=\n\n)|\A\n?)([ ]{0,3}<(hr)\b([^<>])*?/?>[ \t]*(?=\n{2,}|\Z))[/text]) + (html-comment-regex [text](?:(?<=\n\n)|\A\n?)([ ]{0,3}(?s:<!(--.*?--\s*)+>)[ \t]*(?=\n{2,}|\Z))[/text]) + (results '()) + (chunk-count (length (set 'chunks (parse txt "\n\n")))) + (chunk-size 500)) + + ; due to a limitation in PCRE, long sections have to be divided up otherwise we'll crash + ; so divide up long texts into chunks, then do the regex on each chunk + ; not an ideal solution, but it works ok :( + + (for (i 0 chunk-count chunk-size) + ; do a chunk + (set 'text-chunk (join (i (- (min chunk-count (- (+ i chunk-size) 1)) i) chunks) "\n\n")) + (dolist (rgx (list nested-block-regex liberal-tag-regex hr-regex html-comment-regex)) + (replace + rgx + text-chunk + (begin + (set 'key (Hash:hash $1)) + (push (list key $1 ) *hashed-html-blocks* -1) + (string "\n\n" key "\n\n")) + 2)) + ; save this partial result + (push text-chunk results -1) + ) ; for + ; return string result + (join results "\n\n"))) + +(define (unescape-special-chars t) + ; Swap back in all the special characters we've hidden. + (dolist (pair (HashTable)) + (replace (last pair) t (first pair) 10)) t) + +(define (strip-link-definitions txt) + ; strip link definitions from the text and store them + ; Link defs are in the form: ^[id]: url "optional title" + ; stored in link db list as (id (url title)) + (let ((link-db '()) + (url {}) + (id {}) + (title {})) + (replace + [text]^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(?<=\s)["(](.+?)[")][ \t]*)?(?:\n+|\Z)[/text] + txt + (begin + (set 'id (lower-case $1) 'url (amps-and-angles $2) 'title $3) + (if title (replace {"} title {"} 0)) + (push (list id (list url title)) link-db) + (set '$3 {}) ; necessary? + (string {}) ; remove from text + ) + 10) + (set '*link-database* link-db) + txt)) + +(define (horizontal-rules txt) + (replace + [text]^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$[/text] + txt + "\n<hr />" + 14) + (replace + [text]^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$[/text] + txt + "\n<hr />" + 14) + (replace + [text]^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$[/text] + txt + "\n<hr />" + 14)) + +(define (headers txt) + ; setext headers + (let ((level 1)) + (replace + [text]^(.+)[ \t]*\n=+[ \t]*\n+[/text] + txt + (string "<h1>" (span-transforms $1) "</h1>\n\n") + 2) + + (replace + [text]^(.+)[ \t]*\n-+[ \t]*\n+[/text] + txt + (string "<h2>" (span-transforms $1) "</h2>\n\n") + 2) + ; atx headers + (replace + [text]^(\#{1,6})\s*(.+?)[ ]*\#*(\n+)[/text] + txt + (begin + (set 'level (length $1)) + (string "<h" level ">" (span-transforms $2) "</h" level ">\n\n") + ) + 2))) + +(define (lists txt) + (letn ((marker-ul {[*+-]}) + (marker-ol {\d+[.]}) + (marker-any (string {(?:} marker-ul {|} marker-ol {)})) + (whole-list-regex (string [text](([ ]{0,3}([/text] marker-any [text])[ \t]+)(?s:.+?)(\z|\n{2,}(?=\S)(?![ \t]*[/text] marker-any [text][ \t]+)))[/text])) + (my-list {}) + (list-type {}) + (my-result {})) + (replace + (if (> *list-level* 0) + (string {^} whole-list-regex) + (string {(?:(?<=\n\n)|\A\n?)} whole-list-regex)) + txt + (begin + (set 'my-list $1) + (if (find $3 marker-ul) + (set 'list-type "ul" 'marker-type marker-ul) + (set 'list-type "ol" 'marker-type marker-ol)) + (replace [text]\n{2,}[/text] my-list "\n\n\n" 0) + (set 'my-result (process-list-items my-list marker-any)) + (replace {\s+$} my-result {} 0) + (string {<} list-type {>} "\n" my-result "\n" {</} list-type {>} "\n")) + 10 ; must be multiline + ))) + +(define (process-list-items list-text marker-any) + (let ((list-regex (string [text](\n)?(^[ \t]*)([/text] marker-any [text])[ \t]+((?s:.+?)(\n{1,2}))(?=\n*(\z|\2([/text] marker-any [text])[ \t]+))[/text])) + (item {}) + (leading-line {}) + (leading-space {}) + (result {})) + (inc *list-level*) + (replace [text]\n{2,}\z[/text] list-text "\n" 0) + (set '$1 {} '$2 {} '$3 {} '$4 {} '$5 {}) + (replace + list-regex + list-text + (begin + (set 'item $4) + (set 'leading-line $1) + (set 'leading-space $2) + (if (or (not (empty? leading-line)) (ends-with item "\n{2,}" 0)) + (set 'item (block-transforms (outdent item))) + ; recurse for sub lists + (begin + (set 'item (lists (outdent item))) + (set 'item (span-transforms (trim item "\n"))) + )) + (string {<li>} item {</li>} "\n")) + 10) + (dec *list-level*) + list-text)) + +(define (code-blocks txt) + (let ((code-block {}) + (token-list '())) + (replace + [text](?:\n\n|\A)((?:(?:[ ]{4}|\t).*\n+)+)((?=^[ ]{0,3}\S)|\Z)[/text] + txt + (begin + (set 'code-block $1) + ; format if Nestor module is loaded and it's not marked as plain + (if (and (not (starts-with code-block " ;plain\n")) (context? Nestor)) + ; format newlisp + (begin + ; remove flag if present + (replace "[ ]{4};newlisp\n" code-block {} 0) + (set 'code-block (protect (Nestor:nlx-to-html (Nestor:my-read (trim (detab (outdent code-block)) "\n"))))) + code-block) + ; don't format + (begin + ; trim leading and trailing newlines + (replace "[ ]{4};plain\n" code-block {} 0) + (set 'code-block (trim (detab (encode-code (outdent code-block))) "\n")) + (set '$1 {}) + (set 'code-block (string "\n\n<pre><code>" code-block "\n</code></pre>\n\n"))))) + 10))) + +(define (block-quotes txt) + (let ((block-quote {})) + (replace + [text]((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)[/text] + txt + (begin + (set 'block-quote $1) + (replace {^[ ]*>[ ]?} block-quote {} 2) + (replace {^[ ]+$} block-quote {} 2) + (set 'block-quote (block-transforms block-quote)) ; recurse + ; remove leading spaces + (replace + {(\s*<pre>.+?</pre>)} + block-quote + (trim $1) + 2) + (string "<blockquote>\n" block-quote "\n</blockquote>\n\n")) + 2))) + +(define (outdent s) + (replace [text]^(\t|[ ]{1,4})[/text] s {} 2)) + +(define (detab s) + (replace [text](.*?)\t[/text] + s + (string $1 (dup { } (- 4 (% (length $1) 4)))) + 2)) + +(define (form-paragraphs txt) + (let ((grafs '()) + (original nil)) + (set 'txt (trim txt "\n")) ; strip blank lines before and after + (set 'grafs (parse txt "\n{2,}" 0)) ; split + (dolist (p grafs) + (if (set 'original (lookup p *hashed-html-blocks*)) + ; html blocks + (setf (grafs $idx) original) + ; wrap <p> tags round everything else + (setf (grafs $idx) (string {<p>} (replace {^[ ]*} (span-transforms p) {} (+ 4 8 16)) {</p>})))) + (join grafs "\n\n"))) + +[text] +; three command line arguments: let's hope last one is a file +(when (= 3 (length (main-args))) + (println (markdown (read-file (main-args 2)))) + (exit)) + +; hack for command-line and module loading +(set 'level (sys-info 3)) + +; if level is 2, then we're probably invoking markdown.lsp directly +; if level is > 3, then we're probably loading it into another script... + +(when (= level 2) + ; running on command line, read STDIN and execute: + (while (read-line) + (push (current-line) *stdin* -1)) + (println (markdown (join *stdin* "\n"))) + (exit)) +[/text] + +;; version 2011-09-16 16:31:29 +;; Changed to different hash routine. Profiling shows that hashing takes 40% of the execution time. +;; Unfortunately this new version is only very slightly faster. +;; Command-line arguments hack in previous version doesn't work. +;; +;; version 2011-08-18 15:04:40 +;; various fixes, and added hack for running this from the command-line: +;; echo "hi there" | newlisp markdown.lsp +;; echo "hello world" | markdown.lsp +;; cat file.text | newlisp markdown.lsp +;; +;; version 2010-11-14 17:34:52 +;; some problems in ustring. Probably remove it one day, as it's non standard... +;; +;; version 2010-10-14 18:41:38 +;; added code to work round PCRE crash in (protect ... +;; +;; version date 2010-07-10 22:20:25 +;; modified call to 'read' since lutz has changed it +;; +;; version date 2009-11-16 22:10:10 +;; fixed bug in tokenize.html +;; +;; version date 2008-10-08 18:44:46 +;; changed nth-set to setf to be version-10 ready. +;; This means that now this script will NOT work with +;; earlier versions of newLISP!!!!!!!!!!! +;; requires Nestor if you want source code colouring... +;; +;; version date 2008-08-08 16:54:56 +;; changed (unless to (if (not ... :( +;; +;; version date 2008-07-20 14:!2:29 +;; added hex-str-to-unicode-char ustring +;; +;; version date 2008-03-07 15:36:09 +;; fixed load error +;; +;; version date 2007-11-17 16:20:57 +;; added syntax colouring module +;; +;; version date 2007-11-14 09:19:42 +;; removed reliance on dostring for compatibility with 9.1 + + +; eof
\ No newline at end of file diff --git a/tests/examplefiles/newlisp-parser.lsp b/tests/examplefiles/newlisp-parser.lsp new file mode 100644 index 00000000..985615b3 --- /dev/null +++ b/tests/examplefiles/newlisp-parser.lsp @@ -0,0 +1,298 @@ +#!/usr/bin/env newlisp + +;; @module Nlex +;; @author cormullion +;; @description newLISP source code lexer/tokenizer/parser +;; @location somewhere on github +;; @version 0.1 of 2011-09-19 08:55:19 +;;<h4>About this module</h4> +;;<p>The Nlex module is a lexer/tokenizer/parser for newLISP source code. +;; An expert from StackOverflow xplains: +;; A tokenizer breaks a stream of text into tokens. +;; A lexer is basically a tokenizer, but it usually attaches extra context to the tokens. +;; A parser takes the stream of tokens from the lexer and turns it into an abstract syntax tree representing the program represented by the original text.</p> +;;<p><b>Usage</b></p> +;;<p>To tokenize/parse source code stored in symbol 'original, use <b>parse-newlisp</b>, To convert the parsed source tree back to plain source, use <b>nlx-to-plaintext</b>:</p> +;;<pre> +;;(letn ((converted (Nlex:parse-newlisp original-source)) ; parses +;; (new-original (Nlex:nlx-to-plaintext converted))) ; converts back to plain text +;;</pre> +;;<p>After this round trip, original-source and new-original should be identical.</p> +;;<p></p> + +(context 'Nlex) + +; class variables + +(define *cursor*) +(define *source-length*) +(define *source-list*) +(define *depth*) +(define *tree*) +(define *loc*) + +(define (get-next-char) + (let ((nch "")) + (if (< *cursor* *source-length*) + (begin + (set 'nch (*source-list* *cursor*)) + (inc *cursor* (utf8len nch))) + (set 'nch nil)) + nch)) + +(define (peek-char) + (let ((pch "")) + (if (< *cursor* *source-length*) + (set 'pch (*source-list* *cursor*)) + (set 'pch nil)))) + +(define (char-identifier-first? c) + (not (find (lower-case c) [text] #;"'(){}.0123456789[/text]))) + +(define (char-identifier? c) + (not (find (lower-case c) { "':,()}))) + +(define (char-numeric-first? c) + (find c {123456789+-.0})) + +(define (char-numeric? c) + (find c {0123456789+-.xXabcdefABracketedCommandDEF})) + +(define (char-whitespace? c) + (or (= c " ") (= c "\n") (= c "\t"))) + +(define (open-paren-token) + (add-to-parse-tree '(LeftParen "("))) + +(define (close-paren-token) + (add-to-parse-tree '(RightParen ")"))) + +(define (read-comment c) + (let ((res c) (ch "")) + (while (and (!= (set 'ch (get-next-char)) "\n") ch) + (push ch res -1)) + (add-to-parse-tree (list 'Comment (string res "\n"))))) + +(define (read-identifier c) + (let ((res c) (ch "")) + ; look for end of identifier + (while (and (not (find (set 'ch (peek-char)) " \"',()\n\t\r")) (!= ch nil)) + (push (get-next-char) res -1)) + (add-to-parse-tree (list 'Symbol res)))) + +(define (read-number-scanner list-so-far) + (let ((next-char (peek-char))) + ;; if next-char is a digit then recurse + (if (and (char-numeric? next-char) next-char) + (read-number-scanner (cons (get-next-char) list-so-far)) + (reverse list-so-far)))) + +(define (precise-float str) +; more faithful to original format than newLISP's float? + (let ((p "") (q "")) + (map set '(p q) (parse str ".")) + (append p "." q))) + +(define (scientific-float str) + (let ((p "") (q "")) + (map set '(p q) (parse str "e")) + (append p "e" q))) + +(define (read-number c) + (let ((res '() number-as-string "")) + (set 'number-as-string (join (read-number-scanner (list c)))) + (cond + ; try hex first + ((starts-with (lower-case number-as-string) "0x") + (set 'res (list 'Hex number-as-string))) + ; scientific notation if there's an e + ((find "e" (lower-case number-as-string)) + (set 'res (list 'Scientific (scientific-float number-as-string)))) + ; float? + ((find "." number-as-string) + ; newLISP's float function isn't quite what we want here + (set 'res (list 'Float (precise-float number-as-string)))) + ; octal, not hex or float? 017 is OK, 019 is read as 10 + ((and (starts-with (lower-case number-as-string) "0") + (> (length number-as-string) 1) + (empty? (difference (explode number-as-string) (explode "01234567")))) + (set 'res (list 'Octal number-as-string))) + ; perhaps an integer? 019 is read as 19 ... + ((integer? (int number-as-string 0 10)) + (set 'res (list 'Integer (int number-as-string 0 10)))) + ; give up + (true + (set 'res (list 'NaN "NaN")))) + (add-to-parse-tree res))) + +(define (read-quote) + (add-to-parse-tree '(Quote "'"))) + +(define (read-quoted-string) + (let ((res {}) (ch {})) + (while (and (!= (set 'ch (get-next-char)) {"}) ch) + (push ch res -1) + ; check for backslashed quotes + (when (= ch {\}) + (set 'ch (get-next-char)) + (push ch res -1))) + (add-to-parse-tree (list 'QuotedString res)))) + +(define (read-braced-string) + (let ((res "") (ch {}) (level 1)) + ; we've already seen the first { so we're up to level 1 + (while (> level 0) + (set 'ch (get-next-char)) + (if (= ch "{") (inc level)) + (if (= ch "}") (dec level)) + (if (or (< level 0) (= ch nil)) (throw-error (string "error in a braced string at character " *cursor*))) + ; don't push final "}" + (if (and (> level 0)) (push ch res -1))) + (add-to-parse-tree (list 'BracedString res)))) + +(define (read-bracketed-string ch) + (let ((res "") (ch {})) + (cond + ; bracketed TEXT? + ((= (lower-case (join (slice *source-list* (- *cursor* 1) 6))) "[text]") + ; look for final [/text] + (inc *cursor* 5) + ; look for end + (while (and (< *cursor* (- *source-length* 7)) + (!= (lower-case (join (*cursor* 7 *source-list*))) "[/text]") + ch) + (push (get-next-char) res -1)) + (inc *cursor* 7) + (add-to-parse-tree (list 'BracketedText res))) + ; bracketed CMD? + ((= (lower-case (join (slice *source-list* (- *cursor* 1) 5))) "[cmd]") + ; look for final [/cmd] + (inc *cursor* 4) + (while (and (< *cursor* (- *source-length* 6)) + (!= (lower-case (join (*cursor* 6 *source-list*))) "[/cmd]") + ch) + (push (get-next-char) res -1)) + (inc *cursor* 6) + (add-to-parse-tree (list 'BracketedCommand res))) + ; must be those weird bracketed identifiers + (true + (while (and (!= (set 'ch (get-next-char)) {]}) ch) + (push ch res -1) + ; check for backslashed quotes + (when (= ch {\}) + (set 'ch (get-next-char)) + (push ch res -1))) + (add-to-parse-tree (list 'BracketedIdentifier res)))))) + +(define (read-whitespace ch) + (let ((res ch)) + (while (find (set 'ch (peek-char)) " \n\t") + (push (get-next-char) res -1)) + (add-to-parse-tree (list 'WhiteSpace (base64-enc res))))) + +(define (get-token) + (let ((first-char (get-next-char))) + (if first-char + (cond + ; a - or + could be the start of a symbol or a number, so look at the next char + ((or (= first-char "-") (= first-char "+")) + (if (find (peek-char) "1234567890") + (read-number first-char) + (read-identifier first-char))) + ((char-whitespace? first-char) + (read-whitespace first-char)) + ((= first-char {(}) + (open-paren-token)) + ((= first-char {)}) + (close-paren-token)) + ((= first-char {#}) + (read-comment first-char)) + ((= first-char {;}) + (read-comment first-char)) + ((= first-char {"}) + (read-quoted-string)) + ((= first-char "{") + (read-braced-string)) + ((= first-char "[") + (read-bracketed-string first-char)) + ((= first-char {'}) + (read-quote)) + ((char-numeric-first? first-char) + (read-number first-char)) + ((char-identifier-first? first-char) + (read-identifier first-char)) + (true (throw-error (string "{" first-char "} is an unrecognized token"))))))) + +(define (add-to-parse-tree token-pair) + (let (token (first token-pair)) + (cond + ((= token 'LeftParen) + (inc *depth*) + (push '((LeftParen "(")) *tree* *loc*) + (push -1 *loc*)) + ((= token 'RightParen) + (push '(RightParen ")") *tree* *loc*) + (dec *depth*) + (pop *loc*)) + (true + (push token-pair *tree* *loc*) + true)))) + +(define (parse-newlisp src) + ; main function: tokenize/lex/parse the string in src + (set '*depth* 0 + '*tree* '() + '*loc* '(-1) + '*cursor* 0 + '*source-list* (explode src) + '*source-length* (utf8len src) + '*source-length* (length *source-list*)) + (while (< *cursor* *source-length*) + (get-token)) + *tree*) + +(define (nlx-to-plaintext nlx (depth 0)) + (if (= depth 0) (set 'buff {})) ; if first pass, initialize a buffer + (dolist (element nlx) + (set 'token-type (first element) 'token-value (last element)) + (if (atom? token-type) + (cond + ((= token-type 'LeftParen) ; left parenthesis + (extend buff {(})) + ((= token-type 'RightParen) ; right parenthesis + (extend buff {)})) + ((= token-type 'WhiteSpace) ; whitespace + (dostring (s (base64-dec token-value)) + (extend buff (string (char s))))) + ((= token-type 'BracedString) ; braced string + (extend buff (string "{" token-value "}"))) + ((= token-type 'QuotedString) ; quoted string + (extend buff (string {"} token-value {"}))) + ((= token-type 'BracketedText) ; bracketed text + (extend buff (string {[text]} token-value {[/text]}))) + ((= token-type 'Quote); quote + (extend buff (string "'"))) + ((= token-type 'Comment) ; comment + (extend buff (string (last element) "\n"))) + ((= token-type 'Integer) ; int + (extend buff (string (int (last element))))) + ((= token-type 'Float) ; float + (extend buff (string (precise-float (last element))))) + ((= token-type 'Scientific) ; scientific notation + (extend buff (scientific-float (last element)))) + ((= token-type 'BracketedCommand) ; bracketed command + (extend buff (string {[cmd]} (last element) {[/cmd]}))) + ((or + (= token-type 'Symbol) ; close parenthesis + (= token-type 'Hex) ; hex + (= token-type 'NaN) ; not a number + (= token-type 'Octal) ; octal + ) + (extend buff (string (last element)))) + ((= token-type 'BracketedIdentifier) ; bracketed identifier + (extend buff (string {[} (last element) {]})))) + ; not an atom, so recurse but don't initialize buffer + (nlx-to-plaintext element 1))) + buff) + +;eof diff --git a/tests/examplefiles/reversi.lsp b/tests/examplefiles/reversi.lsp new file mode 100644 index 00000000..fa9a333c --- /dev/null +++ b/tests/examplefiles/reversi.lsp @@ -0,0 +1,427 @@ +#!/usr/bin/env newlisp +;; @module reversi.lsp +;; @description a simple version of Reversi: you as white against newLISP as black +;; @version 0.1 alpha August 2007 +;; @author cormullion +;; +;; 2008-10-08 21:46:54 +;; updated for newLISP version 10. (changed nth-set to setf) +;; this now does not work with newLISP version 9! +;; +;; This is my first attempt at writing a simple application using newLISP-GS. +;; The game algorithms are basically by +;; Peter Norvig http://norvig.com/paip/othello.lisp +;; and all I've done is translate to newLISP and add the interface... +;; +;; To-Do: work out how to handle the end of the game properly... +;; To-Do: complete newlispdoc for the functions + +(constant 'empty 0) +(constant 'black 1) +(constant 'white 2) +(constant 'outer 3) ; squares outside the 8x8 board + +(set '*board* '()) ; the master board is a 100 element list +(set '*moves* '()) ; list of moves made + +; these are the 8 different directions from a square on the board + +(set 'all-directions '(-11 -10 -9 -1 1 9 10 11)) + +; return a list of all the playable squares (the 8 by 8 grid inside the 10by10 + +(define (all-squares) + (local (result) + (for (square 11 88) + (if (<= 1 (mod square 10) 8) + (push square result -1))) +result)) + +; make a board + +(define (make-board) + (set '*board* (dup outer 100)) + (dolist (s (all-squares)) + (setf (*board* s) empty))) + +; for testing and working at a terminal + +(define (print-board) + (print { }) + (for (c 1 8) + (print c)) + (set 'c 0) + (for (i 0 99) + (cond + ((= (*board* i) 0) (print {.})) + ((= (*board* i) 1) (print {b})) + ((= (*board* i) 2) (print {w}))) + (if (and (<= i 88) (= (mod (+ i 1) 10) 0)) ; newline + (print "\n" (inc c)))) + (println "\n")) + +; the initial starting pattern + +(define (initial-board) + (make-board) + (setf (*board* 44) white) + (setf (*board* 55) white) + (setf (*board* 45) black) + (setf (*board* 54) black)) + +(define (opponent player) + (if (= player black) white black)) + +(define (player-name player) + (if (= player white) "white" "black")) + +(define (valid-move? move) + (and + (integer? move) + (<= 11 move 88) + (<= 1 (mod move 10) 8))) + +(define (empty-square? square) + (and + (valid-move? square) + (= (*board* square) empty))) + +; test whether a move is legal. The square must be empty +; and it must flip at least one of the opponent's piece + +(define (legal-move? move player) + (and + (empty-square? move) + (exists (fn (dir) (would-flip? move player dir)) all-directions))) + +; would this move by player result in any flips in the given direction? +; if so, return the number of the 'opposite' (bracketing) piece's square + +(define (would-flip? move player dir) + (let + ((c (+ move dir))) + (and + (= (*board* c) (opponent player)) + (find-bracketing-piece (+ c dir) player dir)))) + +(define (find-bracketing-piece square player dir) + ; return the square of the bracketing piece, if any + (cond + ((= (*board* square) player) square) + ((= (*board* square) (opponent player)) + (find-bracketing-piece (+ square dir) player dir)) + (true nil))) + +(define (make-flips move player dir) + (let + ((bracketer (would-flip? move player dir)) + (c (+ move dir))) + (if bracketer + (do-until (= c bracketer) + (setf (*board* c) player) + (push c *flips* -1) + (inc c dir))))) + +; make the move on the master game board, not yet visually + +(define (make-move move player) + (setf (*board* move) player) + (push move *moves* -1) + (set '*flips* '()) ; we're going to keep a record of the flips made + (dolist (dir all-directions) + (make-flips move player dir))) + +(define (next-to-play previous-player) + (let ((opp (opponent previous-player))) + (cond + ((any-legal-move? opp) opp) + ((any-legal-move? previous-player) + (println (player-name opp) " has no moves") + previous-player) + (true nil)))) + +; are there any legal moves (returns first) for this player? +(define (any-legal-move? player) + (exists (fn (move) (legal-move? move player)) + (all-squares))) + +; a list of all legal moves might be useful +(define (legal-moves player) + (let ((result '())) + (dolist (move (all-squares)) + (if (legal-move? move player) + (push move result))) + (unique result))) + +; define any number of strategies that can be called on to calculate +; the next computer move. This is the only one I've done... - make +; any legal move at random! + +(define (random-strategy player) + (seed (date-value)) + (apply amb (legal-moves player))) + +; get the next move using a particular strategy + +(define (get-move strategy player) + (let ((move (apply strategy (list player)))) + (cond + ((and + (valid-move? move) + (legal-move? move player)) + (make-move move player)) + (true + (println "no valid or legal move for " (player-name player) ) + nil)) + move)) + +; that's about all the game algorithms for now +; now for the interface + +(if (= ostype "Win32") + (load (string (env "PROGRAMFILES") "/newlisp/guiserver.lsp")) + (load "/usr/share/newlisp/guiserver.lsp") +) + +(gs:init) +(map set '(screen-width screen-height) (gs:get-screen)) +(set 'board-width 540) +; center on screen +(gs:frame 'Reversi (- (/ screen-width 2) (/ board-width 2)) 60 board-width 660 "Reversi") +(gs:set-border-layout 'Reversi) + +(gs:canvas 'MyCanvas 'Reversi) + (gs:set-background 'MyCanvas '(.8 .9 .7 .8)) + (gs:mouse-released 'MyCanvas 'mouse-released-action true) + +(gs:panel 'Controls) + (gs:button 'Start 'start-game "Start") + +(gs:panel 'Lower) + (gs:label 'WhiteScore "") + (gs:label 'BlackScore "") + +(gs:add-to 'Controls 'Start ) +(gs:add-to 'Lower 'WhiteScore 'BlackScore) +(gs:add-to 'Reversi 'MyCanvas "center" 'Controls "north" 'Lower "south") + +(gs:set-anti-aliasing true) +(gs:set-visible 'Reversi true) + +; size of board square, and radius/width of counter +(set 'size 60 'width 30) + +; initialize the master board + +(define (initial-board) + (make-board) + (setf (*board* 44) white) + (setf (*board* 55) white) + (setf (*board* 45) black) + (setf (*board* 54) black) +) + +; draw a graphical repesentation of the board + +(define (draw-board) + (local (x y) + (dolist (i (all-squares)) + (map set '(x y) (square-to-xy i)) + (gs:draw-rect + (string x y) + (- (* y size) width ) ; !!!!!! + (- (* x size) width ) + (* width 2) + (* width 2) + gs:white)))) + +(define (draw-first-four-pieces) + (draw-piece 44 "white") + (draw-piece 55 "white") + (draw-piece 45 "black") + (draw-piece 54 "black")) + +; this next function can mark the legal moves available to a player + +(define (show-legal-moves player) + (local (legal-move-list x y) + (set 'legal-move-list (legal-moves player)) + (dolist (m (all-squares)) + (map set '(x y) (square-to-xy m)) + (gs:draw-rect + (string x y) + (- (* y size) width ) ; !!!!!! + (- (* x size) width ) + (* width 2) + (* width 2) + (if (find m legal-move-list) gs:blue gs:white) + ) + ) + ) +) + +; convert the number of a square on the master board to coordinates + +(define (square-to-xy square) + (list (/ square 10) (mod square 10))) + +; draw one of the pieces + +(define (draw-piece square colour) + (local (x y) + (map set '(x y) (square-to-xy square)) + (cond + ((= colour "white") + (gs:fill-circle + (string x y) + (* y size) ; !!!!!!! y first, cos y is x ;-) + (* x size) + width + gs:white)) + + ((= colour "black") + (gs:fill-circle + (string x y) + (* y size) + (* x size) + width + gs:black)) + + ((= colour "empty") + (gs:draw-rect + (string x y) + (- (* y size) width ) + (- (* x size) width ) + (* width 2) + (* width 2) + gs:white)) + ))) + +; animate the pieces flipping + +(define (flip-piece square player) +; flip by drawing thinner and fatter ellipses +; go from full disk in opposite colour to invisible +; then from invisible to full disk in true colour + (local (x y colour) + (map set '(x y) (square-to-xy square)) + ; delete original piece + (gs:delete-tag (string x y)) + (set 'colour (if (= player 2) gs:black gs:white )) + (for (i width 1 -3) + (gs:fill-ellipse + (string x y {flip} i) + (* y size) ; y first :-) !!! + (* x size) + i + width + colour) + (sleep 20) ; this might need adjusting... + (gs:delete-tag (string x y {flip} i)) + ) + (set 'colour (if (= player 2) gs:white gs:black)) + (for (i 1 width 3) + (gs:fill-ellipse + (string x y {flip} i) + (* y size) ; :-) !!! + (* x size) + i + width + colour) + (sleep 20) + (gs:delete-tag (string x y {flip} i)) + ) + ; draw the piece again + (gs:fill-circle + (string x y) + (* y size) + (* x size) + width + colour) + ) +) + +(define (do-move move player) + (cond + ; check if the move is good ... + ((and (!= player nil) + (valid-move? move) + (legal-move? move player)) + + ; ... play it + ; make move on board + (make-move move player) + ; and on screen + (draw-piece move (player-name player)) + (gs:update) + ; do flipping stuff + + ; wait for a while + (sleep 1000) + + ; then do flipping + (dolist (f *flips*) + (flip-piece f player)) + + (inc *move-number*) + (draw-piece move (player-name player)) + (gs:update) + + ; update scores + (gs:set-text 'WhiteScore + (string "White: " (first (count (list white) *board*)))) + (gs:set-text 'BlackScore + (string "Black: " (first (count (list black) *board*)))) + ) + ; or return nil + (true + nil))) + +; the game is driven by the mouse clicks of the user +; in reply, the computer plays a black piece +; premature clicking is possible and possibly a bad thing... + +(define (mouse-released-action x y button modifiers tags) + ; extract the tag of the clicked square + (set 'move (int (string (first tags)) 0 10)) + (if (do-move move player) + (begin + (set 'player (next-to-play player)) + ; there is a training mode - legal squares are highlighted + ; you can uncomment the next line... + ; (show-legal-moves player) + (gs:update) + + ; wait for black's reply + (gs:set-cursor 'Reversi "wait") + (gs:set-text 'Start "black's move - thinking...") + ; give the illusion of Deep Thought... + (sleep 2000) + ; black's reply + ; currently only the random strategy has been defined... + (set 'strategy random-strategy) + (set 'move (apply strategy (list player))) + (do-move move player) + (set 'player (next-to-play player)) + ; (show-legal-moves player) ; to see black's moves + (gs:set-text 'Start "your move") + (gs:set-cursor 'Reversi "default") + (gs:update)))) + +(define (start-game) + (gs:set-text 'Start "Click a square to place a piece!") + (gs:disable 'Start) + (set 'player white)) + +(define (start) + (gs:set-text 'Start "Start") + (gs:enable 'Start) + (set '*move-number* 1 + '*flips* '()) + (initial-board) + (draw-board) + (draw-first-four-pieces)) + +(start) + +(gs:listen)
\ No newline at end of file |