##// END OF EJS Templates
Localized and neat error reports
naryl -
r54:3a4e62f8 default
parent child Browse files
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 (load-src
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 (apply #'format target (getf strings key) args)))
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