! Copyright (C) 2008 Doug Coleman. ! See http://factorcode.org/license.txt for BSD license. USING: arrays io io.styles kernel memoize namespaces peg math combinators sequences strings html.elements xml.entities xmode.code2html splitting io.streams.string peg.parsers sequences.deep unicode.categories ; IN: farkup SYMBOL: relative-link-prefix SYMBOL: disable-images? SYMBOL: link-no-follow? string escape-string ] action ; MEMO: delimiter ( -- parser ) [ dup delimiters member? swap "\r\n=" member? not and ] satisfy [ 1string ] action ; : surround-with-foo ( string tag -- seq ) dup swap swapd 3array ; : delimited ( str html -- parser ) [ over token hide , text [ surround-with-foo ] swapd curry action , token hide , ] seq* ; MEMO: escaped-char ( -- parser ) [ "\\" token hide , any-char , ] seq* [ >string ] action ; MEMO: strong ( -- parser ) "*" "strong" delimited ; MEMO: emphasis ( -- parser ) "_" "em" delimited ; MEMO: superscript ( -- parser ) "^" "sup" delimited ; MEMO: subscript ( -- parser ) "~" "sub" delimited ; MEMO: inline-code ( -- parser ) "%" "code" delimited ; MEMO: nl ( -- parser ) "\r\n" token [ drop "\n" ] action "\r" token [ drop "\n" ] action "\n" token 3choice ; MEMO: 2nl ( -- parser ) nl hide nl hide 2seq ; MEMO: h1 ( -- parser ) "=" "h1" delimited ; MEMO: h2 ( -- parser ) "==" "h2" delimited ; MEMO: h3 ( -- parser ) "===" "h3" delimited ; MEMO: h4 ( -- parser ) "====" "h4" delimited ; MEMO: eq ( -- parser ) [ h1 ensure-not , h2 ensure-not , h3 ensure-not , h4 ensure-not , "=" token , ] seq* ; : render-code ( string mode -- string' ) >r string-lines r> [
            htmlize-lines
        
] with-string-writer ; : invalid-url "javascript:alert('Invalid URL in farkup');" ; : check-url ( href -- href' ) { { [ dup empty? ] [ drop invalid-url ] } { [ dup [ 127 > ] contains? ] [ drop invalid-url ] } { [ dup first "/\\" member? ] [ drop invalid-url ] } { [ CHAR: : over member? ] [ dup { "http://" "https://" "ftp://" } [ head? ] with contains? [ drop invalid-url ] unless ] } [ relative-link-prefix get prepend ] } cond ; : escape-link ( href text -- href-esc text-esc ) >r check-url escape-quoted-string r> escape-string ; : make-link ( href text -- seq ) escape-link [ "r , r> "\"" , link-no-follow? get [ " nofollow=\"true\"" , ] when ">" , , "" , ] { } make ; : make-image-link ( href alt -- seq ) disable-images? get [ 2drop "Images are not allowed" ] [ escape-link [ "\""" , ] { } make ] if ; MEMO: image-link ( -- parser ) [ "[[image:" token hide , [ "|]" member? not ] satisfy repeat1 [ >string ] action , "|" token hide [ CHAR: ] = not ] satisfy repeat0 2seq [ first >string ] action optional , "]]" token hide , ] seq* [ first2 make-image-link ] action ; MEMO: simple-link ( -- parser ) [ "[[" token hide , [ "|]" member? not ] satisfy repeat1 , "]]" token hide , ] seq* [ first dup make-link ] action ; MEMO: labelled-link ( -- parser ) [ "[[" token hide , [ CHAR: | = not ] satisfy repeat1 , "|" token hide , [ CHAR: ] = not ] satisfy repeat1 , "]]" token hide , ] seq* [ first2 make-link ] action ; MEMO: link ( -- parser ) [ image-link , simple-link , labelled-link , ] choice* ; DEFER: line MEMO: list-item ( -- parser ) [ "-" token hide , ! text , [ "\r\n" member? not ] satisfy repeat1 [ >string escape-string ] action , ] seq* [ "li" surround-with-foo ] action ; MEMO: list ( -- parser ) list-item nl hide list-of [ "ul" surround-with-foo ] action ; MEMO: table-column ( -- parser ) text [ "td" surround-with-foo ] action ; MEMO: table-row ( -- parser ) "|" token hide table-column "|" token hide list-of "|" token hide nl hide optional 4seq [ "tr" surround-with-foo ] action ; MEMO: table ( -- parser ) table-row repeat1 [ "table" surround-with-foo ] action ; MEMO: code ( -- parser ) [ "[" token hide , [ CHAR: { = not ] satisfy repeat1 optional [ >string ] action , "{" token hide , "}]" token ensure-not any-char 2seq repeat0 [ concat >string ] action , "}]" token hide , ] seq* [ first2 swap render-code ] action ; MEMO: line ( -- parser ) [ nl table 2seq , nl list 2seq , text , strong , emphasis , link , superscript , subscript , inline-code , escaped-char , delimiter , eq , ] choice* repeat1 ; MEMO: paragraph ( -- parser ) line nl over 2seq repeat0 nl nl ensure-not 2seq optional 3seq [ dup [ dup string? not swap [ blank? ] all? or ] deep-all? [ "

" swap "

" 3array ] unless ] action ; PRIVATE> PEG: parse-farkup ( -- parser ) [ list , table , h1 , h2 , h3 , h4 , code , paragraph , 2nl , nl , ] choice* repeat0 nl optional 2seq ; : write-farkup ( parse-result -- ) [ dup string? [ write ] [ drop ] if ] deep-each ; : convert-farkup ( string -- string' ) parse-farkup [ write-farkup ] with-string-writer ;