diff --git a/README.md b/README.md index ea2225f..37fa94a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ I have recently successfully build the demo site. And yes, it now will work! -However, i haven't written the tutorial and the manual page. will do this next week. +Now i'm just refing this melt and modefy its api; ## Part Preview - the init welcome page(use css framework) @@ -25,7 +25,7 @@ $ ./melt-install uninstall # use this to uninstall ``` ## current status -* [x] add subcommand logic now +- ~~add subcommand logic now~~ done * will add more subcommands to complete the function - [x] add init command - [x] add exec command diff --git a/melt/assert.ss b/melt/assert.ss new file mode 100644 index 0000000..c53e7f1 --- /dev/null +++ b/melt/assert.ss @@ -0,0 +1,23 @@ +#!chezscheme +(library + (melt assert) + (export mmessage + massert) + (import (scheme)) + + ;; there are three level errors + ;; 'error 'warn 'fatal + + ;; 'error: the input is not correct, but the program will continue + ;; 'warn: the input may induce some error + ;; 'fatal: the program will breakdown and quit + + ;; error format: + ;; : --> : || + ;; warn format: + ;; : + ;; fatal format: + ;; : + + + ) diff --git a/melt/asset.ss b/melt/asset.ss new file mode 100644 index 0000000..f8512fc --- /dev/null +++ b/melt/asset.ss @@ -0,0 +1,23 @@ +(library (melt asset) + (export asset-cp + asset-list-cp) + (import (scheme) + (melt lib file) + (melt structure)) + + (import type-asset) + + ;; for single object + ;; copy the src file to the target-directory + (define (asset-cp asset-obj) + (let ((src-directory (asset-source asset-obj)) + (target-directory (asset-target asset-obj))) + (cp-rf src-directory + (string-append target-directory + (directory-separator-string) + src-directory)))) + + (define (asset-list-cp asset-list) + (map asset-cp asset-list)) + + ) diff --git a/melt/cell.ss b/melt/cell.ss new file mode 100644 index 0000000..487d756 --- /dev/null +++ b/melt/cell.ss @@ -0,0 +1,35 @@ +(library + (melt cell) + (export make-cell) + (import (scheme)) + + (define (%check-cell-proc proc) + (if (eq? 2 (abs (procedure-arity-mask proc))) + proc + (error 'make-cell "incorrect number of arguments of cell filter definition"))) + + (define (%inner-proc proc) + (let ((internal (%check-cell-proc proc))) + (case-lambda + [(value) + (if (eq? 2 (abs (procedure-arity-mask value))) + (set! internal value) + (error 'make-cell "incorrect number of arguments of cell filter definition"))] + [() internal]))) + + (define make-cell + (case-lambda + [(default-value) + (make-cell default-value (lambda (x) x))] + [(default-value filter) + (let ((internal-value default-value) + (internal-cell (%inner-proc filter))) + (case-lambda + [(value) + (set! internal-value value)] + [(value upt-filter) + (set! internal-value value) + (internal-cell upt-filter)] + [() ((internal-cell) internal-value)]))])) + + ) diff --git a/melt/command.ss b/melt/command.ss new file mode 100644 index 0000000..7aeac62 --- /dev/null +++ b/melt/command.ss @@ -0,0 +1,57 @@ +(library + (melt command) + (export create-command + command-add! + command-remove! + command-proc + command-desc + command-name + command-query + show-commands) + (import (scheme) + (melt data) + (melt lib console)) + + ;; command itself is a data + ;; symbol, which specifics string in command line + ;; string, one line description + ;; procedure for the command, accept arguments + (define (create-command name desc proc) + (create-data '(name desc proc) + `(,name ,desc ,proc))) + + (define (command-name command) + (data-value-query 'name command)) + (define (command-desc command) + (data-value-query 'desc command)) + (define (command-proc command) + (data-value-query 'proc command)) + + ;; add one command to command data + (define (command-add! command data) + (update-data! (create-data (list (command-name command)) + (list command)) + data)) + + ;; remove one command in command data + (define (command-remove! command data) + (update-data! (list (command-name command)) data)) + + ;; query command in a data + (define (command-query name data) + (data-value-query name data)) + + ;; display command names and its description + (define (show-commands command-data) + (define (show-command command) + (format #t " ~8a>>> ~a~%" (symbol->string (command-name command)) (command-desc command))) + + ; (gem:format " ~10@a-->~10a~%" + ; `(("[37;1m" . ,(symbol->string (command-name command))) + ; ("[38;5;166m" . ,(command-desc command))))) + + (do ((command-list (data-values command-data) (cdr command-list))) + ((null? command-list) #t) + (show-command (car command-list)))) + + ) diff --git a/melt/command/init.ss b/melt/command/init.ss new file mode 100644 index 0000000..ab0b5ed --- /dev/null +++ b/melt/command/init.ss @@ -0,0 +1,46 @@ +(library + (melt command init) + (export init-cli) + (import (scheme) + (melt command) + (melt cell) + (melt lib console)) + + (define (init-help) + (gemd:help (string-append + (gem:text "[37;1m" "melt") + (gem:text "[38;5;67m" " init ") + (gem:text "[38;5;253m" "name"))) + (gemd:help (string-append + "if the" + (gem:text "[38;5;253m" " name ") + "is not provided, it will generate .melt in working directory"))) + + ;; create some files + (define (initprocedure) + (mkdir ".melt") + (mkdir "public") + (mkdir "post") + (system "touch .melt/config.scm") + (system "touch .melt/melt.scm") + (mkdir ".melt/fasl") + (mkdir ".melt/resource")) + + ;; interface to be invoked in ui + (define command-init + (lambda args + (cond + [(null? args) + (init-help)] + [(eq? (length args) 1) + (let ((target (make-cell (car args) (lambda (value) (if (file-exists? value) + (begin (gemd:error "file exists!") #f) + value))))) + (if (target) + (begin (target (target) (lambda (value) value)) (mkdir (target)) (cd (target)) (initprocedure))))] + [else (gemd:info "only one argument required.")]))) + + (define init-cli + (create-command 'init + "init command to generate basic settings." + command-init))) diff --git a/melt/command/invoke.ss b/melt/command/invoke.ss new file mode 100644 index 0000000..fcbca70 --- /dev/null +++ b/melt/command/invoke.ss @@ -0,0 +1,42 @@ +(library + (melt command invoke) + (export invoke-cli) + (import (scheme) + (melt invoke) + (melt lib console) + (melt lib file) + (melt command) + (melt utils)) + + ;; display the build command usage + (define (invoke-cli-help) + (gemd:info + (string-append + (gem:text "[37;1m" "melt") + (gem:text "[38;5;67m" " invoke") + (gem:text "[38;5;253m" " \n") + " If the" + (gem:text "[38;5;253m" " ") + "is not provided, use" + (gem:text "[38;5;190m" " melt.scm ") + "instead."))) + + (define (invoke-cli global-invocation) + (create-command 'invoke + "melt command to execute one list actions, mostly build the site." + (lambda args + (cond + [(eq? 1 (length args)) + (let ((main-ex-file (car args))) + (if (file-exists? main-ex-file) + (begin + (load main-ex-file) + (invoke global-invocation)) + (begin + (gemd:error "Coundn't find config file !!") + (gemd:error (string-append "expect " (basename main-ex-file) " but the file doesn't exist!")))))] + [else + (gemd:warn "please give me the melt.scm!") + (invoke-cli-help)])))) + + ) diff --git a/melt/command/post.scm b/melt/command/post.scm new file mode 100644 index 0000000..c5a5cc8 --- /dev/null +++ b/melt/command/post.scm @@ -0,0 +1,94 @@ +(library + (melt command post) + (export post) + (import (scheme) + (melt uutil) + (melt lib console) + (melt config) + (melt command)) + + (define (post data) + (create-command 'post "note post command." + (lambda args + (cond + [(null? args) + (gemd:help (gem:text "[38;5;15m" "this is the post command, contain 'new', 'list', and 'edit' subcommands.")) + (gemd:help (gem:text "[38;5;15m" "use -h in each subcommand to get more info."))] + [(equal? (car args) "new") + (apply (action-new data) (cdr args))] + [(equal? (car args) "list") + (apply (action-list data) (cdr args))] + [(equal? (car args) "edit") + (apply (action-edit data) (cdr args))] + [(equal? (car args) "-h") + (gemd:help (gem:text "[38;5;15m" "this is the post command, contain 'new', 'list', and 'edit' subcommands.")) + (gemd:help (gem:text "[38;5;15m" "use -h in each subcommand to get more info."))] + [else (gemd:error "unrecognized command, add -h to get info")])))) + + ;; ==================================================================== + ;; command new + (define (action-new data) + (lambda title + (let* ((str-num-sequence (map car (get-md-post-title-list "post"))) + (numbers (sort > (map string->number str-num-sequence)))) + (if (not (null? title)) + (write-template (car title) + (string-append (config-value-query 'post data) + "/" + (number->string (+ 1 (car numbers))) ".md")) + (gemd:error "please provide a title\n"))))) + + + (define (write-template title path) + (call-with-output-file + path + (lambda (port) + (display "~~~\n" port) + (format port "title : ~a~%" title) + (format port "date : ~a~%" (date-and-time)) + (display "~~~\n\n" port)))) + + ;; ==================================================================== + ;; command list + (define (action-list data) + (lambda args + (if (not (null? args)) + (if (equal? (car args) "-h") + (gem:display + (gem:text "[38;5;15m" "A subcommand to list post sequence\n"))) + (list-post data)))) + + (define (list-post data) + (lambda () + (do ((title-list (sort (lambda (pre aft) (string>? (car pre) (car aft))) (get-md-post-title-list (config-value-query 'post data))) + (cdr title-list))) + ((null? title-list) (gem:display (gem:text "[37m" "=====\n"))) + (gemd:item (string-append + (gem:text "[38;5;15m" (car (car title-list))) + " ==> " + (gem:text "[38;5;15m" (cdr (car title-list))) + "\n"))))) + + + ;; ==================================================================== + ;; command edit + (define (action-edit data) + (lambda number + (let* ((str-num-sequence (map car (get-md-post-title-list "post"))) + (numbers (sort > (map string->number str-num-sequence)))) + (if (null? number) + ((call-editor data) (number->string (car numbers))) + ((call-editor data) (car number)))))) + + + ;; use external shell to call editor + (define (call-editor data) + (lambda (number) + (system (string-append (config-value-query 'editor data) + " " + (config-value-query 'post data) + "/" + number ".md")))) + + + ) diff --git a/melt/command/serve.scm b/melt/command/serve.scm index 462058e..5c81e4d 100644 --- a/melt/command/serve.scm +++ b/melt/command/serve.scm @@ -1,24 +1,24 @@ -(library (melt command serve) - (export serve) - (import (scheme) - (melt structure)) +(library + (melt command serve) + (export serve-cli) + (import (scheme) + (melt lib console) + (melt command)) - (import type-command) + (define (serve-help) + (gemd:info "the command is not ready yet")) - (define (serve-help) - (display "the command is not ready yet\n")) + (define serve-command + (lambda args + (cond + [(null? args) + (gemd:info "the command is not ready yet")] + [else + (gemd:info "the command is not ready yet")]))) - (define serve-cli - (lambda args - (cond - [(null? args) - (display "the command is not ready yet\n")] - [else - (display "the command is not ready yet\n")]))) + (define serve-cli + (create-command 'serve + "melt server to preview your site." + serve-command)) - (define serve - (make-command 'serve - "melt server to preview your site" - serve-cli)) - - ) + ) diff --git a/melt/config.ss b/melt/config.ss new file mode 100644 index 0000000..2f510b4 --- /dev/null +++ b/melt/config.ss @@ -0,0 +1,35 @@ +(library + (melt config) + (export config-update-option! + config-add-option! + config-value-query + config-options-query) + (import (scheme) + (melt data)) + + ;; update one option or add the option with default value + (define (config-update-option! key value data) + (let ((para (data-guard-query key data))) + (if para + (para value) + (config-add-option! key value data)))) + + ;; add one option with default value + (define (config-add-option! key value data) + (update-data! (create-data '(key) (list value)) data)) + + ;; return the value of #f if it doesn't exist + (define (config-value-query key data) + (data-value-query key data)) + + ;; if no arg provided, return the whole options list + ;; if provide a key(option), return #t if exists and #f otherwise + (define (config-options-query data) + (lambda para + (cond + [(null? para) + (data-keys data)] + [(symbol? (car para)) + (data-value-query (car para) data)]))) + + ) diff --git a/melt/data.ss b/melt/data.ss new file mode 100644 index 0000000..19f0544 --- /dev/null +++ b/melt/data.ss @@ -0,0 +1,111 @@ +(library + (melt data) + (export create-data + update-data! + data-keys + data-values + data-guards + data->alist + data-guard-query + data-value-query) + (import (scheme) + (melt cell) + (melt utils)) + + ;; !!!!!! need to take use of gurd of cell + + ;; accept nothing or list of keys and list of values + ;; keys are a list symbols + ;; values are corresponded to the keys + ;; or an assoc list + (define create-data + (lambda args + (cond + [(null? args) + (make-cell '())] + [(and (eq? 1 (length args)) (alist? (car args))) + ((lambda (keys values) + (make-cell (map cons keys (map make-cell values)))) + (map car (car args)) + (map cdr (car args)))] + [(and (eq? 2 (length args)) (list? (car args))) + ((lambda (keys values) + (make-cell (map cons keys (map make-cell values)))) + (car args) + (cadr args))] + [else + (error 'create-data "please provide keys and args or nothing")]))) + + ;; return the data key list + (define (data-keys data) + (map car (data))) + + ;; return the data value list + (define (data-values data) + (map (lambda (proc) (proc)) (map cdr (data)))) + + ;; return a list of accessors + (define (data-guards data) + (map cdr (data))) + + ;; delete one item in data, return left item + (define (%delete-data key data) + (remove (assq key (data)) (data))) + + ;; transform data to an association list + (define (data->alist data) + (map cons (data-keys data) (data-values data))) + + ;; same as %delete-data, but return unspecific + (define (%delete-data! key data) + (data (remove (assq key (data)) (data)))) + + ;; query one value via a key + (define (data-value-query key data) + (if (memv key (data-keys data)) + (force (cdr (assq key (data)))) + #f)) + + ;; query one accessor of one item + (define (data-guard-query key data) + (if (memv key (data-keys data)) + (cdr (assq key (data))) + #f)) + + ;; add one item in data + ;; return the alist if successfully + ;; return #f if exist one + (define (%add-data! key value data) + (if (memv key (data-keys data)) + #f + (begin + (data (cons (cons key (make-cell value)) (data))) + (data->alist data)))) + + ;; it receives three type args + ;; 1. the data type, used to update values or add new values + ;; 2. the assoc list, same as 1 + ;; 3. a list of symbols, all the symbols may be the keys and it + ;; will delete items in the data + ;; first two is used to update! the data + ;; the third is used to delete! the data + (define (update-data! k-values data) + (if (and (procedure? data) (alist? (data))) + (cond + [(alist? k-values) + (do ((keys (map car k-values) (cdr keys))) + ((null? keys) data) + (if (memv (car keys) (data-keys data)) + ((data-guard-query (car keys) data) + (cdr (assq (car keys) k-values))) + (%add-data! (car keys) (cdr (assq (car keys) k-values)) data)))] + [(procedure? k-values) + (update-data! (data->alist k-values) data)] + [(list? k-values) + (do ((keys k-values (cdr keys))) + ((null? keys) data) + (%delete-data! (car keys) data))] + [else (error 'update-data! "no matched keys")]) + (error 'update-data! "should accept one data"))) + + ) diff --git a/melt/glob.ss b/melt/glob.ss new file mode 100644 index 0000000..ca21859 --- /dev/null +++ b/melt/glob.ss @@ -0,0 +1,81 @@ +#!chezscheme +(library + (melt glob) + (export %%invocation + %%user-commands + %%builtin-commands + user:command-add! + user:show-commands + user:command-query + inter:command-add! + inter:command-query + inter:show-commands + + user:config-query + user:config-add-option! + user:config-update-option! + ) + (import (scheme) + (melt invoke) + (melt data) + (melt config) + (melt command)) + + ;; /////config ////// + (define %%config (create-data)) + + (define (user:config-query key) + (config-value-query key %%config)) + + (define (user:config-add-option! key value) + (config-add-option! key value %%config)) + + (define (user:config-update-option! key value) + (config-update-option! key value %%config)) + + (define %%invocation (create-invocation)) + + ;; define global data + (define %%builtin-commands (create-data)) + (define %%user-commands (create-data)) + + (define (user:command-add! command) + (command-add! command %%user-commands)) + + (define (user:command-query name) + (let ((command (data-value-query name %%user-commands))) + (if command + (command-proc command) + #f))) + + (define (user:show-commands) + (show-commands %%user-commands)) + + ;; ---------------define inter command + (define (inter:command-add! command) + (command-add! command %%builtin-commands)) + + (define (inter:command-query name) + (let ((command (data-value-query name %%builtin-commands))) + (if command + (command-proc command) + #f))) + + (define (inter:show-commands) + (show-commands %%builtin-commands)) + + ;; ======= inter command import + (import (melt command init)) + (import (melt command invoke)) + (import (melt command serve)) + (import (melt command post)) + + (inter:command-add! init-cli) + + (inter:command-add! (invoke-cli %%invocation)) + + (inter:command-add! serve-cli) + + (inter:command-add! (post %%config)) + + ) diff --git a/melt/invoke.ss b/melt/invoke.ss new file mode 100644 index 0000000..20d5014 --- /dev/null +++ b/melt/invoke.ss @@ -0,0 +1,82 @@ +#!chezscheme +(library + (melt invoke) + (export + invoke + create-invocation + invoke-guard + invoke-procs + invoke-data + invoke-update-data! + invoke-delete-data! + invoke-add-proc! + create-proc + execute-proc) + (import (scheme) + (melt utils) + (melt data) + (melt cell)) + + (define invoke + (lambda (invocation) + (let ((proc-alist (data->alist (invoke-procs invocation)))) + (do ((procs-list (map cdr (sort (lambda (pre aft) + (> (car pre) (car aft))) + proc-alist)) + (cdr procs-list))) + ((null? procs-list) (void)) + (execute-proc (car procs-list)))))) + + ;; create invocation + (define create-invocation + (case-lambda + [() + (define invocation + (create-data `((iguard . #t) + (procs . ,(create-data)) + (data . ,(create-data))))) + (update-data! `((self . ,invocation)) invocation) + (make-cell invocation)] + [(iguard procs data) + (let ((invocation (create-data `((iguard . ,iguard) + (procs . ,procs) + (data . ,data))))) + (update-data! `((self . ,invocation)) invocation) + (make-cell invocation))])) + + (define (invoke-guard invocation) + (data-value-query 'iguard (invocation))) + + (define (invoke-procs invocation) + (data-value-query 'procs (invocation))) + + (define (invoke-data invocation) + (data-value-query 'data (invocation))) + + ;; update data + (define (invoke-update-data! key value invocation) + (let ((data (invoke-data invocation))) + (update-data! `((,key . ,value)) data))) + + ;; delete one item + (define (invoke-delete-data! key invocation) + (let ((data (invoke-data invocation))) + (update-data! (list key) data))) + + ;; add one proc + (define (invoke-add-proc! proc invocation) + (let ((procs (invoke-procs invocation))) + (update-data! `((,(+ 1 (apply max (if (null? (data-keys procs)) '(0) (data-keys procs)))) . ,proc)) + procs))) + + ;; create proc for chain execute unite + (define (create-proc proc . args) + (create-data `((proc . ,proc) + (args . ,args)))) + + ;; execute one proc + (define (execute-proc proc) + (apply (data-value-query 'proc proc) + (data-value-query 'args proc))) + + ) diff --git a/melt/lesh.ss b/melt/lesh.ss new file mode 100644 index 0000000..5d2d76a --- /dev/null +++ b/melt/lesh.ss @@ -0,0 +1,22 @@ +(library + (melt lesh) + (export ) + (import (scheme)) + + ;; help system will + (module lesh-system + [help-type] + + ;; help-type will tell the basic information for the type + (define (help-type arg) + (cond + [(string? arg) (cond + ([equal? arg "site"] + (display "this is the help doc for site!\n")) + (else (type-list)))] + [else (display "Sorry! No doc provided!\n")])) + + (define (type-list) + (display "Types are listed :\n") + (display "|| site || reader || page || post || asset ||\n"))) + ) diff --git a/melt/lib/console.ss b/melt/lib/console.ss new file mode 100644 index 0000000..863b85b --- /dev/null +++ b/melt/lib/console.ss @@ -0,0 +1,75 @@ +#!chezscheme +(library + (melt lib console) + (export gem:command + gem:text + gem:display + gem:format + gemd:info + gemd:warn + gemd:error + gemd:help + gemd:item) + (import (scheme) + (melt utils) + (melt cell)) + + ;; apply one shell sequence, and it won't reset it + (define (gem:command shell-sequence) + (string-append (string #\033) shell-sequence)) + + ;; generate code sequence text string + (define (gem:text shell-sequence text) + (string-append (gem:command shell-sequence) + text + (gem:command "[0m"))) + + ;; just append strings and display it + (define gem:display + (lambda args + (display (apply string-append args)))) + + ;; first is the format string, others are inserted strings + ;; (gem:format template-string assoc-list) + ;; assoc-list: ((code-sequence . str) ... ) + (define gem:format + (lambda args + (apply format + (flatten (list #t (car args) (map gem:text (map car (cadr args)) (map cdr(cadr args)))))))) + + ;; display info text + (define (gemd:info text) + (gem:display (gem:text "[37m" "[") + (gem:text "[38;5;155m" "info") + (gem:text "[37m" "] ") + text "\n")) + + ;; display warn text + (define (gemd:warn text) + (gem:display (gem:text "[37m" "{") + (gem:text "[38;5;220m" "warn") + (gem:text "[37m" "} ") + text "\n")) + + ;; display error text + (define (gemd:error text) + (gem:display (gem:text "[37m" "(") + (gem:text "[38;5;197m" "error") + (gem:text "[37m" ") ") + text "\n")) + + ;; display list text + (define (gemd:item text) + (gem:display (gem:text "[37m" "*") + (gem:text "[38;5;238m[48;5;15m" "item") + (gem:text "[37m" "* ") + text "\n")) + + ;; display help info + (define (gemd:help text) + (gem:display (gem:text "[37;1m" "%") + (gem:text "[38;5;123m" "help") + (gem:text "[37;1m" ">> ") + text "\n")) + + ) diff --git a/melt/lib/file.ss b/melt/lib/file.ss new file mode 100644 index 0000000..9335ed6 --- /dev/null +++ b/melt/lib/file.ss @@ -0,0 +1,82 @@ +(library + (melt lib file) + (export copy-file + cp-f + cp-rf + mkdir-r + basename + directory-separator-string) + (import (scheme) + (melt utils)) + + (define (directory-separator-string) + (string (directory-separator))) + + (define basename + (lambda (path) + (path-last (path-root path)))) + + ;; copy file + ;; the mode is for the output port + (define copy-file + (case-lambda + [(src-file target-file) + (let ((input-port (open-file-input-port src-file)) + (output-port (open-file-output-port target-file))) + (put-bytevector output-port (get-bytevector-all input-port)) + (close-port input-port) + (close-port output-port))] + [(src-file target-file mode) + (let ((input-port (open-file-input-port src-file)) + (output-port (open-file-output-port target-file mode))) + (put-bytevector output-port (get-bytevector-all input-port)) + (close-port input-port) + (close-port output-port))])) + + + ;; accept strings as arguments + ;; if the target exsites, replace it. + (define (cp-f src-file target-file) + (if (not (file-exists? src-file)) (error src-file "File not exists!")) + (mkdir-r (path-parent target-file)) + (copy-file src-file target-file (file-options no-fail))) + + ;; copy recursively and force + (define (cp-rf src-file target-file) + (if (file-exists? src-file) + (cond + [(file-regular? src-file) + (cp-f src-file target-file)] + [(file-directory? src-file) + (mkdir-r target-file) + (do ((file-list (directory-list src-file) (cdr file-list)) + (element (car (directory-list src-file)) (car file-list))) + ((null? file-list) + (cp-rf (string-append src-file (directory-separator-string) element) + (string-append target-file (directory-separator-string) element)) + #t) + (cp-rf (string-append src-file (directory-separator-string) element) + (string-append target-file (directory-separator-string) element)))]) + (error src-file "File not exists"))) + + ;; create directory recursively + ;; if one directory exists, just enter it and create rest directory + ;; it will never report an error!! + (define (mkdir-r dir) + ;; Create the dir just like use makedir bash command but clever + (define dir-list (decompose-path-name dir)) + (let ((file-name (if (path-absolute? dir) + (string-append "/" (car dir-list)) + (car dir-list)))) + (until (eq? '() dir-list) + (if (file-exists? file-name) + (if (not (file-directory? file-name)) + (begin + (format (current-error-port) "There exists conficts file!!~%") + (break))) + (mkdir file-name)) + (set! dir-list (cdr dir-list)) + (if (not (eq? '() dir-list)) + (set! file-name (string-append file-name "/" (car dir-list))))) + #t)) + ) diff --git a/melt/lib/markdown.scm b/melt/lib/markdown.scm index 63959a1..b20757d 100644 --- a/melt/lib/markdown.scm +++ b/melt/lib/markdown.scm @@ -74,56 +74,57 @@ (error 'pattern-atoms "unproperly pattern atom")])))) ($$check pattern (port-position port) port)) - (trace-define (g-check-list ls) - (lambda (ob) - (equal? ls ob))) + (define (g-check-list ls) + (lambda (ob) + (equal? ls ob))) ;; use the pattern to determine which context to enter in ;; return the parsed sxml and pass previous context(procedure) to it ;; so it can return to previous context ;; 这里之后可以使用cc来进行错误处理和异常报告排错, 需要和define-FA 联合 - (trace-define (context-transform sxml port pattern-ls FAs cur-context) - (call/cc - (lambda (cc) - (do ((patterns pattern-ls (cdr patterns)) - (key-context-list (map cons pattern-ls FAs))) - ((null? patterns) #f) - (display "this means last don't pass\n") - (let ((cur-pattern (car patterns))) - (if (pattern-match cur-pattern port) - (cc ((lambda (item) (cur-context (scone sxml item) port)) - ((cdr (assp (g-check-list cur-pattern) key-context-list)) (list) port))))))))) + (define (context-transform sxml port pattern-ls FAs cur-context) + (call/cc + (lambda (cc) + (do ((patterns pattern-ls (cdr patterns)) + (key-context-list (map cons pattern-ls FAs))) + ((null? patterns) #f) + (let ((cur-pattern (car patterns))) + (if (pattern-match cur-pattern port) + (cc ((lambda (item) (cur-context (scone sxml item) port cur-context)) + ((cdr (assp (g-check-list cur-pattern) key-context-list)) (list) port cur-context))))))))) ;; check each terminate pattern and end the context if matched returning value. ;; forward-numbers list is to forward the port position if matched. ;; if not match, return #f. ;; the value must use delay to wrap - (trace-define (context-return value port pattern-ls forward-numbers) - (call/cc - (lambda (cc) - (do ((end-pattern (cons `(,eof) pattern-ls) (cdr end-pattern)) - (query-forward-numbers (map cons (cons `(,eof) pattern-ls) - (cons 0 forward-numbers)))) - ((null? end-pattern) #f) - (let* ((cur-pattern (car end-pattern)) - (number (cdr (assp (g-check-list cur-pattern) query-forward-numbers)))) - (if (pattern-match cur-pattern port) - (begin (char-forward port number) (cc (force value))))))))) + (define (context-return value port pattern-ls forward-numbers) + (call/cc + (lambda (cc) + (do ((end-pattern (cons `(,eof) pattern-ls) (cdr end-pattern)) + (query-forward-numbers (map cons (cons `(,eof) pattern-ls) + (cons 0 forward-numbers)))) + ((null? end-pattern) #f) + (let* ((cur-pattern (car end-pattern)) + (number (cdr (assp (g-check-list cur-pattern) query-forward-numbers)))) + (if (pattern-match cur-pattern port) + (begin (char-forward port number) (cc (force value))))))))) ;; define a FA ;; end-lambda must be (lambda (sxml port) bodys...) ;; TODO 之后可以在else后边加入错误处理 (define-syntax define-FA (syntax-rules () - [(_ name end-lambda (end-patterns number-list) (trans-patterns FAs) ) + [(_ name pre-lambda end-lambda (end-patterns number-list) (trans-patterns FAs) ) (define name - (lambda (sxml port) - (cond - [(context-return (delay (end-lambda sxml port)) - port end-patterns number-list)] - [(context-transform sxml port trans-patterns FAs name)] - [else (error name "no proper transform context definition")])))])) + (lambda (sxml port pre-context) + (if (equal? pre-context name) + (cond + [(context-return (delay (end-lambda sxml port)) + port end-patterns number-list)] + [(context-transform sxml port trans-patterns FAs name)] + [else (error 'define-FA "no proper transform context definition")]) + (name (scone sxml (pre-lambda sxml port)) port name))))])) ;; (g 'x 3) ==> (x x x) (define (g ele num) @@ -141,41 +142,45 @@ ;; define makrdown-> sxml (define (markdown->sxml port) - (pattern-top-parse (list) port)) + (pattern-top-parse (list) port #f)) ;;; =========================== A ===== U ====== X ====================== ;; return the escaped char - (define (aux-escape sxml port) + (define (aux-escape sxml port . idle) (char-forward port 1) (read-char port)) ;; generate procedure which ignore 'number' chars (define (aux-ignore number) - (lambda (sxml port) + (lambda (sxml port . idle) (char-forward port number) '())) ;; read one char - (define (aux-common sxml port) + (define (aux-common sxml port . idle) (let ((char (read-char port))) (if (eof-object? char) '() char))) - ;; for the list - and * - (define (pattern-parse-list sxml port) - (display "not ready") '()) - ;; the top environment(context) (define-FA pattern-top-parse - (lambda (sxml port) `(,sxml)) + (lambda (sxml port) '()) + (lambda (sxml port) sxml) [(list `(,eof)) '(0)] [(list '(#\\ ()) '((#\newline #\space)) '(#\` #\` #\`) '(#\#) + '(#\* #\* #\*) + '(#\- #\- #\-) + '(#\- #\space #\[ (#\space) #\] #\space) + '(#\* #\space #\[ (#\space) #\] #\space) + '(#\* #\space #\[ (! #\space) #\] #\space) + '(#\- #\space #\[ (! #\space) #\] #\space) '(#\* #\space) '(#\- #\space) + '((#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) #\. #\space) '(#\! #\[) '(#\> #\space) '(())) @@ -183,8 +188,15 @@ (aux-ignore 1) pattern-parse-block-code pattern-parse-header - pattern-parse-list - pattern-parse-list + pattern-parse-hr + pattern-parse-hr + aux-parse-task-list-unchecked + aux-parse-task-list-unchecked + aux-parse-task-list-checked + aux-parse-task-list-checked + pattern-parse-ul-list + pattern-parse-ul-list + pattern-parse-ol-list pattern-parse-img pattern-parse-block-quote pattern-parse-paragraph)]) @@ -192,23 +204,27 @@ ;;; ==================== paragraph =================== ;; return the paragraph (define-FA pattern-parse-paragraph + (lambda (sxml port) '()) (lambda (sxml port) `(p ,sxml)) [(list '(#\newline #\newline) '(#\newline #\` #\` #\`) '(#\newline #\#) '(#\newline #\* #\space) '(#\newline #\- #\space) + '(#\newline (#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) #\. #\space) '(#\newline #\! #\[) '(#\newline #\> #\space)) - (list 2 1 1 1 1 1 1)] + (list 2 1 1 1 1 1 1 1)] [(list '(#\\ ()) '(#\newline) '(#\[) '(#\`) '(#\* #\*) - '(#\- #\-) + '(#\_ #\_) '(#\*) - '(#\-) + '(#\_) + '(#\$ #\$) + '(#\~ #\~) '(())) (list aux-escape aux-paragraph-space @@ -218,46 +234,37 @@ pattern-parse-strong pattern-parse-em pattern-parse-em + pattern-parse-math + pattern-parse-strike-through aux-common)]) - (define (aux-paragraph-space sxml port) + (define (aux-paragraph-space sxml port . idle) (context-return (delay #\space) port (list '(#\newline)) (list 1))) ;; ===================== block-quote ======================= ;; return block quote (define-FA pattern-parse-block-quote - (lambda (sxml port) sxml) - ((list '(#\newline #\newline) - '(#\newline #\` #\` #\`) - '(#\newline #\#) - '(#\newline #\* #\space) - '(#\newline #\- #\space) - '(#\newline #\! #\[) - '(#\newline #\> #\space)) - (list 2 1 1 1 1 1 1)) - ((list '(#\> #\space) - '(())) - (list (aux-ignore 2) - aux-parse-block-quote))) - - (define-FA aux-parse-block-quote + (aux-ignore 2) (lambda (sxml port) `(blockquote (p ,sxml))) ((list '(#\newline #\newline) '(#\newline #\` #\` #\`) '(#\newline #\#) '(#\newline #\* #\space) '(#\newline #\- #\space) + '(#\newline (#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) #\. #\space) '(#\newline #\! #\[) '(#\newline #\> #\space)) - (list 0 0 0 0 0 0 0)) + (list 2 1 1 1 1 1 1 1)) ((list '(#\\) '(#\newline) '(#\`) '(#\[) '(#\* #\*) - '(#\- #\-) + '(#\_ #\_) '(#\*) - '(#\-) + '(#\_) + '(#\$ #\$) + '(#\~ #\~) '(())) (list aux-escape aux-paragraph-space @@ -267,17 +274,172 @@ pattern-parse-strong pattern-parse-em pattern-parse-em + pattern-parse-math + pattern-parse-strike-through aux-common))) + ;; ======================== list ============================= + ;; for the list context + ;; return ul or ol + (define-FA pattern-parse-ul-list + (lambda (sxml port) '()) + (lambda (sxml port) (cons 'ul sxml)) + [(list '(#\newline (! #\* #\-) (! #\space)) + '(#\newline (! #\* #\-) ())) + (list 1 1)] + [(list '(#\newline) + '(#\* #\space) + '(#\- #\space)) + (list (aux-ignore 1) + aux-parse-ul-list + aux-parse-ul-list)]) + + (define-FA aux-parse-ul-list + (lambda (sxml port) '()) + (lambda (sxml port) `(li ,sxml)) + [(list '(#\newline)) + (list 0)] + [(list '(#\* #\space) + '(#\- #\space) + '(#\~ #\~) + '(#\$ #\$) + '(#\* #\*) + '(#\_ #\_) + '(#\*) + '(#\_) + '(#\`) + '(())) + (list (aux-ignore 2) + (aux-ignore 2) + pattern-parse-strike-through + pattern-parse-math + pattern-parse-strong + pattern-parse-strong + pattern-parse-em + pattern-parse-em + pattern-parse-inline-code + aux-common)]) + + (define-FA pattern-parse-ol-list + (lambda (sxml port) '()) + (lambda (sxml port) (cons 'ol sxml)) + [(list '(#\newline (! #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) (! #\.) (! #\space)) + '(#\newline (! #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) ())) + (list 1 1)] + [(list '(#\newline) + '((#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) #\. #\space)) + (list (aux-ignore 1) + aux-parse-ol-list)]) + + (define-FA aux-parse-ol-list + (lambda (sxml port) '()) + (lambda (sxml port) `(li ,sxml)) + [(list '(#\newline)) + (list 0)] + [(list '((#\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\0) #\. #\space) + '(#\~ #\~) + '(#\$ #\$) + '(#\* #\*) + '(#\_ #\_) + '(#\*) + '(#\_) + '(#\`) + '(())) + (list (aux-ignore 3) + pattern-parse-strike-through + pattern-parse-math + pattern-parse-strong + pattern-parse-strong + pattern-parse-em + pattern-parse-em + pattern-parse-inline-code + aux-common)]) + + ;; ====================== task list ================= + (define-FA pattern-parse-task-list + (lambda (sxml port) '()) + (lambda (sxml port) (cons 'ul sxml)) + [(list '(#\newline (! #\- #\*))) + (list 1)] + [(list '(#\newline) + '(#\- #\space #\[ (#\space) #\] #\space) + '(#\* #\space #\[ (#\space) #\] #\space) + '(#\* #\space #\[ (! #\space) #\] #\space) + '(#\- #\space #\[ (! #\space) #\] #\space)) + (list (aux-ignore 1) + aux-parse-task-list-unchecked + aux-parse-task-list-unchecked + aux-parse-task-list-checked + aux-parse-task-list-checked)]) + + (define-FA aux-parse-task-list-checked + (lambda (sxml port) '()) + (lambda (sxml port) `(li (input (@ (checked "") + (disabled "") + (type "checkbox"))) + ,sxml)) + [(list '(#\newline)) + (list 0)] + [(list '(#\\) + '(#\- #\space #\[ (! #\space) #\] #\space) + '(#\* #\space #\[ (! #\space) #\] #\space) + '(#\~ #\~) + '(#\* #\*) + '(#\_ #\_) + '(#\*) + '(#\_) + '(#\`) + '(())) + (list aux-escape + (aux-ignore 6) + (aux-ignore 6) + pattern-parse-strike-through + pattern-parse-strong + pattern-parse-strong + pattern-parse-em + pattern-parse-em + pattern-parse-inline-code + aux-common)]) + + (define-FA aux-parse-task-list-unchecked + (lambda (sxml port) '()) + (lambda (sxml port) `(li (input (@ (disabled "") + (type "checkbox"))) + ,sxml)) + [(list '(#\newline)) + (list 0)] + [(list '(#\\) + '(#\- #\space #\[ (#\space) #\] #\space) + '(#\* #\space #\[ (#\space) #\] #\space) + '(#\~ #\~) + '(#\* #\*) + '(#\_ #\_) + '(#\*) + '(#\_) + '(#\`) + '(())) + (list aux-escape + (aux-ignore 6) + (aux-ignore 6) + pattern-parse-strike-through + pattern-parse-strong + pattern-parse-strong + pattern-parse-em + pattern-parse-em + pattern-parse-inline-code + aux-common)]) + + ;;; =============== header =============== ;; return the header (define-FA pattern-parse-header - (lambda (sxml port) `(,sxml)) + (lambda (sxml port) '()) + (lambda (sxml port) sxml) [(list '(#\newline)) (list 1)] [(list '(#\#)) (list aux-parse-header)]) - (define (aux-parse-header sxml port) + (define (aux-parse-header sxml port . idle) (cond [(pattern-match '(#\newline) port) sxml] @@ -289,7 +451,6 @@ (list 'h2 (aux-parse-header sxml port))] [(pattern-match '(#\# #\# #\# #\space) port) (char-forward port 4) - (display "in 3\n") (list 'h3 (aux-parse-header sxml port))] [(pattern-match '(#\# #\# #\# #\# #\space) port) (char-forward port 5) @@ -305,6 +466,7 @@ ;; ================= block code ========================== ;; return the block code (define-FA pattern-parse-block-code + (lambda (sxml port) '()) (lambda (sxml port) `(pre (code ,(car sxml) ,(car (cdr sxml))))) ((list '(#\` #\` #\` #\newline)) (list 4)) ((list '(#\` #\` #\`) @@ -313,104 +475,94 @@ aux-parse-block-code-cont))) (define aux-parse-block-code-type - (case-lambda - [(sxml port) - (cond - [(context-return (lambda () `(@ (class ,(apply string sxml)))) - port '((#\newline)) '(1))] - [(pattern-match '(#\` #\` #\` #\space) port) - (char-forward port 4) - (aux-parse-block-code-type (scone sxml (read-char port)) port)] - [else (aux-parse-block-code-type (scone sxml (read-char port)) port)])])) + (lambda (sxml port . idle) + (cond + [(context-return (lambda () `(@ (class ,(apply string sxml)))) + port '((#\newline)) '(1))] + [(pattern-match '(#\` #\` #\` #\space) port) + (char-forward port 4) + (aux-parse-block-code-type (scone sxml (read-char port)) port)] + [(pattern-match '(#\` #\` #\`) port) + (char-forward port 3) + (aux-parse-block-code-type (scone sxml (read-char port)) port)] + [else (aux-parse-block-code-type (scone sxml (read-char port)) port)]))) (define aux-parse-block-code-cont - (case-lambda - [(sxml port) - (cond - [(pattern-match '(#\` #\` #\` #\newline) port) - sxml] - [else (aux-parse-block-code-cont - (scone sxml (read-char port)) port)])])) - + (lambda (sxml port . idle) + (cond + [(pattern-match '(#\` #\` #\` #\newline) port) + sxml] + [else (aux-parse-block-code-cont + (scone sxml (read-char port)) port)]))) ;; =================== inline code =========================== ;; return the code type (define-FA pattern-parse-inline-code + (aux-ignore 1) (lambda (sxml port) (cons 'code sxml)) - [(list '(() #\`)) (list 2)] - [(list '(#\`) - '(())) - (list (aux-ignore 1) - aux-parse-inline-code)]) - - (define-FA aux-parse-inline-code - (lambda (sxml port) (scone sxml (peek-char port))) - [(list '(() #\`)) (list 0)] - [(list '(#\\ ()) + [(list '(#\`)) (list 1)] + [(list '(#\\) '(())) (list aux-escape aux-common)]) ;; ==================== strong ============================= (define-FA pattern-parse-strong + (aux-ignore 2) (lambda (sxml port) (cons 'strong sxml)) - [(list '(() #\* #\*) - '(() #\_ #\_)) - (list 3 3)] [(list '(#\* #\*) - '(#\_ #\_) - '(())) - (list (aux-ignore 2) - (aux-ignore 2) - aux-parse-strong)]) - - (define-FA aux-parse-strong - (lambda (sxml port) (scone sxml (peek-char port))) - [(list '(() #\* #\*) - '(() #\_ #\_)) - (list 0 0)] + '(#\_ #\_)) + (list 2 2)] [(list '(#\\) '(())) (list aux-escape aux-common)]) - ;; ===================== em ================================ + ;; ===================== em ================================ ;; return the emphsis (define-FA pattern-parse-em + (aux-ignore 1) (lambda (sxml port) (cons 'em sxml)) - ((list '(() #\*) - '(() #\_)) - (list 2 2)) ((list '(#\*) - '(#\_) - '(())) - (list (aux-ignore 1) - (aux-ignore 1) - aux-parse-em))) - - (define-FA aux-parse-em - (lambda (sxml port) (scone sxml (peek-char port))) - ((list '(() #\*) - '(() #\_)) - (list 0 0)) + '(#\_)) + (list 1 1)) ((list '(#\\) '(())) (list aux-escape aux-common))) + ;; ====================== strike through ================================ + (define-FA pattern-parse-strike-through + (aux-ignore 2) + (lambda (sxml port) (list 'del sxml)) + [(list '(#\~ #\~)) (list 2)] + [(list '(#\\) + '(#\* #\*) + '(#\_ #\_) + '(#\*) + '(#\_) + '(#\`) + '(())) + (list aux-escape + pattern-parse-strong + pattern-parse-strong + pattern-parse-em + pattern-parse-em + pattern-parse-inline-code + aux-common)]) + ;; ====================== hr ================================== ;; return (hr) (define-FA pattern-parse-hr + (lambda (sxml port) '()) (lambda (sxml port) '(hr)) ((list '(#\newline)) '(1)) ((list '(())) - (list aux-parse-hr))) - - (define (aux-parse-hr sxml port) - (read-char port) '()) + (list aux-common))) ;; ====================== link ================================== (define-FA pattern-parse-link + (lambda (sxml port) '()) (lambda (sxml port) `(a ,(car (cdr sxml)) ,(car sxml))) ((list '(#\))) '(1)) ((list '(#\() @@ -418,7 +570,7 @@ (list aux-parse-link-end aux-parse-link-begin))) - (define (aux-parse-link-begin sxml port) + (define (aux-parse-link-begin sxml port . idle) (cond [(pattern-match '(#\[) port) (char-forward port 1) @@ -428,7 +580,7 @@ sxml] [else (aux-parse-link-begin (scone sxml (read-char port)) port)])) - (define (aux-parse-link-end sxml port) + (define (aux-parse-link-end sxml port . idle) (cond [(pattern-match '(#\() port) (char-forward port 1) @@ -440,6 +592,7 @@ ;; ======================= img ================================ ;; return an img (define-FA pattern-parse-img + (lambda (sxml port) '()) (lambda (sxml port) `(img ,(car (cdr sxml)) ,(car sxml))) ((list '(#\))) '(1)) ((list '(#\() @@ -447,7 +600,7 @@ (list aux-parse-img-end aux-parse-img-begin))) - (define (aux-parse-img-begin sxml port) + (define (aux-parse-img-begin sxml port . idle) (cond [(pattern-match '(#\! #\[) port) (char-forward port 2) @@ -457,7 +610,7 @@ sxml] [else (aux-parse-img-begin (scone sxml (read-char port)) port)])) - (define (aux-parse-img-end sxml port) + (define (aux-parse-img-end sxml port . idle) (cond [(pattern-match '(#\() port) (char-forward port 1) @@ -466,4 +619,12 @@ `(@ (src ,(apply string sxml)))] [else (aux-parse-img-end (scone sxml (read-char port)) port)])) + ;; ========== math block (need external support ======= + (define-FA pattern-parse-math + (aux-ignore 2) + (lambda (sxml port) (list 'span '(@ (class "math")) sxml)) + [(list '(#\$ #\$)) (list 2)] + [(list '(())) + (list aux-common)]) + ) diff --git a/melt/lib/string.ss b/melt/lib/string.ss new file mode 100644 index 0000000..0b7f575 --- /dev/null +++ b/melt/lib/string.ss @@ -0,0 +1,43 @@ +(library (melt lib string) + (export string-reverse + string-trim + string-split) + (import (scheme)) + + (define (string-reverse str) + (if (string? str) + (list->string (reverse (string->list str))) + (error str "should accept a string"))) + + (define (string-trim str . symbol) + (define (%%string-trim str-ls) + (if (not (null? str-ls)) + (if (char-whitespace? (car str-ls)) + (%%string-trim (cdr str-ls)) + str-ls) + (error str "string list is empty"))) + (cond + [(and (string? str) (null? symbol)) + (list->string (%%string-trim (string->list str)))] + [(and (string? str) (eq? (car symbol) 'pre)) + (string-trim str)] + [(and (string? str) (eq? (car symbol) 'suf)) + (string-reverse (string-trim (string-reverse str)))] + [(and (string? str) (eq? (car symbol) 'both)) + (string-reverse (string-trim (string-reverse (string-trim str))))])) + + ;; split string into a list of sub strings, seperated by ch + (define (string-split str ch) + (let ((len (string-length str))) + (letrec + ((split + (lambda (a b) + (cond + ((>= b len) (if (= a b) '() (cons (substring str a b) '()))) + ((char=? ch (string-ref str b)) (if (= a b) + (split (+ 1 a) (+ 1 b)) + (cons (substring str a b) (split b b)))) + (else (split a (+ 1 b))))))) + (split 0 0)))) + + ) diff --git a/melt/lib/sxml.ss b/melt/lib/sxml.ss new file mode 100644 index 0000000..5937332 --- /dev/null +++ b/melt/lib/sxml.ss @@ -0,0 +1,117 @@ +#!chezscheme +;; convert sxml to html +(library (melt lib sxml) + (export sxml->html + sxml->html-string) + (import (scheme) + (melt utils) + (melt srfi match)) + + + (define %void-elements + '[area + base + br + col + command + embed + hr + img + input + keygen + link + meta + param + source + track + wbr]) + + ;; Return #t if TAG is a void element. + (define (void-element? tag) + (pair? (memq tag %void-elements))) + + ;; something like '<' -> ≤ + ;; This need to be update from time to time + ;; --- mark --- + (define %escape-chars + (alist->hash-table + '((#\" . "quot") + (#\& . "amp") + (#\< . "lt") + (#\> . "gt")))) + + ;; Write the HTML escaped form of sxml to PORT. + ;; chars is a string + (define (string->escaped-html chars port) + (define (escape c) + (let ((escaped (hash-ref %escape-chars c))) + (if escaped + (format port "&~a;" escaped) + (display c port)))) + (string-for-each escape chars)) + + ;; Write the HTML escaped form of OBJ to PORT + (define (object->escaped-html obj port) + (string->escaped-html + (call-with-string-output-port + (lambda (x) (display obj x))) + port)) + + ;; Write the HTML escaped form of VALUE to PORT. + (define (attribute-value->html value port) + (if (string? value) + (string->escaped-html value port) + (object->escaped-html value port))) + + ;; Write ATTR and VALUE to PORT + ;; attr must be a symbol. the value could be string or symbol + (define (attribute->html attr value port) + (format port "~a=\"" attr) + (attribute-value->html value port) + (display #\" port)) + + (define (element->html tag attrs body port) + ;; Write the HTML TAG to PORT, where TAG has the attributes in the + ;; list ATTRS and the child nodes in BODY. + (format port "<~a" tag) + (for-each (match-lambda + ((attr value) + (display #\space port) + (attribute->html attr value port))) + attrs) + (if (and (null? body) (void-element? tag)) + (display "/>" port) + (begin + (display #\> port) + (for-each (lambda (x) + (sxml->html x port)) body) + (format port "" tag)))) + + (define (doctype->html doctype port) + (format port "" doctype)) + + (define (sxml->html tree port) + ;;Write the serialized HTML form of TREE to PORT. + (match tree + (() 'unspecified) + (('doctype type) + (doctype->html type port)) + (((? symbol? tag) ('@ attrs ...) body ...) + (element->html tag attrs body port)) + (((? symbol? tag) body ...) + (element->html tag '() body port)) + ((nodes ...) + (for-each (lambda (x) + (sxml->html x port)) nodes)) + ((? string? text) + (string->escaped-html text port)) + ;; Render arbitrary Scheme objects, too. + (obj (object->escaped-html obj port)))) + + ;; Render SXML as an HTML string. + (define (sxml->html-string sxml) + (call-with-string-output-port + (lambda (port) + (sxml->html sxml port)))) + + ) diff --git a/melt/lib/time.scm b/melt/lib/time.scm new file mode 100644 index 0000000..3cc0289 --- /dev/null +++ b/melt/lib/time.scm @@ -0,0 +1,46 @@ +(library (melt lib time) + (export date-and-time-string->date + month->number) + (import (scheme) + (melt lib string)) + + (define %%months-number-alist + '(("Jan" . 1) + ("Feb" . 2) + ("Mar" . 3) + ("Apr" . 4) + ("May" . 5) + ("Jun" . 6) + ("Jul" . 7) + ("Aug" . 8) + ("Set" . 9) + ("Oct" . 10) + ("Nov" . 11) + ("Dec" . 12))) + + ;; convert string like Dec to number + (define (month->number str) + (cdr (assp (lambda (obj) + (string=? obj str)) %%months-number-alist))) + + ;; convert (date-and-time) output string to a date + (define (date-and-time-string->date str) + (let* ((substr-ls (string-split str #\space)) + (mon (list-ref substr-ls 1)) + (day (list-ref substr-ls 2)) + (time (list-ref substr-ls 3)) + (year (list-ref substr-ls 4))) + (let* ((time-str-ls (string-split time #\:)) + (hour (list-ref time-str-ls 0)) + (minute (list-ref time-str-ls 1)) + (second (list-ref time-str-ls 2))) + (make-date 0 + (string->number second) + (string->number minute) + (string->number hour) + (string->number day) + (month->number mon) + (string->number year))))) + + ) + diff --git a/melt/page.scm b/melt/page.scm index 1469eaa..47ace57 100644 --- a/melt/page.scm +++ b/melt/page.scm @@ -1,39 +1,40 @@ -(library (melt page) +(library + (melt page) (export create-page - compose - create-writer - page-list-query) + compose + create-writer + page-list-query) (import (scheme) (melt srfi match) (melt parser sxml) (melt utils) - (melt lib sxml) - (melt lib file) + (melt lib sxml) + (melt lib file) (melt asset) - (melt renderer) + (melt renderer) (melt structure)) - + (import type-page) (define (create-page meta cont comt) - (make-page meta cont comt)) + (make-page meta cont comt)) (define (compose page renderer-list) - (let ((generate-sxml (page-cont page))) - (generate-sxml page renderer-list))) + (let ((generate-sxml (page-cont page))) + (generate-sxml page renderer-list))) (define (page-list-query key page-list) - (if (null? page-list) - #f - (if (eq? key (page-meta (car page-list))) - (car page-list) - (page-list-query key (cdr page-list))))) - + (if (null? page-list) + #f + (if (eq? key (page-meta (car page-list))) + (car page-list) + (page-list-query key (cdr page-list))))) + ;; convert the sxml to html and write it to a file (define (create-writer output-file-name) (lambda (sxml) (let ((port (open-output-file output-file-name 'replace))) (sxml->html sxml port) (close-output-port port)))) - + ) diff --git a/melt/parser.scm b/melt/parser.scm index cb65ce1..b9d225b 100644 --- a/melt/parser.scm +++ b/melt/parser.scm @@ -1,50 +1,61 @@ -(library (melt parser) +(library + (melt parser) (export parse - create-parser) + parse-with-parsers + create-parser) (import (scheme) (melt structure) - (melt lib console) + (melt lib console) (melt utils)) - + (import type-parser) ;; parser procedure for using parser to parse soucre file ;; and use refine procedure to update source file - (define (parse file-path parser) - (let ((filt (make-filter (symbol->string (parser-type parser))))) - (if (filt file-path) - (let ((proc (parser-proc parser)) - (refine (parser-refp parser))) - (define returned-value (proc file-path)) - (if refine (refine file-path)) - returned-value) - #f))) + (define (parse path parser) + (let ((filt (make-filter (symbol->string (parser-type parser))))) + (if (filt path) + (let ((proc (parser-proc parser)) + (refine (parser-refp parser))) + (define returned-value (proc path)) + (if refine (refine path)) + returned-value) + #f))) + + ;; almost same with parse but accept a parser list + ;; rely on parse + (define (parse-with-parsers path parser-list) + (call/cc + (lambda (cc) + (do ((parser-ls parser-list (cdr parser-ls))) + ((null? parser-ls) #f) + (let ((re-value (parse path (car parser-ls)))) + (if re-value (cc re-value))))))) ;; make file extension matcher ;; return the function which returns #t if the file's ;; extension is ext (define make-filter (lambda (ext) - (if (string? ext) - (lambda (path) - (string=? ext (path-extension path))) - (gem-display (gem "[38;5;160m" "error: ") - (gem "[38;5;112m" "in (melt parser): make-filter") - "ext must be string!\n")))) + (if (string? ext) + (lambda (path) + (string=? ext (path-extension path))) + (gemd:error + (string-append (gem:text "[38;5;112m" "in (melt parser): make-filter") + "ext must be string!"))))) ;; please use this function instead of make-parser (define create-parser - (case-lambda - [(type proc refp) - (if (symbol? type) - (make-parser type proc refp) - (begin - (gem-display (gem "[38;5;160m" "error: ") - (gem "[38;5;112m" "in (melt parser): create-parser") - "type must be symbol!\n") - (exit 1)))] - [(type proc) - (make-parser type proc #f)])) - - + (case-lambda + [(type proc refp) + (if (symbol? type) + (make-parser type proc refp) + (begin + (gemd:error (string-append (gem:text "[38;5;112m" "in (melt parser): create-parser") + "type must be symbol!")) + (exit 1)))] + [(type proc) + (make-parser type proc #f)])) + + ) diff --git a/melt/parser/markdown.scm b/melt/parser/markdown.scm index 66958a1..22de9cf 100644 --- a/melt/parser/markdown.scm +++ b/melt/parser/markdown.scm @@ -1,41 +1,44 @@ -(library (melt parser markdown) - (export markdown) - (import (scheme) - (melt parser) - (melt lib markdown) - (melt lib sxml) - (melt utils)) +(library + (melt parser markdown) + (export markdown + parse-markdown-attr) + (import (scheme) + (melt parser) + (melt lib markdown) + (melt lib sxml) + (melt lib string)) - (define (parse-markdown-attr port) - (define (get-attrs sxml port) - (if (pattern-match '(#\~ #\~ #\~ #\newline) port) - (begin (char-forward port 4) sxml) - (get-attrs (scone sxml (cons (string->symbol - (parse-key (list) port)) - (parse-value (list) port))) - port))) - ;; following procedure returns a string - (define (parse-key chars port) - (if (pattern-match '(#\:) port) - (begin (char-forward port 1) (string-trim (apply string chars) - 'both)) - (parse-key (scone chars (read-char port)) port))) - (define (parse-value chars port) - (if (pattern-match '(#\newline) port) - (begin (char-forward port 1) (string-trim (apply string chars) - 'both)) - (parse-value (scone chars (read-char port)) port))) + (define (parse-markdown-attr port) + (define (get-line-attrs port) + (if (pattern-match '(#\~ #\~ #\~ #\newline) port) + (begin (char-forward port 4) '()) + (let ((str-ls (string-split (get-line port) #\:))) + ;; this is for parsing date + (if (eq? (length str-ls) 4) + (cons (string->symbol (string-trim (car str-ls) 'both)) + (string-trim (string-append (list-ref str-ls 1) ":" + (list-ref str-ls 2) ":" + (list-ref str-ls 3)) + 'both)) + (cons (string->symbol (string-trim (car str-ls) 'both)) + (string-trim (apply string-append (cdr str-ls)) 'both)))))) - (if (pattern-match '(#\~ #\~ #\~ #\newline) port) - (begin (char-forward port 4) (get-attrs (list) port)) - (read-char port))) + (if (pattern-match '(#\~ #\~ #\~ #\newline) port) + (begin (char-forward port 4) + (do ((cur-attr (get-line-attrs port) (get-line-attrs port)) + (sxml '())) + ((null? cur-attr) sxml) + (set! sxml (scone sxml cur-attr)))) + (begin (read-char port) + (parse-markdown-attr port)))) - (define markdown - (create-parser 'md - (lambda (file-name) - (call-with-input-file file-name - (lambda (input-port) - (let* ((attrs (parse-markdown-attr input-port)) - (contents (markdown->sxml input-port))) - (list attrs contents))))))) - ) + (define markdown + (create-parser 'md + (lambda (file-name) + (call-with-input-file + file-name + (lambda (input-port) + (let* ((attrs (parse-markdown-attr input-port)) + (contents (markdown->sxml input-port))) + (list attrs contents))))))) + ) diff --git a/melt/post.ss b/melt/post.ss new file mode 100644 index 0000000..8e8c7eb --- /dev/null +++ b/melt/post.ss @@ -0,0 +1,33 @@ +(library + (melt post) + (export post-meta-query + post-attr-query + create-post + compose-post) + (import (scheme) + (melt structure) + (melt utils) + (melt data)) + + (import type-post) + + ;; accept two assoc list + (define create-post + (case-lambda + [(meta attr cont) + (make-post meta attr cont)])) + + ;; use assoc list to compose a post + (define (compose-post meta-alist attr-alist cont-sxml) + (make-post (create-data meta-alist) + (create-data attr-alist) + cont-sxml)) + + ;; query the data in post and return the value if exists + (define (post-meta-query key post) + (data-value-query key (post-meta post))) + + (define (post-attr-query key post) + (data-value-query key (post-attr post))) + + ) diff --git a/melt/renderer.scm b/melt/renderer.scm index 488cdd8..fb3ab7e 100644 --- a/melt/renderer.scm +++ b/melt/renderer.scm @@ -1,26 +1,26 @@ -(library (melt renderer) +(library + (melt renderer) (export create-renderer - update-renderer-data!) + update-renderer-data!) (import (scheme) - (melt structure) - (melt invoke) - (melt data)) + (melt structure) + (melt invoke) + (melt data)) (import type-renderer) - (define create-renderer - (case-lambda - [(type proc) - (make-renderer type proc (create-data))] - [(type proc data) - (make-renderer type proc data)])) + (case-lambda + [(type proc) + (make-renderer type proc (create-data))] + [(type proc data) + (make-renderer type proc data)])) (define update-renderer-data! - (lambda (renderer keys data) - (if (renderer? renderer) - (update-data! (renderer-data renderer) - data) - (error 'renderer "in update-renderer-data! : renderer must be a renderer type")))) - + (lambda (renderer keys data) + (if (renderer? renderer) + (update-data! (renderer-data renderer) + data) + (error 'renderer "in update-renderer-data! : renderer must be a renderer type")))) + ) diff --git a/melt/script/compile.ss b/melt/script/compile.ss new file mode 100644 index 0000000..78c4d70 --- /dev/null +++ b/melt/script/compile.ss @@ -0,0 +1,5 @@ +(define (compile-melt directory) + (if (file-directory? directory) + ())) + +(define (compile-melt-single )) diff --git a/melt/structure.ss b/melt/structure.ss new file mode 100644 index 0000000..1766f0f --- /dev/null +++ b/melt/structure.ss @@ -0,0 +1,158 @@ +(library + (melt structure) + (export type-parser + type-post + type-renderer + type-page + + type-site + type-asset + + type-trigger) + + (import (scheme)) + + ;; it now is an uniform utility! can be stroed in + ;; one place and use multiple times! + (module type-parser + [make-parser + parser? + parser-type + parser-proc + parser-refp] + (define-record-type + parser + (nongenerative melt-parser) + (fields + ;; the symbol which means the file type + ;; the symbol is used as file extension + (immutable type parser-type) + ;; proc is the procedure which take charge with + ;; the source file + (immutable proc parser-proc) + ;; refp==>refine procedure : update the resource file + ;; it need to be designed carefully, because it will alter + ;; the source file + (immutable refp parser-refp)))) + + ;; there maybe a lot of procedure between + ;; this two components. + + ;; the post recieve the data from parser + ;; and then process the data to satisfy its + ;; need. So the data stored in a post is all + ;; the data one post needs. No more change but + ;; use. + + ;; the meta and attr is all an assoc list + (module type-post + [make-post + post? + post-meta post-meta-set! + post-attr post-attr-set! + post-cont post-cont-set!] + (define-record-type + post + (nongenerative melt-post) + (fields + ;; it contains the attribute about the + ;; source file! + ;; the meta and attr are all the data + ;; but cont is sxml tree + (mutable meta post-meta post-meta-set!) + (mutable attr post-attr post-attr-set!) + (mutable cont post-cont post-cont-set!)))) + + ;; used to render the page component + (module type-renderer + [make-renderer + renderer? + renderer-type + renderer-proc proc-set! + renderer-data data-set!] + (define-record-type + renderer + (nongenerative melt-renderer) + (fields + ;; the type is an unique id to distinguish the render + (immutable type renderer-type) + ;; proc==>process process function used to render the + ;; page + (mutable proc renderer-proc proc-set!) + ;; data is the data which maybe be needed, it's the data type. + (mutable data renderer-data data-set!)))) + + ;; page is used to compose one page + ;; and use the proc to write ti to disk + ;; all the information relevant should + ;; be done before page, page only store + ;; information about the page it self. + (module type-page + [make-page + page? + page-meta page-meta-set! + page-cont page-cont-set! + page-comt page-comt-set!] + (define-record-type + page + (nongenerative melt-page) + (fields + ;; meta ==> store some useful value for the page + ;; cont ==> is the template for the page; actually it + ;; is a procedure accept itself a page obj, and generate + ;; sxml tree + ;; comt ==> it is a list of symbols map the renderer type + ;; need to be registered first + (mutable meta page-meta page-meta-set!) + (mutable cont page-cont page-cont-set!) + (mutable comt page-comt page-comt-set!)))) + + ;; site type is only for definition + (module type-site + [make-site + site? + site-layout layout-set! + site-comt comt-set! + site-attr attr-set!] + (define-record-type + site + (nongenerative melt-site) + (fields + ;; it stores data type data + ;; this defines how the published site to be generated! + (mutable layout site-layout layout-set!) + ;; comt==>component : it describes the composement of the + ;; site and the action on each component. for example: the site map + ;; it's also a data type + (mutable comt site-comt comt-set!) + ;; it is the attribute of the site like domain name + ;; it is a data type + (mutable attr site-attr attr-set!)))) + + (module type-asset + [make-asset + asset? + asset-source + asset-target] + (define-record-type + asset + (nongenerative melt-asset) + (fields + (immutable source asset-source) + (immutable target asset-target)))) + + ;; the trigger module for future + (module type-trigger + [make-trigger + trigger? + trigger-cond + trigger-act] + (define-record-type + trigger + (nongenerative melt-trigger) + (fields + (immutable cond trigger-cond) + (immutable act trigger-act)))) + + + ) diff --git a/melt/ui.scm b/melt/ui.scm index d8cad69..6d39897 100644 --- a/melt/ui.scm +++ b/melt/ui.scm @@ -1,98 +1,95 @@ #!chezscheme -(library (melt ui) - (export melt) - (import (scheme) - (melt utils) - (melt lib console) - (melt version) - (melt glob) - (melt command) - (melt structure) - (melt srfi match)) +(library + (melt ui) + (export melt) + (import (scheme) + (melt lib console) + (melt version) + (melt command) + (melt glob) + (melt cell) + (melt srfi match)) - (define (show-version) + (define (show-version) + (gemd:info (string-append + (gem:text "[37;1m" "melt") + (gem:text "[38;5;15m" " version ") + (gem:text "[38;5;165m" "0.0.5")) )) - (display (string-append (gem "[37;1m" "melt") - (gem "[38;5;15m" " version ") - (gem "[38;5;165m" "0.0.3") - "\n"))) + ;; the basic information + (define (introduction) + (gem:display (gem:text "[37m" "This is melt! Meta Excellent Local Note System.\n") + (gem:text "[37m" "Please use \"-h\" or \"--help\" to get further help.\nFor more information please follow") + (gem:text "[36m" " github io page.\n")) + (gemd:info "github blog https://haxpeta.github.io/Lago")) - ;; the basic information - (define (introduction) - (gem-display (gem "[37m" "This is melt! Meta Excellent Local Note System.\n") - (gem "[37m" "Please use \"-h\" or \"--help\" to get further help.\nFor more information please follow") - (gem "[36m" " github io page.\n"))) + ;; basic help information + (define (help) + (gem:display (gem:text "[37;1m" "melt ") + (gem:text "[38;5;102m" "[options] [command] [command options] \n")) + (gem:display (gem:text "[38;5;80m" "available options are :") + (gem:text "[38;5;111m" " -h | -v | -vs | -l\n"))) - ;; basic help information - (define (help) - (gem-display (gem "[37;1m" "melt ") - (gem "[38;5;102m" "[options] [command] [command options] \n")) - (gem-display (gem "[38;5;80m" "available options are :") - (gem "[38;5;111m" " -h | -v | -vs | -l\n"))) + ;; to structure commands + (define prepare + (make-cell + #t + (lambda (value) + (let ((melt-load (make-cell ".melt/config.scm" + (lambda (x) + (if (file-regular? x) + (begin (load x) #t) + (begin + (gemd:error (gem:text "[38;5;222m" "melt configure file doesn't exist!")) + #f)))))) + (if value + (if (melt-load) + (begin + (gemd:info "Available commands :") + (inter:show-commands) + (user:show-commands)) + (begin + (gemd:info "Available commands :") + (inter:show-commands))) + (melt-load)))))) - ;; to structure commands - (define (prepare flag) - (define (melt-load) - (if (file-exists? ".melt") - (cond - [(file-directory? ".melt") - (load ".melt/settings.scm") - #t] - [else #f]) - #f)) - - (if flag - (if (melt-load) - (begin - (gem-display (gem "[38;5;10m" "==========") - (gem "[38;5;142m" " Available commands :\n")) - (show-commands %builtin-commands) - (show-commands %user-commands)) - (begin - (gem-display (gem "[38;5;196m" "Error! ") - (gem "[38;5;222m" "melt configure file doesn't exist! Only show builtin commands\n")) - (gem-display (gem "[38;5;10m" "==========") - (gem "[38;5;142m" " Available commands :\n")) - (show-commands %builtin-commands))) - (melt-load))) + (define melt-scan (make-cell ".melt" (lambda (value) (if (file-exists? value) + #t + (begin (gemd:error (gem:text "[38;5;196m" "You are not in the root of working directory!")) #f))))) - ;; user interface - (define (melt arg . extra-args) - (match extra-args - [(or ("-h") ("--help")) - (help) - (exit 0)] - [(or ("-v") ("--version")) - (show-version) - (exit 0)] - [(or ("-vs") ("--version-history")) - (show-version-history) - (exit 0)] - [(or ("-l") ("--list")) - (prepare #t) - (exit 0)] - (else (if (null? extra-args) - (begin (introduction) - (exit 0))))) - (if (not (file-exists? ".melt")) - (gem-display (gem "[38;5;196m" "You are not in the root of working directory!\n"))) - (prepare #f) - (let ((command-built (command-query (string->symbol (car extra-args)) - %builtin-commands)) - (command-user (command-query (string->symbol (car extra-args)) - %user-commands))) - (cond - [command-built - (apply command-built - (cdr extra-args))] - [command-user - (apply command-user - (cdr extra-args))] - [else - (gem-display (gem "[38;5;99m" "Command not available!\n")) - (prepare #t)])) - ) + ;; user interface + (define (melt self . extra-args) + (match extra-args + [(or ("-h") ("--help")) + (help) + (exit 0)] + [(or ("-v") ("--version")) + (show-version) + (exit 0)] + [(or ("-vs") ("--version-history")) + (show-version-history) + (exit 0)] + [(or ("-l") ("--list")) + (begin (prepare #t) (prepare)) + (exit 0)] + ['() + (introduction) + (exit 0)] + [else (gemd:info "searching command ...")]) - - - ) + (melt-scan) + (prepare #f) + (prepare) + + (let ((inter-command (inter:command-query (string->symbol (car extra-args)))) + (user-command (user:command-query (string->symbol (car extra-args))))) + (cond + [inter-command + (apply inter-command (cdr extra-args))] + [user-command + (apply user-command (cdr extra-args))] + [else + (gemd:error (gem:text "[38;5;99m" "Command not available!")) + (begin (prepare #t) (prepare))]))) + + ) diff --git a/melt/utils.scm b/melt/utils.scm index f5546b2..3f9f6e5 100644 --- a/melt/utils.scm +++ b/melt/utils.scm @@ -1,204 +1,159 @@ -(library (melt utils) - (export get-absolute-path - decompose-path-name - compose-path-name - identity - basename - hash-ref - assq-ref - make-alist - alist->hash-table - alist-delete - alist? - alist-cons - string-split-dual - string-trim - list-directory - while) - (import (scheme) - (melt structure)) - - (define (make-alist keys values) - (map cons keys values)) - - ;; just like echo, return what it accept!! - (define identity - (lambda (obj) - obj)) - - ;; return the absolute path of the file - (define (get-absolute-path file-name) - (if (string? file-name) - (if (path-absolute? file-name) - file-name - (string-append (current-directory) "/" file-name)) - (error file-name "must be string!"))) - - ;; decompose a string - ;; example - ;; "/usr/lib/share" ==> ("usr" "lib" "share") - (define (decompose-path-name path-name) - (define (generate-list path) - (if (eq? path "") - '() - (if (eq? "" (path-first path)) - (cons (path-rest path) - '()) - (cons (path-first path) - (generate-list (path-rest path)))))) - (if (string? path-name) - (if (eq? "" path-name) - '() - (let ((name (if (path-absolute? path-name) - (path-rest path-name) - path-name))) - (generate-list name))) - (error path-name "must be string!"))) - - ;; define string join - (define (string-join str-list seperator command) - (define (join-seperator str seperator command) +(library + (melt utils) + (export get-absolute-path + flatten + decompose-path-name + compose-path-name + identity + hash-ref + assq-ref + make-alist + alist->hash-table + alist-delete + alist? + alist-cons + until) + (import (scheme) + (melt structure)) + + (define (make-alist keys values) + (map cons keys values)) + + (define (flatten x) + (cond ((null? x) '()) + ((pair? x) (append (flatten (car x)) (flatten (cdr x)))) + (else (list x)))) + + ;; just like echo, return what it accept!! + (define identity + (lambda (obj) + obj)) + + ;; return the absolute path of the file + (define (get-absolute-path file-name) + (if (string? file-name) + (if (path-absolute? file-name) + file-name + (string-append (current-directory) "/" file-name)) + (error file-name "must be string!"))) + + ;; decompose a string + ;; example + ;; "/usr/lib/share" ==> ("usr" "lib" "share") + (define (decompose-path-name path-name) + (define (generate-list path) + (if (eq? path "") + '() + (if (eq? "" (path-first path)) + (cons (path-rest path) + '()) + (cons (path-first path) + (generate-list (path-rest path)))))) + (if (string? path-name) + (if (eq? "" path-name) + '() + (let ((name (if (path-absolute? path-name) + (path-rest path-name) + path-name))) + (generate-list name))) + (error path-name "must be string!"))) + + ;; define string join + (define (string-join str-list seperator command) + (define (join-seperator str seperator command) + (cond + [(eq? command 'prefix) + (string-append seperator str)] + [(eq? command 'suffix) + (string-append str seperator)])) + (define (verify-string str-list) + (if (eq? str-list '()) + #f + (if (string? (car str-list)) + (if (eq? '() (cdr str-list)) + #t + (verify-string (cdr str-list))) + #f))) + + (cond + [(atom? str-list) + (if (eq? '() str-list) + (error str-list "Empty list!") + (error str-list "Must be a list!"))] + [(verify-string str-list) + (if (or (string? seperator) + (char? seperator)) + (let ((sep (if (char? seperator) + (string seperator) + seperator))) (cond - [(eq? command 'prefix) - (string-append seperator str)] - [(eq? command 'suffix) - (string-append str seperator)])) - (define (verify-string str-list) - (if (eq? str-list '()) - #f - (if (string? (car str-list)) - (if (eq? '() (cdr str-list)) - #t - (verify-string (cdr str-list))) - #f))) - - (cond - [(atom? str-list) - (if (eq? '() str-list) - (error str-list "Empty list!") - (error str-list "Must be a list!"))] - [(verify-string str-list) - (if (or (string? seperator) - (char? seperator)) - (let ((sep (if (char? seperator) - (string seperator) - seperator))) - (cond - [(or (eq? command 'prefix) - (eq? command 'suffix)) - (let* ((number (length str-list)) - (new-list (map join-seperator str-list (make-list number sep) (make-list number command)))) - (apply string-append new-list))] - [(eq? command 'middle) - (let* ((number (- (length str-list) 1)) - (new-list (map join-seperator (cdr str-list) (make-list number sep) (make-list number 'suffix)))) - (apply string-append (cons (car str-list) - new-list)))] - [else (error command "isn't a proper command!")])) - (error seperator "isn't a string or char!"))])) - - ;; components is a list of strings - ;; like ("hello" "nice" "good") => /hello/nice/good - (define (compose-path-name str-list) - (string-join str-list "/" 'prefix)) - - ;; while the test is satified, end the iterate - (define-syntax while - (syntax-rules () - [(_ test forms ...) - (do () - (test) - forms ...)])) - - (define alist->hash-table - (lambda (alist) - (let ((ht (make-eqv-hashtable))) - (do ((iterate-alist alist (cdr iterate-alist)) - (pair-list (car alist) (car iterate-alist))) - ((eq? iterate-alist '()) ht) - (hashtable-set! ht - (car pair-list) - (cdr pair-list)))))) - - (define hash-ref - (case-lambda - [(hashtable key) - (hashtable-ref hashtable key #f)] - [(hashtable key default) - (hashtable-ref hashtable key default)])) - - ;; return the left alist which dosn't contain the symbal - (define alist-delete - (lambda (symbal alist) - (if (assq symbal alist) - (remove (assq symbal alist) alist) - (error symbal "symbol is not in alist! : in alist-delete utils.scm \n")))) - - (define assq-ref - (lambda (symbol alist) - (cdr (assq symbol alist)))) - - (define basename - (lambda (path) - (path-last (path-root path)))) - - (define string-split-dual - (lambda (arg-string char) - (define first-position - (lambda (arg-char-list char) - (do ((char-list arg-char-list (cdr char-list)) - (end? #f) - (number 0 (+ 1 number))) - (end? (- number 1)) - (if (eq? (car char-list) - char) - (set! end? #t))))) ;; zero based - (let* ((char-list (string->list arg-string)) - (position (first-position char-list char))) - (list (apply string (list-head char-list position)) - (apply string (list-tail char-list (+ 1 position))))))) - - (define string-trim - (lambda (arg-string symbol) - (cond - [(eq? symbol 'prefix) - (do ((string-list (string->list arg-string)) - (end? #f)) - [end? (apply string string-list)] - (if (equal? (car string-list) - #\space) - (set! string-list (cdr string-list)) - (set! end? #t)))] - [(eq? symbol 'suffix) - (apply string (reverse (string->list (string-trim (apply string (reverse (string->list arg-string))) 'prefix))))] - [(eq? symbol 'both) - (string-trim (string-trim arg-string 'prefix) - 'suffix)] - [else (error symbol "Not proper symbol!")]))) - - - (define alist-cons - (lambda (key obj alist) - (cons (cons key obj) - alist))) - - (define list-directory - (lambda path - (cond - [(null? path) - (directory-list (cd))] - [else (map directory-list path)]))) - - ;; need to be refined!!!! - (define (alist? arg) - (call/cc - (lambda (cc) - (if (atom? arg) - (cc #f)) - (do ((arg-list arg (cdr arg-list))) - ((null? arg-list) #t) - (if (pair? (car arg-list)) - #t - (cc #f))))) - ) - ) + [(or (eq? command 'prefix) + (eq? command 'suffix)) + (let* ((number (length str-list)) + (new-list (map join-seperator str-list (make-list number sep) (make-list number command)))) + (apply string-append new-list))] + [(eq? command 'middle) + (let* ((number (- (length str-list) 1)) + (new-list (map join-seperator (cdr str-list) (make-list number sep) (make-list number 'suffix)))) + (apply string-append (cons (car str-list) + new-list)))] + [else (error command "isn't a proper command!")])) + (error seperator "isn't a string or char!"))])) + + ;; components is a list of strings + ;; like ("hello" "nice" "good") => /hello/nice/good + (define (compose-path-name str-list) + (string-join str-list "/" 'prefix)) + + ;; until the test is satified, end the iterate + (define-syntax until + (syntax-rules () + [(_ test forms ...) + (do () + (test) + forms ...)])) + + (define alist->hash-table + (lambda (alist) + (let ((ht (make-eqv-hashtable))) + (do ((iterate-alist alist (cdr iterate-alist)) + (pair-list (car alist) (car iterate-alist))) + ((eq? iterate-alist '()) ht) + (hashtable-set! ht + (car pair-list) + (cdr pair-list)))))) + + (define hash-ref + (case-lambda + [(hashtable key) + (hashtable-ref hashtable key #f)] + [(hashtable key default) + (hashtable-ref hashtable key default)])) + + ;; return the left alist which dosn't contain the symbal + (define alist-delete + (lambda (symbal alist) + (if (assq symbal alist) + (remove (assq symbal alist) alist) + (error symbal "symbol is not in alist! : in alist-delete utils.scm \n")))) + + (define assq-ref + (lambda (symbol alist) + (cdr (assq symbol alist)))) + + (define alist-cons + (lambda (key obj alist) + (cons (cons key obj) + alist))) + + ;; take '() as an alist + (define (alist? arg) + (call/cc + (lambda (cc) + (if (and (atom? arg) (not (null? arg))) + (cc #f)) + (do ((arg-list arg (cdr arg-list))) + ((null? arg-list) #t) + (if (not (pair? (car arg-list))) + (cc #f)))))) + ) diff --git a/melt/uutil.scm b/melt/uutil.scm new file mode 100644 index 0000000..d3e6e76 --- /dev/null +++ b/melt/uutil.scm @@ -0,0 +1,66 @@ +(library + (melt uutil) + (export parse-posts + get-md-attr + get-md-title + get-md-post-title-list + scone!) + (import (scheme) + (melt post) + (melt lib file) + (melt lib console) + (melt parser markdown) + (melt data) + (melt utils) + (melt config) + (melt parser)) + + (define-syntax scone! + (syntax-rules () + [(_ ls item) + (set! ls (append ls (if (not (null? item)) (list item) '())))])) + + ;; parse a directory and return a list of posts + (define (parse-posts path parser-list) + (let ((dir-ls (directory-list path)) + (post-ls '())) + (do ((obj-ls (map string-append + (make-list (length dir-ls) (string-append path (directory-separator-string))) + dir-ls) + (cdr obj-ls))) + ((null? obj-ls) (flatten post-ls)) + (if (file-directory? (car obj-ls)) + (scone! post-ls (parse-posts (car obj-ls) parser-list)) + (let ((raw-sxml (parse-with-parsers (car obj-ls) parser-list))) + (if raw-sxml + (scone! post-ls (compose-post + `((path . ,(car obj-ls)) + (name . ,(path-last (path-root (car obj-ls))))) + (car raw-sxml) + (car (cdr raw-sxml)))))))))) + + ;; return the assoc list of a markdown post + (define (get-md-attr file-name) + (call-with-input-file file-name + (lambda (port) + (parse-markdown-attr port)))) + + ;; return the markdown post title, title is a string + (define (get-md-title file-name) + (cdr (assq 'title (get-md-attr file-name)))) + + ;; accept a directory and return pairs like (file-name . post-title) + (define (get-md-post-title-list pdirectory) + (let ((post-title-list '()) + (items (directory-list pdirectory))) + (do ((files (map string-append + (make-list (length items) (string-append pdirectory "/")) + items) + (cdr files))) + ((null? files) post-title-list) + (if (file-regular? (car files)) + (scone! post-title-list `(,(path-root (path-last (car files))) . ,(get-md-title (car files)))))))) + + + + ) diff --git a/melt/version.ss b/melt/version.ss new file mode 100644 index 0000000..3a1f8a2 --- /dev/null +++ b/melt/version.ss @@ -0,0 +1,41 @@ +#!chezscheme +(library + (melt version) + (export show-version-history) + (import (scheme) + (melt cell) + (melt lib console)) + + (define (show-version-history) + (do ((cur-list version-history (cdr cur-list))) + ((null? cur-list) #t) + (let ((ver (car cur-list))) + (gem:format "Melt ~a --- ~a~%" + (list (begin (output-ver (car ver)) (output-ver)) + (begin (output-desc (cdr ver)) (output-desc))))))) + + (define output-ver + (make-cell (void) + (lambda (str) + `("[37m" . ,str)))) + + (define output-desc + (make-cell (void) + (lambda (str) + `("[38;5;220m" . ,str)))) + + ;; there are three numbers in the version string X. Y. Z + ;; -- the X is the Milestone , when it updates, this means + ;; it now is a new release and it may be not compatibale with + ;; previous release. + ;; -- the Y is the stable release during X, add some functions or bug fix. + ;; -- the Z is the trial number. Each tested feature will be in here. + + (define version-history + '[("0.0.1" . "navigate to chezscheme!") + ("0.0.2" . "add markdown lib! now it will work!") + ("0.0.3" . "add markdown parser and sxml parser!") + ("0.0.4" . "refine console and data, also add cell") + ("0.0.5" . "refine sub command system, refine glob data")]) + + )