Show More
@@ -0,0 +1,1 b'' | |||
|
1 | error |
@@ -1,19 +1,23 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | 4 | (defparameter *languages* (list "en" "ru")) |
|
5 | 5 | |
|
6 | 6 | (defparameter *l10n-strings* |
|
7 | 7 | (mapcan (lambda (lang) |
|
8 | 8 | (cons (intern (string-upcase lang) :keyword) |
|
9 | (read-code-from-string | |
|
10 |
( |
|
|
11 | (concatenate 'string "strings/" lang ".sexp"))))) | |
|
9 | (list (apply #'append | |
|
10 | (read-code-from-string | |
|
11 | (load-src | |
|
12 | (concatenate 'string "strings/" lang ".sexp"))))))) | |
|
12 | 13 | *languages*)) |
|
13 | 14 | |
|
14 | 15 | (defun lformat (target key &rest args) |
|
15 | 16 | (let* ((lang (intern (string-upcase (first (system-locale:languages))) |
|
16 | 17 | :keyword)) |
|
17 | 18 | (strings (or (getf *l10n-strings* lang) |
|
18 |
(getf *l10n-strings* :en))) |
|
|
19 |
( |
|
|
19 | (getf *l10n-strings* :en))) | |
|
20 | (string (getf strings key))) | |
|
21 | (if string | |
|
22 | (apply #'format target string args) | |
|
23 | (error "Unknown localization string ~S for language ~S" key lang)))) |
@@ -1,153 +1,158 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | 4 | (defvar *app-name* "") |
|
5 | 5 | |
|
6 | 6 | (defun entry-point-no-args () |
|
7 | (setf *delivered* t) | |
|
7 | 8 | (entry-point uiop:*command-line-arguments*)) |
|
8 | 9 | |
|
9 | 10 | (defun entry-point (args) |
|
10 | 11 | (setf *app-name* (uiop:argv0)) |
|
11 | 12 | (let ((*package* (find-package :txt2web))) |
|
12 | 13 | (catch :terminate |
|
13 | 14 | (let ((compiler (apply #'make-instance 'compiler (parse-opts args)))) |
|
14 | 15 | (write-compiled-file compiler)))) |
|
15 | 16 | (values)) |
|
16 | 17 | |
|
17 | 18 | (defun parse-opts (args) |
|
18 | 19 | (let ((mode :sources) |
|
19 | 20 | (data (list :sources nil :target nil :js nil :css nil :body nil :compile nil :beautify nil))) |
|
20 | 21 | (loop :for arg :in args |
|
21 | 22 | :do (alexandria:switch (arg :test #'string=) |
|
22 | 23 | ("-o" (setf mode :target)) |
|
23 | 24 | ("--js" (setf mode :js)) |
|
24 | 25 | ("--css" (setf mode :css)) |
|
25 | 26 | ("--body" (setf mode :body)) |
|
26 | 27 | ("-c" (setf (getf data :compile) t)) |
|
27 | 28 | ("--beautify" (setf (getf data :beautify) t)) |
|
28 | 29 | (t (push arg (getf data mode))))) |
|
29 | 30 | (unless (< 0 (length (getf data :sources))) |
|
30 | 31 | (report-error "There should be at least one source")) |
|
31 | 32 | (unless (> 1 (length (getf data :target))) |
|
32 | 33 | (report-error "There should be no more than one target")) |
|
33 | 34 | (unless (> 1 (length (getf data :body))) |
|
34 | 35 | (report-error "There should be no more than one body")) |
|
35 | 36 | (unless (getf data :target) |
|
36 | 37 | (setf (getf data :target) |
|
37 | 38 | (let* ((sources (first (getf data :sources))) |
|
38 | 39 | (tokens (uiop:split-string sources :separator ".")) |
|
39 | 40 | (target (format nil "~{~A~^.~}.html" |
|
40 | 41 | (butlast tokens)))) |
|
41 | 42 | (list target)))) |
|
42 | 43 | (list :sources (getf data :sources) |
|
43 | 44 | :target (first (getf data :target)) |
|
44 | 45 | :js (getf data :js) |
|
45 | 46 | :css (getf data :css) |
|
46 | 47 | :body (first (getf data :body)) |
|
47 | 48 | :compile (getf data :compile) |
|
48 | 49 | :beautify (getf data :beautify)))) |
|
49 | 50 | |
|
50 | 51 | (defun print-usage () |
|
51 | 52 | (lformat t :usage *app-name*)) |
|
52 | 53 | |
|
53 | 54 | (defun parse-file (filename) |
|
54 | (p:parse 'txt2web-grammar | |
|
55 | (alexandria:read-file-into-string filename))) | |
|
55 | (handler-case | |
|
56 | (p:parse 'txt2web-grammar | |
|
57 | (alexandria:read-file-into-string filename)) | |
|
58 | (p:esrap-parse-error (e) | |
|
59 | (format t "~A~%" e) | |
|
60 | (uiop:quit 1)))) | |
|
56 | 61 | |
|
57 | 62 | (defun report-error (fmt &rest args) |
|
58 | 63 | (format t "ERROR: ~A~%" (apply #'format nil fmt args)) |
|
59 | 64 | (print-usage) |
|
60 | 65 | (throw :terminate nil)) |
|
61 | 66 | |
|
62 | 67 | ;;; JS |
|
63 | 68 | |
|
64 | 69 | (defun minify-package (package-designator minify prefix) |
|
65 | 70 | (setf (ps:ps-package-prefix package-designator) prefix) |
|
66 | 71 | (if minify |
|
67 | 72 | (ps:obfuscate-package package-designator) |
|
68 | 73 | (ps:unobfuscate-package package-designator))) |
|
69 | 74 | |
|
70 | 75 | (defmethod js-sources ((compiler compiler)) |
|
71 | 76 | (let ((ps:*ps-print-pretty* (beautify compiler))) |
|
72 | 77 | (cond ((beautify compiler) |
|
73 | 78 | (minify-package "TXT2WEB.MAIN" nil "qsp_") |
|
74 | 79 | (minify-package "TXT2WEB.API" nil "qsp_api_") |
|
75 | 80 | (minify-package "TXT2WEB.LIB" nil "qsp_lib_")) |
|
76 | 81 | (t |
|
77 | 82 | (minify-package "TXT2WEB.MAIN" t "_") |
|
78 | 83 | (minify-package "TXT2WEB.API" t "a_") |
|
79 | 84 | (minify-package "TXT2WEB.LIB" t "l_"))) |
|
80 | 85 | (format nil "~{~A~^~%~%~}" (mapcar #'ps:ps* (reverse (js compiler)))))) |
|
81 | 86 | |
|
82 | 87 | ;;; CSS |
|
83 | 88 | |
|
84 | 89 | (defmethod css-sources ((compiler compiler)) |
|
85 | 90 | (format nil "~{~A~^~%~%~}" (css compiler))) |
|
86 | 91 | |
|
87 | 92 | ;;; HTML |
|
88 | 93 | |
|
89 | 94 | (defmethod html-sources ((compiler compiler)) |
|
90 | 95 | (let ((flute:*escape-html* nil) |
|
91 | 96 | (body-template (body compiler)) |
|
92 | 97 | (js (js-sources compiler)) |
|
93 | 98 | (css (css-sources compiler))) |
|
94 | 99 | (with-output-to-string (out) |
|
95 | 100 | (write |
|
96 | 101 | (flute:h |
|
97 | 102 | (html |
|
98 | 103 | (head |
|
99 | 104 | (title "txt2web")) |
|
100 | 105 | (body |
|
101 | 106 | body-template |
|
102 | 107 | (style css) |
|
103 | 108 | (script js)))) |
|
104 | 109 | :stream out |
|
105 | 110 | :pretty nil)))) |
|
106 | 111 | |
|
107 | 112 | (defun filename-game (filename) |
|
108 | 113 | (let ((filename (alexandria:lastcar (uiop:split-string filename :separator "/")))) |
|
109 | 114 | (format nil "~{~A~^.~}" (butlast (uiop:split-string filename :separator "."))))) |
|
110 | 115 | |
|
111 | 116 | (defmethod initialize-instance ((compiler compiler) &key sources ((:js js-files)) ((:css css-files)) ((:body body-file)) compile &allow-other-keys) |
|
112 | 117 | (call-next-method) |
|
113 | 118 | (with-slots (body css js) |
|
114 | 119 | compiler |
|
115 | 120 | ;; Compile the game's JS |
|
116 | 121 | (dolist (source sources) |
|
117 | 122 | (let ((ps (parse-file source)) |
|
118 | 123 | (game-name (filename-game source))) |
|
119 | 124 | (destructuring-bind (kw &rest locations) |
|
120 | 125 | ps |
|
121 | 126 | (unless (eq kw 'lib:game) |
|
122 | 127 | (report-error "Internal error!")) |
|
123 | 128 | (push |
|
124 | 129 | `(lib:game (,game-name) ,@locations) |
|
125 | 130 | js)))) |
|
126 | 131 | ;; Does the user need us to do anything else |
|
127 | 132 | (unless compile |
|
128 | 133 | ;; Read in body |
|
129 | 134 | (when body-file |
|
130 | 135 | (setf body |
|
131 | 136 | (alexandria:read-file-into-string body-file))) |
|
132 | 137 | ;; Include js files |
|
133 | 138 | (dolist (js-file js-files) |
|
134 | 139 | (push (format nil "////// Included file ~A~%~A" js-file |
|
135 | 140 | (alexandria:read-file-into-string js-file)) |
|
136 | 141 | js)) |
|
137 | 142 | ;; Include css files |
|
138 | 143 | (when css-files |
|
139 | 144 | ;; User option overrides the default css |
|
140 | 145 | (setf css nil) |
|
141 | 146 | (dolist (css-file css-files) |
|
142 | 147 | (push (format nil "////// Included file ~A~%~A" css-file |
|
143 | 148 | (alexandria:read-file-into-string css-file)) |
|
144 | 149 | css)))))) |
|
145 | 150 | |
|
146 | 151 | (defmethod write-compiled-file ((compiler compiler)) |
|
147 | 152 | (alexandria:write-string-into-file |
|
148 | 153 | (if (compile-only compiler) |
|
149 | 154 | ;; Just the JS |
|
150 | 155 | (js-sources compiler) |
|
151 | 156 | ;; All of it |
|
152 | 157 | (html-sources compiler)) |
|
153 | 158 | (target compiler) :if-exists :supersede)) |
@@ -1,74 +1,131 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package parenscript) |
|
3 | 3 | |
|
4 | 4 | ;;; async/await |
|
5 | 5 | |
|
6 | 6 | (defprinter ps-js::await (x) |
|
7 | 7 | (psw (string-downcase "await ")) |
|
8 | 8 | (print-op-argument 'ps-js::await x)) |
|
9 | 9 | |
|
10 | 10 | (define-trivial-special-ops await ps-js::await) |
|
11 | 11 | |
|
12 | 12 | (define-statement-operator async-defun (name lambda-list &rest body) |
|
13 | 13 | (multiple-value-bind (effective-args body-block docstring) |
|
14 | 14 | (compile-named-function-body name lambda-list body) |
|
15 | 15 | (list 'ps-js::async-defun name effective-args docstring body-block))) |
|
16 | 16 | |
|
17 | 17 | (defprinter ps-js::async-defun (name args docstring body-block) |
|
18 | 18 | (when docstring (print-comment docstring)) |
|
19 | 19 | (psw "async ") |
|
20 | 20 | (print-fun-def name args body-block)) |
|
21 | 21 | |
|
22 | 22 | (define-expression-operator async-lambda (lambda-list &rest body) |
|
23 | 23 | (multiple-value-bind (effective-args effective-body) |
|
24 | 24 | (parse-extended-function lambda-list body) |
|
25 | 25 | `(ps-js::async-lambda |
|
26 | 26 | ,effective-args |
|
27 | 27 | ,(let ((*function-block-names* ())) |
|
28 | 28 | (compile-function-body effective-args effective-body))))) |
|
29 | 29 | |
|
30 | 30 | (defprinter ps-js::async-lambda (args body-block) |
|
31 | 31 | (psw "async ") |
|
32 | 32 | (print-fun-def nil args body-block)) |
|
33 | 33 | |
|
34 | 34 | (cl:export 'await) |
|
35 | 35 | (cl:export 'async-defun) |
|
36 | 36 | (cl:export 'async-lambda) |
|
37 | 37 | |
|
38 | 38 | ;;; ES6 |
|
39 | 39 | |
|
40 | 40 | (define-expression-operator => (lambda-list &rest body) |
|
41 | 41 | (unless (listp lambda-list) |
|
42 | 42 | (setf lambda-list (list lambda-list))) |
|
43 | 43 | (multiple-value-bind (effective-args effective-body) |
|
44 | 44 | (parse-extended-function lambda-list body) |
|
45 | 45 | `(ps-js::=> |
|
46 | 46 | ,effective-args |
|
47 | 47 | ,(let ((*function-block-names* ())) |
|
48 | 48 | (compile-function-body effective-args effective-body))))) |
|
49 | 49 | |
|
50 | 50 | (defprinter ps-js::=> (args body) |
|
51 | 51 | (unless (= 1 (length args)) |
|
52 | 52 | (psw "(")) |
|
53 | 53 | (loop for (arg . remaining) on args do |
|
54 | 54 | (psw (symbol-to-js-string arg)) (when remaining (psw ", "))) |
|
55 | 55 | (unless (= 1 (length args)) |
|
56 | 56 | (psw ")")) |
|
57 | 57 | (psw " => ") |
|
58 | 58 | (ps-print body)) |
|
59 | 59 | |
|
60 | 60 | (cl:export '=>) |
|
61 | 61 | |
|
62 | 62 | ;;; Actually return nothing (with no empty return) |
|
63 | 63 | (defvar *old-return-result-of* (function return-result-of)) |
|
64 | 64 | |
|
65 | 65 | (defun return-result-of (tag form) |
|
66 | 66 | (if (equal form '(void)) |
|
67 | 67 | nil |
|
68 | 68 | (funcall *old-return-result-of* tag form))) |
|
69 | 69 | (cl:export 'void) |
|
70 | 70 | |
|
71 | 71 | ;;; Bitwise stuff |
|
72 | 72 | ;; No idea why these are not exported |
|
73 | 73 | (export '<<) |
|
74 | 74 | (export '>>) |
|
75 | ||
|
76 | (in-package esrap) | |
|
77 | (defmethod print-object :around ((condition esrap-error) stream) | |
|
78 | (when (not txt2web::*delivered*) | |
|
79 | (return-from print-object | |
|
80 | (call-next-method))) | |
|
81 | (when (or *print-escape* | |
|
82 | *print-readably* | |
|
83 | (and *print-lines* (<= *print-lines* 5))) | |
|
84 | (return-from print-object)) | |
|
85 | ||
|
86 | ;; FIXME: this looks like it won't do the right thing when used as | |
|
87 | ;; part of a logical block. | |
|
88 | (if-let ((text (esrap-error-text condition)) | |
|
89 | (position (esrap-error-position condition))) | |
|
90 | (labels ((safe-index (index) | |
|
91 | (min (max index 0) (length text))) | |
|
92 | (find-newline (&key (start 0) (end (length text)) (from-end t)) | |
|
93 | (let ((start (safe-index start)) | |
|
94 | (end (safe-index end))) | |
|
95 | (cond | |
|
96 | ((when-let ((position (position #\Newline text | |
|
97 | :start start :end end | |
|
98 | :from-end from-end))) | |
|
99 | (1+ position))) | |
|
100 | ((and from-end (zerop start)) | |
|
101 | start) | |
|
102 | ((and (not from-end) (= end (length text))) | |
|
103 | end))))) | |
|
104 | ;; FIXME: magic numbers | |
|
105 | (let* ((line (count #\Newline text :end position)) | |
|
106 | (column (- position (or (find-newline :end position) 0) 1)) | |
|
107 | (min-start (- position 160)) | |
|
108 | (max-end (+ position 24)) | |
|
109 | (line-start (or (find-newline :start min-start | |
|
110 | :end position) | |
|
111 | (safe-index min-start))) | |
|
112 | (start (cond | |
|
113 | ((= (safe-index min-start) line-start) | |
|
114 | line-start) | |
|
115 | ((find-newline :start min-start | |
|
116 | :end (1- line-start))) | |
|
117 | (t | |
|
118 | ||
|
119 | line-start))) | |
|
120 | (end (or (find-newline :start position | |
|
121 | :end max-end | |
|
122 | :from-end nil) | |
|
123 | (safe-index max-end))) | |
|
124 | (*print-circle* nil)) | |
|
125 | (txt2web::lformat stream :error | |
|
126 | (= position (length text)) | |
|
127 | (list (subseq text start end)) | |
|
128 | (- position line-start) | |
|
129 | (1+ line) (1+ column) position))) | |
|
130 | ||
|
131 | (format stream "~2&<text and position not available>~2%"))) |
@@ -1,24 +1,26 b'' | |||
|
1 | 1 | |
|
2 | 2 | (in-package txt2web) |
|
3 | 3 | |
|
4 | (defvar *delivered* nil) | |
|
5 | ||
|
4 | 6 | (defun src-file (filename) |
|
5 | 7 | (uiop/pathname:merge-pathnames* |
|
6 | 8 | filename |
|
7 | 9 | (asdf:system-source-directory :txt2web))) |
|
8 | 10 | |
|
9 | 11 | (defun read-progn-from-string (string) |
|
10 | 12 | `(progn |
|
11 | 13 | ,@(read-code-from-string string))) |
|
12 | 14 | |
|
13 | 15 | (defun read-code-from-string (string) |
|
14 | 16 | (with-input-from-string (in string) |
|
15 | 17 | (let ((*package* *package*)) |
|
16 | 18 | (loop :for form := (read in nil :eof) |
|
17 | 19 | :until (eq form :eof) |
|
18 | 20 | :when (eq (first form) 'cl:in-package) |
|
19 | 21 | :do (setf *package* (find-package (second form))) |
|
20 | 22 | :else |
|
21 | 23 | :collect form)))) |
|
22 | 24 | |
|
23 | 25 | (defun load-src (filename) |
|
24 | 26 | (alexandria:read-file-into-string (src-file filename))) |
@@ -1,12 +1,15 b'' | |||
|
1 | 1 | (:usage "Usage: ~A <source> [options] |
|
2 | 2 | Options: |
|
3 | 3 | -o <filename> - Output filename |
|
4 | 4 | --js <filenames...> - List of extra .js files to include in the game |
|
5 | 5 | --css <filenames...> - List of .css files to include in the game. Default is in extras/default.css |
|
6 | 6 | --body <filename> - Alternative page body. Default is in extras/body.html |
|
7 | 7 | |
|
8 | 8 | -c - Just compile the game to a .js file without making it a full web page |
|
9 | 9 | --beautify - Make the JS content pretty. By default it gets minified. |
|
10 | 10 | |
|
11 | 11 | Note that the files in extras/ are not actually used. They're just there for the reference~%") |
|
12 | 12 | |
|
13 | (:error "Error at~:[~; end of input~]~2%~ | |
|
14 | ~2@T~<~@;~A~:>~%~ | |
|
15 | ~2@T~V@T^ (Line ~D, Column ~D, Position ~D)~2%") No newline at end of file |
@@ -1,12 +1,15 b'' | |||
|
1 | 1 | (:usage "Использование: ~A <source> [options] |
|
2 | 2 | Опции: |
|
3 | 3 | -o <имя файла> - Имя .html файла для записи скомпилированной игры |
|
4 | 4 | --js <имена файлов...> - Список дополнительных .js файлов |
|
5 | 5 | --css <имена файлов...> - Список .css файлов. Стиль по-умолчанию - в файле extras/default.css |
|
6 | 6 | --body <имя файла> - Альтернативное тело страницы. Тело по-умолчанию - в файле extras/body.html |
|
7 | 7 | |
|
8 | 8 | -c - Просто скомпилировать игру в .js файл, не компонуя полную .html страницу |
|
9 | 9 | --beautify - Не минифицировать .js скрипты |
|
10 | 10 | |
|
11 | 11 | Файлы в extras на самом деле компилятором не используются. Используйте только как образец.~%") |
|
12 | 12 | |
|
13 | (:error "Ошибка в~:[~; конце файла~]~2%~ | |
|
14 | ~2@T~<~@;~A~:>~%~ | |
|
15 | ~2@T~V@T^ (Строка ~D, Столбец ~D, Символ ~D)~2%") No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now