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