##// END OF EJS Templates
100% parser, 100% macros, 50% intrinsics, 10% api, 0% misc
naryl -
r1:de781984 default
parent child Browse files
Show More
@@ -0,0 +1,2 b''
1 .*~
2 .qlot
@@ -0,0 +1,17 b''
1
2 all: diagrams.png $(BIN)
3
4 $(BIN): src/*.lisp src/*.ps
5 buildapp.sbcl
6
7 install-deps:
8 sbcl --load install-deps.lisp
9
10 update-deps:
11 sbcl --load update-deps.lisp
12
13 diagrams.png: diagrams.dot
14 dot $< -T png -o $@
15
16 clean:
17 rm sugar-qsp
@@ -0,0 +1,9 b''
1
2 * API
3 * Sample
4 * html
5 * bundle it all
6 * Qlot
7 * make a binary
8
9 * Finish intrinsics and api No newline at end of file
@@ -0,0 +1,18 b''
1
2 digraph diagrams {
3 rankdir = LR
4 subgraph cluster_code {
5 label = "Code Transformations"
6 QSP -> intermediate [label="parser.lisp (esrap)"]
7 intermediate -> Parenscript [label="ps-macros.lisp"]
8 Parenscript -> Javascript [label="main.lisp"]
9 }
10 subgraph cluster_js {
11 label = "Javascript modules call hierarchy"
12 user -> "ps-macros"
13 user -> intrinsics
14 "ps-macros" -> intrinsics
15 "ps-macros" -> api
16 intrinsics -> api
17 }
18 }
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,22 b''
1
2 # dynamic
3 DYNAMIC {$a=0}
4 DYNAMIC {if $a="string":'text!'}
5 DYNAMIC {
6 $args[0]
7 addobj $args[1]
8 },'Текст','Вилка'
9
10 DYNEVAL({result = 3+4})
11 PL DYNEVAL({$result = mid("abcd",2,1)+"qwerty"})
12 PL DYNEVAL({$test + $args[0]}, val($test))
13 проход=DYNEVAL({result = ($args[0] <> 'текст')}, 'строка')
14
15 $code = {
16 *pl "<<$args[0]>>"
17 *pl $args[0]
18 }
19
20 DYNAMIC $code,'asdfg'
21 ! будет выведено две строки 'asdfg'
22 -
@@ -0,0 +1,8 b''
1
2 # main
3 *P '1'
4 $txt = $MAINTXT
5 *PL '23'
6 *NL '456'
7 '78'
8 -
@@ -0,0 +1,7 b''
1
2 # aux
3 P '1'
4 $txt = $STATTXT
5 PL '23'
6 NL '456'
7 -
@@ -0,0 +1,18 b''
1
2 # diag
3 ! простой вывод сообщения.
4 MSG 'Много спелых груш'
5 ! получим окно с сообщением 'Много спелых груш'
6
7 ! Пример сообщения в действии ACT.
8 ACT 'Поесть груш':
9 MSG 'Ммм груши очень вкусные'
10 END
11 ! Получим вывод сообщения при клике по действию "Поесть груш"
12
13 !Пример с условием.
14 IF $hlebgotov = 1:
15 MSG 'Похоже хлеб уже готов'
16 END
17 ! Получаем вывод сообщения когда условие $hlebgotov = 1
18 -
@@ -0,0 +1,8 b''
1
2 # act
3 act 'test', 'img':
4 act 'test2':
5 'omg'
6 end
7 end
8 -
@@ -0,0 +1,21 b''
1
2 # objs
3 OBJECTS['деньги'] = 12
4 OBJECTS['патроны'] = 137
5
6 'Количество: <<OBJECTS[$getobj(countobj)]>>'
7
8 !Первый предмет в списке
9 GETOBJ(1)
10
11 !Последний предмет в списке
12 GETOBJ(COUNTOBJ)
13
14 i = 1
15 :loop
16 IF i <= COUNTOBJ:
17 OBJECTS[$GETOBJ(i)] = OBJECTS[$GETOBJ(i)] + 1
18 i = i + 1
19 JUMP 'loop'
20 END
21 -
@@ -0,0 +1,13 b''
1
2 # menu
3 ! нет иконки
4 $usr_menu[0] = 'Взять предмет:take_item'
5 ! иконка задана gif-файлом
6 $usr_menu[1] = 'Положить предмет:put_item:images/put_item.gif'
7 ! иконка задана значением $icon_file
8 $usr_menu[2] = 'Осмотреть предмет:look_item:<<$icon_file>>'
9 ! пункт меню задан 3-мя переменными
10 $usr_menu[3] = '<<$name>>:<<$loc>>:<<$file>>'
11
12 menu 'usr_menu' &! покажет меню из 4-х пунктов
13 -
@@ -0,0 +1,15 b''
1
2 # sound
3 !Громкость 100%
4 PLAY 'sound/music.mp3'
5 !Громкость 50%
6 PLAY 'sound/music.mp3',50
7 !Громкость 0% (без звука)
8 PLAY 'sound/music.mp3',0
9
10 !Проигрывание файла по адресу из переменной $file
11 ! с расширением 'mid'
12 ! и громкостью volume
13 PLAY '<<$file>>.mid',volume
14 PLAY $file,volume &! аналогично
15 -
@@ -0,0 +1,14 b''
1
2 # img
3 $BACKIMAGE = 'content/back.png'
4
5 VIEW 'content/monster.png'
6
7 ! Включаем режим HTML. Если во всей игре используется HTML,
8 ! то достаточно включить его на самой первой локации.
9 USEHTML = 1
10 ! Выводим картинку в основное описание
11 '<img src="content/room.jpg">'
12 ! Выводим картинку в доп. описание
13 PL '<img src="content/map.jpg">'
14 -
@@ -0,0 +1,20 b''
1
2 # input
3 $text = $USER_TEXT
4 CMDCLEAR
5 SHOWINPUT 0
6
7 $name = INPUT('Как звать тебя, герой?')
8
9 ! Вопрос с одним правильным ответом.
10 if input ('Что приходит после тьмы?') = 'свет':
11 msg 'Все верно.'
12 else
13 msg 'Ответ не верный.'
14 end
15
16 ! Пример реализации вопроса с несколькими вариантами ответа из которых любой будет правильным.
17 $answer = TRIM(LCASE(input('Ответить')))
18 if INSTR($answer, 'красный') OR INSTR($answer, 'желтый') OR INSTR($answer, 'зеленый'): gt 'win'
19 ! Введя красный или зеленый ил желтый произойдет переход на локацию 'win'
20 -
@@ -0,0 +1,9 b''
1
2 # loc
3 GT 'локация'
4 GT 'локация',1,'данные'
5 -
6
7 # локация
8
9 -
@@ -0,0 +1,44 b''
1
2 # time
3 ! устанавливает период выполнения кода
4 ! на локации-счётчике в 100 миллисекунд
5 SETTIMER 100
6
7 settimer 20
8 if стихотворение=0:
9 if время_первая_строка=0:
10 время_первая_строка=msecscount+1500
11 end
12 if msecscount>=время_первая_строка:
13 *pl 'Веленью совести, о Муза, будь послушна!'
14 стихотворение=1
15 end
16 end
17 if стихотворение=1:
18 if время_вторая_строка=0:
19 время_вторая_строка=msecscount+1500
20 end
21 if msecscount>=время_вторая_строка:
22 *pl 'Обиды не страшась, не требуя венца,'
23 стихотворение=2
24 end
25 end
26 if стихотворение=2:
27 if время_третья_строка=0:
28 время_третья_строка=msecscount+1500
29 end
30 if msecscount>=время_третья_строка:
31 *pl 'Хвалу и клевету приемли равнодушно'
32 стихотворение=3
33 end
34 end
35 if стихотворение=3:
36 if время_четвёртая_строка=0:
37 время_четвёртая_строка=msecscount+1500
38 end
39 if msecscount>=время_четвёртая_строка:
40 *pl 'И не оспаривай глупца.'
41 стихотворение=4
42 end
43 end
44 -
@@ -0,0 +1,7 b''
1
2 # var
3 var1 = 42
4 SET var2 = 42
5 LET var3 = 42
6 KILLVAR 'var1'
7 -
@@ -0,0 +1,16 b''
1
2 # expr
3 x=5
4 y=6
5 tmp=(x ! y) OR (x <> y)
6 tmp=x OR y
7 tmp=x AND y
8 tmp=y MOD x
9 tmp=OBJ 'стул'
10 tmp=LOC 'дом'
11 tmp=res = x*y + y*-x/2
12 tmp=res = NO x = y
13
14 tmp=15*5/2
15 tmp=15/2*5
16 -
@@ -0,0 +1,56 b''
1
2 # code
3 яблоко = 1
4 груша = 1
5
6 *pl 'Яблок' + яблоко
7 *pl 'Груш' + груша
8
9 '<a href="EXEC: яблоко += 1 & GT $CURLOC">яблоко</a>'
10 !При нажатии на ссылку выполнится код:
11 яблоко += 1
12 GT $CURLOC
13
14 !Однострочный комментарий
15 яблоки = 0
16 !'Многострочный
17 комментарий'
18 яблоки = 1
19 сыр = 5 & ! А здесь"комментарий начинается
20 в той же строке, но" заканчивается 'сильно
21 позже'. Во всём виноваты {кавычки и скобки
22 }Кстати:
23 яблоки=0
24 !'Комментарии рекомендуется писать всё-таки
25 в отдельных строках, а не как с "сыром"'
26
27 !'Общий вид вызова оператора
28 имя_оператора аргумент1, аргумент2, ...
29 или
30 имя_оператора (аргумент1, аргумент2, ...)
31 '
32 !Примеры вызовов операторов:
33 ADDOBJ 'ключ','pics/key.png'
34 ADDOBJ ('ключ','pics/key.png')
35 PL ('текст')
36 CLEAR()
37
38 !Примеры вызовов функций:
39 X = MAX(1, 2, 4)
40 A = RAND(4)
41 B = COUNTOBJ
42 PL STR(43)
43 PL $STR(43)
44 !Последние два выражения эквивалентны
45
46 RAND(1,4) &! вернёт случайное значение от 1 до 4
47 RAND(4,1) &! вернёт случайное значение от 1 до 4
48 RAND(1000) &! вернёт случайное значение от 0(1) до 1000
49 RAND 1000 &! вернёт случайное значение от 0(1) до 1000
50
51 MAX(1,2,5,2,0) &! вернёт 5
52 MAX(a,b,c) &! вернёт максимальное из значений переменных a,b,c
53 MAX('aa','ab','zz') &! вернёт 'zz'
54 MAX('a') &! вернёт максимальное из значений элементов массива "a"
55 MAX('$b') &! вернёт максимальное из значений элементов массива "$b"
56 -
@@ -0,0 +1,25 b''
1
2 # arrays
3 $яблоки[0]='антоновка'
4 $яблоки[1]='белый налив'
5 $яблоки[2]='астраханское'
6 $яблоки[3]='ранетка'
7 $яблоки[4]='симиренко'
8
9 сорт_яблока[0] = 1
10 сорт_яблока[1] = 2
11 сорт_яблока[2] = 4
12
13 *pl $яблоки[сорт_яблока[номер_яблока]]
14
15 $любимый_сорт['иван'] = $яблоки[2]
16 любимое_число['Алексей'] = 5
17 $item_loc['палка'] = 'лес'
18
19 $objs[] = 'Напильник' &! Если массив был пустой, то
20 $objs[] = 'Топор' &! [0] = 'Напильник',
21 $objs[] = 'Доска' &! [1] = 'Топор', [2] = 'Доска'
22
23 $a = $objs[] &! 'Доска' из примера выше
24 a = сорт_яблока[] &! 4 из примера выше
25 -
@@ -0,0 +1,19 b''
1
2 # str
3 *PL 'Byte Soft''s "QSP"'
4 *PL "Byte Soft's ""QSP"""
5
6 'Данный текст
7 будет расположен на
8 нескольких строках'
9
10 $a='И этот
11 текст
12 также'
13
14 a=2 & act 'Многострочное
15 название':gt 'next' & GoTo 'next'
16
17 $res = ('x=' & x & ' y=' & y)
18 $res = 'x=' & x
19 -
@@ -0,0 +1,56 b''
1
2
3 # if-elseif
4 IF a=1:
5 !операторы
6 ELSEIF a=2:
7 !операторы
8 ELSEIF a=3:
9 !операторы
10 END
11
12 -
13
14 # if-iif
15 !Модуль числа
16 abs_x = IIF(x > 0, x, -x)
17
18 !А следующая конструкция вызовет ошибку деления на ноль:
19 x = 0
20 y = IIF(x = 0, 0, 1/x)
21 -
22
23 # if-inline
24 if ((a+b)/c)=45+54 or (b<5 or c>45) and no obj 'лопата' and $f=$vvv+'RRRRR': p 'OK' & goto 'Next'
25 if был_здесь[$curloc]: exit
26 if a<3: jump 'sss'
27 if $имя = '': msg 'Введите имя!' & jump 'ввод'
28 if a+b=2:c=30 & gt 'next' else c=10 & d=11
29 -
30
31 # if-lines
32 if a+b<3 or y=8:
33 p 'A+B<3 или Y=8'
34 nl
35 if j=88: nl & p 'NEXT'
36 if $h='ooo':
37 p 'loo' & jump 'lll'
38 end
39 end
40
41 if a=0:
42 'abc1234'
43 if b=0: '0' else '1'
44 if j=88:nl & p 'NEXT'
45 if $h='ooo':
46 p 'loo' & jump 'lll'
47 else
48 p 'sample text'
49 v=8
50 end
51 '1234'
52 else
53 '01234'
54 g=78
55 end
56 -
@@ -0,0 +1,18 b''
1
2 # sub
3 GS 'переход', 'локация'
4 GS 'ход'
5 GS $loc,1
6 GS 'ход',$var,2,'данные'
7 яблоки = FUNC('функция')
8 PL FUNC($name, 1) * 78
9 MSG "text" + FUNC($name, "строка", 2)
10 if args[0] = 0: exit
11 -
12
13 #переход
14 *PL $ARGS[0] &! На экран выведется 'локация'
15 ACT 'Перейти':
16 GT $ARGS[0] &! Работать не будет, т.к. массив ARGS пуст
17 END
18 -
@@ -0,0 +1,27 b''
1
2 # loops
3 jump 'КонеЦ'
4 p 'Это сообщение не будет выведено'
5 :конец
6 p 'А это сообщение пользователь увидит'
7
8 s=0
9 :loop
10 if s<9:
11 s=s+1
12 pl s
13 jump 'loop'
14 end
15 p 'Всё!'
16
17 :loop
18 if y<y0:
19 if x<x0:
20 x=x+1
21 jump 'loop'
22 end
23 y=y+1
24 x=0
25 jump 'loop'
26 end
27 -
@@ -0,0 +1,3 b''
1 (ql:quickload :qlot)
2 (asdf:load-system :qlot/cli)
3 (qlot/cli:install)
@@ -0,0 +1,4 b''
1 ql alexandria
2 ql esrap
3 ql parenscript
4 ql cl-uglify-js
@@ -0,0 +1,24 b''
1 ("quicklisp" .
2 (:class qlot/source/dist:source-dist
3 :initargs (:distribution "http://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest)
4 :version "2020-02-18"))
5 ("alexandria" .
6 (:class qlot/source/ql:source-ql
7 :initargs (:%version :latest)
8 :version "ql-2020-02-18"))
9 ("esrap" .
10 (:class qlot/source/ql:source-ql
11 :initargs (:%version :latest)
12 :version "ql-2020-02-18"))
13 ("parenscript" .
14 (:class qlot/source/ql:source-ql
15 :initargs (:%version :latest)
16 :version "ql-2020-02-18"))
17 ("cl-uglify-js" .
18 (:class qlot/source/ql:source-ql
19 :initargs (:%version :latest)
20 :version "ql-2020-02-18"))
21 ("adopt" .
22 (:class qlot/source/github:source-github
23 :initargs (:repos "sjl/adopt" :ref nil :branch nil :tag nil)
24 :version "github-b594120fec8a15c68cae5040f8db6658"))
@@ -0,0 +1,39 b''
1
2 (in-package sugar-qsp)
3
4 ;;; API deals with DOM manipulation and some bookkeeping for the
5 ;;; intrinsics, namely variables
6 ;;; API is an implementation detail and has no QSP documentation. It
7 ;;; doesn't call intrinsics
8
9 (setf (root api) (ps:create))
10
11 ;;; Function calls
12
13 (defm (root api init-args) (args)
14 (dotimes (i (length args))
15 (if (numberp (elt args i))
16 (set (var args i) (elt args i))
17 (set (var $args i) (elt args i)))))
18
19 (defm (root api get-result) ()
20 (if (not (equal "" (var $result 0)))
21 (var $result 0)
22 (var result 0)))
23
24 ;;; Text windows
25
26 (defm (root api add-text) ())
27 (defm (root api get-text) ())
28 (defm (root api clear-text) ())
29
30 ;;; Actions
31
32 (defm (root api add-act) ())
33 (defm (root api clear-act) ())
34
35 ;;; Variables
36
37 (defm (root api get-var) ())
38 (defm (root api set-var) ())
39 (defm (root api kill-var) ())
@@ -0,0 +1,289 b''
1
2 (in-package sugar-qsp)
3
4 ;;;; Intrinsics are functions and procedures defined by the language.
5 ;;;; They can call api and deal with locations and other data directly.
6 ;;;; Except vars. Use VAR and SET or GET-VAR and SET-VAR api calls.
7
8 (setf (root intrinsics) (ps:create))
9
10 ;;; 1loc
11
12 (defm (root intrinsics goto) (target &rest args)
13 (api-call clear-text :main)
14 (apply (root intrinsics xgoto) target args))
15
16 (defm (root intrinsics xgoto) (target &rest args)
17 (api-call clear-act)
18 (api-call init-args args)
19 (setf (root current-location) target)
20 (funcall (ps:getprop (root locations) target)))
21
22 ;;; 2var
23
24 (defm (root intrinsics killvar) (varname &optional (index :whole))
25 (api-call kill-var varname index))
26
27 (defm (root intrinsics killall) ()
28 (api-call kill-all))
29
30 ;;; 3expr
31
32 (defm (root intrinsics obj) (name)
33 (funcall (root objs includes) name))
34
35 (defm (root intrinsics loc) ()
36 (funcall (root locations includes) name))
37
38 (defm (root intrinsics no) (arg)
39 (- -1 arg))
40
41 ;;; 4code
42
43 (defm (root intrinsics qspver) ()
44 "0.0.1")
45
46 (defm (root intrinsics curloc) ()
47 (root current-location))
48
49 (defm (root intrinsics rand) (a b)
50 (let ((min (min a b))
51 (max (max a b)))
52 (+ min (ps:chain *math (random (- max min))))))
53
54 (defm (root intrinsics rnd) ()
55 (funcall (root intrinsics rand) 1 1000))
56
57 (defm (root intrinsics qspmax) (&rest args)
58 (apply (ps:@ *math max) args))
59
60 (defm (root intrinsics qspmin) (&rest args)
61 (apply (ps:@ *math min) args))
62
63 ;;; 5arrays
64
65 (defm (root intrinsics copyarr) (to from start count)
66 (ps:for ((i start))
67 ((< i (min (api-call array-size from)
68 (+ start count))))
69 ((incf i))
70 (api-call set-var to (+ start i)
71 (api-call get-var from (+ start i)))))
72
73 (defm (root intrinsics arrsize) (name)
74 (api-call array-size name))
75
76 (defm (root intrinsics arrpos) (name value &optional (start 0))
77 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
78 (when (eq (api-call get-var name i) value)
79 (return i)))
80 -1)
81
82 (defm (root intrinsics arrcomp) (name pattern &optional (start 0))
83 (ps:for ((i start)) ((< i (api-call array-size name))) ((incf i))
84 (when (funcall (ps:getprop (api-call get-var name i) 'match) pattern)
85 (return i)))
86 -1)
87
88 ;;; 6str
89
90 (defm (root intrinsics len) (s)
91 (length s))
92
93 (defm (root intrinsics mid) (s from &optional count)
94 (s.substring from count))
95
96 (defm (root intrinsics ucase) (s)
97 (s.to-upper-case))
98
99 (defm (root intrinsics lcase) (s)
100 (s.to-lower-case))
101
102 (defm (root intrinsics trim) (s)
103 (s.trim))
104
105 (defm (root intrinsics replace) (s from to)
106 (s.replace from to))
107
108 (defm (root intrinsics instr) (s subs &optional (start 1))
109 (+ start (ps:chain s (substring (- start 1)) (search subs))))
110
111 (defm (root intrinsics isnum) (s)
112 (if (is-na-n s)
113 0
114 -1))
115
116 (defm (root intrinsics val) (s)
117 (parse-int s 10))
118
119 (defm (root intrinsics qspstr) (n)
120 (+ "" n))
121
122 (defm (root intrinsics strcomp) (s pattern)
123 (if (s.match pattern)
124 -1
125 0))
126
127 (defm (root intrinsics strfind) (s pattern group)
128 (let* ((re (ps:new (*reg-exp pattern)))
129 (match (re.exec s)))
130 (match.group group)))
131
132 (defm (root intrinsics strpos) (s pattern &optional (group 0))
133 (let* ((re (ps:new (*reg-exp pattern)))
134 (match (re.exec s))
135 (found (match.group group)))
136 (if found
137 (s.search found)
138 0)))
139
140 ;;; 7if
141
142 (defm (root intrinsics iif) (cond then else)
143 (if (= -1 cond) then else))
144
145 ;;; 8sub
146
147 (defm (root intrinsics gosub) (target &rest args)
148 (conserving-vars (args $args result $result)
149 (api-call init-args args)
150 (funcall (ps:getprop (root locations) target))
151 (values)))
152
153 (defm (root intrinsics func) (target &rest args)
154 (conserving-vars (args $args result $result)
155 (api-call init-args args)
156 (funcall (ps:getprop (root locations) target))
157 (api-call get-result)))
158
159 ;;; 9loops
160
161 ;;; 10dynamic
162
163 (defm (root intrinsics dyneval) (block &rest args)
164 (conserving-vars (args $args result $result)
165 (api-call init-args args)
166 (funcall block)
167 (api-call get-result)))
168
169 (defm (root intrinsics dynamic) (&rest args)
170 (conserving-vars (args $args result $result)
171 (api-call init-args args)
172 (funcall block)
173 (values)))
174
175 ;;; 11main
176
177 (defm (root intrinsics main-p) (s)
178 (api-call add-text :main s))
179
180 (defm (root intrinsics main-pl) ()
181 (api-call add-text :main (+ s "\n")))
182
183 (defm (root intrinsics main-nl) ()
184 (api-call add-text :main (+ "\n" s)))
185
186 (defm (root intrinsics maintxt) (s)
187 (api-call get-text :main))
188
189 (defm (root intrinsics desc) (s)
190 (api-call report-error "DESC is not supported"))
191
192 (defm (root intrinsics main-clear) ()
193 (api-call clear-text :main))
194
195 ;;; 12aux
196
197 (defm (root intrinsics showstat) ())
198
199 (defm (root intrinsics aux-p) ())
200
201 (defm (root intrinsics aux-pl) ())
202
203 (defm (root intrinsics aux-nl) ())
204
205 (defm (root intrinsics stattxt) ())
206
207 (defm (root intrinsics clear) ())
208
209 (defm (root intrinsics cls) ())
210
211 ;;; 13diag
212
213 (defm (root intrinsics msg) ())
214
215 ;;; 14act
216
217 (defm (root intrinsics showacts) ())
218
219 (defm (root intrinsics delact) ())
220
221 (defm (root intrinsics curacts) ())
222
223 (defm (root intrinsics cla) ())
224
225 ;;; 15objs
226
227 (defm (root intrinsics showobjs) ())
228
229 (defm (root intrinsics addobj) ())
230
231 (defm (root intrinsics delobj) ())
232
233 (defm (root intrinsics killobj) ())
234
235 (defm (root intrinsics countobj) ())
236
237 (defm (root intrinsics getobj) ())
238
239 ;;; 16menu
240
241 (defm (root intrinsics menu) ())
242
243 ;;; 17sound
244
245 (defm (root intrinsics play) ())
246
247 (defm (root intrinsics isplay) ())
248
249 (defm (root intrinsics close) ())
250
251 (defm (root intrinsics closeall) ())
252
253 ;;; 18img
254
255 (defm (root intrinsics refint) ())
256
257 (defm (root intrinsics view) ())
258
259 ;;; 19input
260
261 (defm (root intrinsics showinput) ())
262
263 (defm (root intrinsics usertxt) ())
264
265 (defm (root intrinsics cmdclear) ())
266
267 (defm (root intrinsics input) ())
268
269 ;;; 20time
270
271 (defm (root intrinsics wait) ())
272
273 (defm (root intrinsics msecscount) ())
274
275 (defm (root intrinsics settimer) ())
276
277 ;;; misc
278
279 (defm (root intrinsics rgb) ())
280
281 (defm (root intrinsics openqst) ())
282
283 (defm (root intrinsics addqst) ())
284
285 (defm (root intrinsics killqst) ())
286
287 (defm (root intrinsics opengame) ())
288
289 (defm (root intrinsics savegame) ())
@@ -0,0 +1,17 b''
1
2 (in-package sugar-qsp)
3
4 (defun entry-point (&rest args)
5 (catch :terminate
6 (make-javascript (parse-file (first args)))))
7
8 (defun parse-file (filename)
9 (p:parse 'sugar-qsp-grammar
10 (alexandria:read-file-into-string filename)))
11
12 (defun make-javascript (locations)
13 (mapcar #'make-javascript locations))
14
15 (defun report-error (fmt &rest args)
16 (apply #'format t fmt args)
17 (throw :terminate nil))
@@ -0,0 +1,2 b''
1
2 (in-package sugar-qsp)
@@ -0,0 +1,7 b''
1
2 (in-package cl-user)
3
4 (defpackage :sugar-qsp
5 (:use :cl)
6 (:local-nicknames (#:p #:esrap))
7 (:export #:parse-file #:entry-point))
1 NO CONTENT: new file 100644, binary diff hidden
This diff has been collapsed as it changes many lines, (568 lines changed) Show them Hide them
@@ -0,0 +1,568 b''
1
2 (in-package sugar-qsp)
3
4 ;;;; Parses TXT source to an intermediate representation
5
6 ;;; Utility
7
8 (defun remove-nth (list nth)
9 (append (subseq list 0 nth)
10 (subseq list (1+ nth))))
11
12 (defun not-quote (char)
13 (not (eql #\' char)))
14
15
16 (defun not-doublequote (char)
17 (not (eql #\" char)))
18
19 (defun not-brace (char)
20 (not (eql #\} char)))
21
22 (defun not-integer (string)
23 (when (find-if-not #'digit-char-p string)
24 t))
25
26 (defun not-newline (char)
27 (not (eql #\newline char)))
28
29 (defun id-any-char (char)
30 (and
31 (not (digit-char-p char))
32 (not (eql #\newline char))
33 (not (find char " !:&=<>+-*/,'\"()[]{}"))))
34
35 (defun intern-first (list)
36 (list* (intern (string-upcase (first list)))
37 (rest list)))
38
39 (defun remove-nil (list)
40 (remove nil list))
41
42 (defun binop-rest (list)
43 (destructuring-bind (ws1 operator ws2 operand2)
44 list
45 (declare (ignore ws1 ws2))
46 (list (intern (string-upcase operator)) operand2)))
47
48 (defun do-binop% (left-op other-ops)
49 (if (null other-ops)
50 left-op
51 (destructuring-bind ((operator right-op) &rest rest-ops)
52 other-ops
53 (if (eq (first left-op)
54 operator)
55 (do-binop% (append left-op (list right-op)) rest-ops)
56 (do-binop% (list operator left-op right-op) rest-ops)))))
57
58 (defun do-binop (list)
59 (destructuring-bind (left-op rest-ops)
60 list
61 (do-binop% left-op
62 (mapcar #'binop-rest rest-ops))))
63
64 (p:defrule line-continuation (and #\_ #\newline)
65 (:constant nil))
66
67 (p:defrule text-spaces (+ (or #\space #\tab line-continuation))
68 (:text t))
69
70 (p:defrule spaces (+ (or #\space #\tab line-continuation))
71 (:constant nil))
72
73 (p:defrule spaces? (* (or #\space #\tab line-continuation))
74 (:constant nil))
75
76 (p:defrule colon #\:
77 (:constant nil))
78
79 (p:defrule alphanumeric (alphanumericp character))
80
81 (p:defrule not-newline (not-newline character))
82
83 (p:defrule squote-esc "''"
84 (:lambda (list)
85 (p:text (elt list 0))))
86
87 (p:defrule dquote-esc "\"\""
88 (:lambda (list)
89 (p:text (elt list 0))))
90
91 (p:defrule sstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "\"exec:")))
92 (or squote-esc (not-quote character))))
93 (:lambda (list)
94 (p:text (mapcar #'second list))))
95
96 (p:defrule dstring-chars (+ (and (and (p:! "<<") (p:! (p:~ "'exec:")))
97 (or dquote-esc (not-doublequote character))))
98 (:lambda (list)
99 (p:text (mapcar #'second list))))
100
101 ;;; Identifiers
102
103 ;; From the official docs
104 (defparameter *keywords* '(act addlib addobj addqst and arrcomp arrpos arrsize $backimage bcolor cla clear *clear close clr *clr cls cmdclear cmdclr copyarr $counter countobj $curacts curloc debug delact dellib delobj desc disablescroll disablesubex dynamic dyneval else elseif end exit fcolor $fname freelib fsize func getobj gosub goto gs gt if iif inclib input instr isnum isplay jump killall killobj killqst killvar lcase lcolor len let loc $maintxt max menu mid min mod msecscount msg nl *nl no nosave obj $onactsel $ongload $ongsave $onnewloc $onobjadd $onobjdel $onobjsel opengame openqst or p *p pl *pl play qspver rand refint replace rgb rnd savegame selact selobj set settimer showacts showinput showobjs showstat $stattxt str strcomp strfind strpos trim ucase unsel unselect usehtml $usercom user_text usrtxt val view wait xgoto xgt))
105
106 (defun qsp-keyword-p (id)
107 (member (intern (string-upcase id)) *keywords*))
108
109 (defun not-qsp-keyword-p (id)
110 (not (member (intern (string-upcase id)) *keywords*)))
111
112 (p:defrule qsp-keyword (qsp-keyword-p identifier-raw))
113
114 (p:defrule id-first (id-any-char character))
115 (p:defrule id-next (or (id-any-char character)
116 (digit-char-p character)))
117 (p:defrule identifier-raw (and id-first (* id-next))
118 (:lambda (list)
119 (let ((id (p:text list)))
120 (when (member id *keywords*)
121 (error "~A is a keyword" id))
122 (intern (string-upcase id)))))
123
124 (p:defrule identifier (not-qsp-keyword-p identifier-raw))
125
126 ;;; Strings
127
128 (p:defrule qsp-string (or normal-string brace-string))
129
130 (p:defrule normal-string (or sstring dstring)
131 (:lambda (str)
132 (list* 'str (or str (list "")))))
133
134 (p:defrule sstring (and #\' (* (or string-interpol
135 sstring-exec
136 sstring-chars))
137 #\')
138 (:function second))
139
140 (p:defrule dstring (and #\" (* (or string-interpol
141 dstring-exec
142 dstring-chars))
143 #\")
144 (:function second))
145
146 (p:defrule string-interpol (and "<<" expression ">>")
147 (:function second))
148
149 (p:defrule sstring-exec-body (+ (or squote-esc (not-doublequote character)))
150 (:text t))
151
152 (p:defrule dstring-exec-body (+ (or dquote-esc (not-quote character)))
153 (:text t))
154
155 (p:defrule sstring-exec (and (p:~ "\"exec:") sstring-exec-body #\")
156 (:lambda (list)
157 (list* 'exec (p:parse 'exec-body (second list)))))
158
159 (p:defrule dstring-exec (and (p:~ "'exec:") dstring-exec-body #\')
160 (:lambda (list)
161 (list* 'exec (p:parse 'exec-body (second list)))))
162
163 (p:defrule brace-string (and #\{ before-statement block-body #\})
164 (:lambda (list)
165 (list* 'qspblock (third list))))
166
167 ;;; Location
168
169 (p:defrule sugar-qsp-grammar (and (* (or spaces #\newline))
170 (* location))
171 (:function second))
172
173 (p:defrule location (and location-header block-body location-end)
174 (:destructure (header body end)
175 (declare (ignore end))
176 `(location (,header) ,@body)))
177
178 (p:defrule location-header (and #\#
179 (+ not-newline)
180 (and #\newline spaces? before-statement))
181 (:destructure (spaces1 name spaces2)
182 (declare (ignore spaces1 spaces2))
183 (string-upcase (string-trim " " (p:text name)))))
184
185 (p:defrule location-end (and #\- #\newline before-statement)
186 (:constant nil))
187
188 ;;; Block body
189
190 (p:defrule newline-block-body (and #\newline spaces? block-body)
191 (:function third))
192
193 (p:defrule block-body (* statement)
194 (:function remove-nil))
195
196 ;; Just for <a href="exec:...'>
197 ;; Explicitly called from that rule's production
198 (p:defrule exec-body (and before-statement line-body)
199 (:function second))
200
201 (p:defrule line-body (and inline-statement (* next-inline-statement))
202 (:lambda (list)
203 (list* (first list) (second list))))
204
205 (p:defrule before-statement (* (or #\newline spaces))
206 (:constant nil))
207
208 (p:defrule statement-end (or statement-end-real statement-end-block-close))
209
210 (p:defrule statement-end-real (and (or #\newline
211 (and #\& spaces? (p:& statement%)))
212 before-statement)
213 (:constant nil))
214
215 (p:defrule statement-end-block-close (or (p:& #\}))
216 (:constant nil))
217
218 (p:defrule inline-statement (and statement% spaces?)
219 (:function first))
220
221 (p:defrule next-inline-statement (and #\& spaces? inline-statement)
222 (:function third))
223
224 (p:defrule not-a-non-statement (and (p:! (p:~ "elseif"))
225 (p:! (p:~ "else"))
226 (p:! (p:~ "end"))))
227
228 (p:defrule statement (and inline-statement statement-end)
229 (:function first))
230
231 (p:defrule statement% (and not-a-non-statement
232 (or label comment string-output
233 block non-returning-intrinsic assignment
234 expression-output))
235 (:function second))
236
237 (p:defrule expr-stopper (or comment block non-returning-intrinsic))
238
239 (p:defrule string-output qsp-string
240 (:lambda (string)
241 (list 'main-pl string)))
242
243 (p:defrule expression-output expression
244 (:lambda (list)
245 (list 'main-pl list)))
246
247 (p:defrule label (and colon identifier)
248 (:lambda (list)
249 (intern (string (second list)) :keyword)))
250
251 (p:defrule comment (and #\! (* (or text-spaces qsp-string brace-string not-newline)))
252 (:constant nil))
253
254 ;;; Blocks
255
256 (p:defrule block (or block-act block-if))
257
258 (p:defrule block-if (and block-if-head block-if-body)
259 (:destructure (head body)
260 `(qspcond (,@head ,@(first body))
261 ,@(rest body))))
262
263 (p:defrule block-if-head (and (p:~ "if") spaces expression spaces? colon spaces?)
264 (:function remove-nil)
265 (:function cdr))
266
267 (p:defrule block-if-body (or block-if-ml block-if-sl)
268 (:destructure (if-body elseifs else &rest ws)
269 (declare (ignore ws))
270 `(,(remove-nil if-body) ,@elseifs ,@(when else `((else ,@else))))))
271
272 (p:defrule block-if-sl (and line-body
273 (p:? block-if-elseif-inline)
274 (p:? block-if-else-inline)
275 spaces?))
276
277 (p:defrule block-if-ml (and (and #\newline spaces?)
278 block-body
279 (p:? block-if-elseif)
280 (p:? block-if-else)
281 block-if-end)
282 (:lambda (list)
283 (cdr list)))
284
285 (p:defrule block-if-elseif-inline (and block-if-elseif-head line-body (p:? block-if-elseif-inline))
286 (:destructure (head statements elseif)
287 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
288
289 (p:defrule block-if-elseif (and block-if-elseif-head statement-end block-body (p:? block-if-elseif))
290 (:destructure (head ws statements elseif)
291 (declare (ignore ws))
292 `((,@(rest head) ,@(remove-nil statements)) ,@elseif)))
293
294 (p:defrule block-if-elseif-head (and (p:~ "elseif") spaces expression spaces? colon spaces?)
295 (:function remove-nil)
296 (:function intern-first))
297
298 (p:defrule block-if-else-inline (and block-if-else-head line-body)
299 (:function second))
300
301 (p:defrule block-if-else (and block-if-else-head #\newline spaces? block-body)
302 (:function fourth))
303
304 (p:defrule block-if-else-head (and (p:~ "else") spaces?)
305 (:constant nil))
306
307 (p:defrule block-if-end (and (p:~ "end")
308 (p:? (and spaces (p:~ "if"))))
309 (:constant nil))
310
311 (p:defrule block-act (and block-act-head (or block-act-ml block-act-sl))
312 (:lambda (list)
313 (apply #'append list)))
314
315 (p:defrule block-act-sl (and line-body #\newline spaces?)
316 (:function first))
317
318 (p:defrule block-act-ml (and newline-block-body block-act-end)
319 (:lambda (list)
320 (apply #'list* (butlast list))))
321
322 (p:defrule block-act-head (and (p:~ "act") spaces qsp-string spaces?
323 (p:? block-act-head-img)
324 colon spaces?)
325 (:lambda (list)
326 (intern-first (remove-nil list))))
327
328 (p:defrule block-act-head-img (and #\, spaces? qsp-string spaces?)
329 (:lambda (list)
330 (or (third list) "")))
331
332 (p:defrule block-act-end (and (p:~ "end"))
333 (:constant nil))
334
335 ;;; Calls
336
337 (p:defrule first-argument (and expression spaces?)
338 (:function first))
339 (p:defrule next-argument (and "," spaces? expression)
340 (:function third))
341 (p:defrule arguments (or parenthesized-arguments plain-arguments no-arguments))
342 (p:defrule parenthesized-arguments (and spaces? #\( base-arguments #\))
343 (:function third))
344 (p:defrule plain-arguments (and spaces base-arguments)
345 (:function second))
346 (p:defrule no-arguments (or spaces (p:& #\newline) (p:& #\&))
347 (:constant nil))
348 (p:defrule base-arguments (and first-argument (* next-argument))
349 (:destructure (first rest)
350 (list* first rest)))
351
352 ;;; Intrinsics
353
354 (defmacro defintrinsics ((rule-name returning-rule-name non-returning-rule-name) &body clauses)
355 `(progn
356 ,@(loop :for clause :in clauses
357 :collect `(defintrinsic ,@clause))
358 (p:defrule ,returning-rule-name (or ,@(remove-nil
359 (mapcar (lambda (clause)
360 (when (second clause)
361 (alexandria:symbolicate
362 'intrinsic- (first clause))))
363 clauses))))
364 (p:defrule ,non-returning-rule-name (or ,@(remove-nil
365 (mapcar (lambda (clause)
366 (unless (second clause)
367 (alexandria:symbolicate
368 'intrinsic- (first clause))))
369 clauses))))
370 (p:defrule ,rule-name (or ,returning-rule-name ,non-returning-rule-name))))
371
372 (defmacro defintrinsic (sym returning &optional (min-arity 0) (max-arity 10) &rest names)
373 (declare (ignore returning))
374 (setf names
375 (if names
376 (mapcar #'string-upcase names)
377 (list (string sym))))
378 `(p:defrule ,(alexandria:symbolicate 'intrinsic- sym)
379 (and (p:? #\$) (or ,@(loop :for name :in names :collect `(p:~ ,name)))
380 arguments)
381 (:destructure (dollar name arguments)
382 (declare (ignore dollar))
383 (unless (<= ,min-arity (length arguments) ,max-arity)
384 (error "Intrinsic ~A expects between ~A and ~A arguments but ~A were provided:~%~S"
385 name ,min-arity ,max-arity (length arguments) arguments))
386 (list* ',sym arguments))))
387
388 (defintrinsics (intrinsic returning-intrinsic non-returning-intrinsic)
389 ;; Transitions
390 (goto nil 0 10 "gt" "goto")
391 (xgoto nil 0 10 "xgt" "xgoto")
392 ;; Variables
393 (killvar nil 0 2)
394 ;; Expressions
395 (obj t 1 1)
396 (loc t 1 1)
397 (no t 1 1)
398 ;; Basic
399 (qspver t 0 0)
400 (curloc t 0 0)
401 (rand t 1 2)
402 (rnd t 0 0)
403 (qspmax t 1 10 "max")
404 (qspmin t 1 10 "min")
405 ;; Arrays
406 (killall nil 0 0)
407 (copyarr nil 2 4)
408 (arrsize t 1 1)
409 (arrpos t 2 3)
410 (arrcomp t 2 3)
411 ;; Strings
412 (len t 1 1)
413 (mid t 2 3)
414 (ucase t 1 1)
415 (lcase t 1 1)
416 (trim t 1 1)
417 (replace t 2 3)
418 (instr t 2 3)
419 (isnum t 1 1)
420 (val t 1 1)
421 (qspstr t 1 1 "str")
422 (strcomp t 2 2)
423 (strfind t 2 3)
424 (strpos t 2 3)
425 ;; IF
426 (iif t 2 3)
427 ;; Subs
428 (gosub nil 1 10 "gosub" "gs")
429 (func t 1 10)
430 (exit nil 0 0)
431 ;; Jump
432 (jump nil 1 1)
433 ;; Dynamic
434 (dynamic nil 1 10)
435 (dyneval t 1 10)
436 ;; Main window
437 (main-p nil 1 1 "*p")
438 (main-pl nil 1 1 "*pl")
439 (main-nl nil 0 1 "*nl")
440 (maintxt t 0 0)
441 (desc t 1 1)
442 (main-clear nil 0 0 "*clear" "*clr")
443 ;; Aux window
444 (showstat nil 1 1)
445 (stat-p nil 1 1 "p")
446 (stat-pl nil 1 1 "pl")
447 (stat-nl nil 0 1 "nl")
448 (stattxt t 0 0)
449 (stat-clear nil 0 0 "clear" "clr")
450 (cls nil 0 0)
451 ;; Dialog
452 (msg nil 1 1)
453 ;; Acts
454 (showacts nil 1 1)
455 (delact nil 1 1 "delact" "del act")
456 (curacts t 0 0)
457 (cla nil 0 0)
458 ;; Objects
459 (showobjs nil 1 1)
460 (addobj nil 1 3 "addobj" "add obj")
461 (delobj nil 1 1 "delobj" "del obj")
462 (killobj nil 0 1)
463 (countobj t 0 0)
464 (getobj t 1 1)
465 ;; Menu
466 (menu nil 1 1)
467 ;; Sound
468 (play nil 1 2)
469 (isplay t 1 1)
470 (close nil 1 1)
471 (closeall nil 0 0 "close all")
472 ;; Images
473 (refint nil 0 0)
474 (view nil 0 1)
475 ;; Fonts
476 (rgb t 3 3)
477 ;; Input
478 (showinput nil 1 1)
479 (usertxt t 0 0 "user_text" "usrtxt")
480 (cmdclear nil 0 0 "cmdclear" "cmdclr")
481 (input t 1 1)
482 ;; Files
483 (openqst nil 1 1)
484 (addqst nil 1 1 "addqst" "addlib" "inclib")
485 (killqst nil 1 1 "killqst" "dellib" "freelib")
486 (opengame nil 0 1)
487 (savegame nil 0 1)
488 ;; Real time
489 (wait nil 1 1)
490 (msecscount t 0 0)
491 (settimer nil 1 1))
492
493 ;;; Expression
494
495 (p:defrule expression or-expr)
496
497 (p:defrule or-expr (and and-expr (* (and spaces? or-op spaces? and-expr)))
498 (:function do-binop))
499
500 (p:defrule and-expr (and eq-expr (* (and spaces? and-op spaces? eq-expr)))
501 (:function do-binop))
502
503 (p:defrule eq-expr (and cat-expr (* (and spaces? (or "<>" "<=" ">=" "=<" "=>"
504 #\= #\< #\> #\!)
505 spaces? cat-expr)))
506 (:function do-binop))
507
508 (p:defrule cat-expr (and sum-expr (* (and spaces? #\& spaces? (p:! expr-stopper) sum-expr)))
509 (:lambda (list)
510 (do-binop (list (first list) (mapcar (lambda (l)
511 (remove-nth l 3))
512 (second list))))))
513
514 (p:defrule sum-expr (and mod-expr (* (and spaces? (or #\+ #\-) spaces? mod-expr)))
515 (:function do-binop))
516
517 (p:defrule mod-expr (and mul-expr (* (and spaces? (p:~ "mod") spaces? mul-expr)))
518 (:function do-binop))
519
520 (p:defrule mul-expr (and unary-expr (* (and spaces? (or #\* #\/) spaces? unary-expr)))
521 (:function do-binop))
522
523 (p:defrule unary-expr (and (p:? (or #\+ #\-)) atom-expr)
524 (:lambda (list)
525 (let ((expr (remove-nil list)))
526 (if (= 1 (length expr))
527 (first expr)
528 (intern-first expr)))))
529
530 (p:defrule atom-expr (and (or variable literal returning-intrinsic paren-expr exists-expr) spaces?)
531 (:function first))
532
533 (p:defrule paren-expr (and #\( spaces? expression spaces? #\))
534 (:function third))
535
536 (p:defrule or-op (p:~ "or")
537 (:constant "or"))
538
539 (p:defrule and-op (p:~ "and")
540 (:constant "and"))
541
542 ;;; Variables
543
544 (p:defrule variable (and identifier (p:? array-index))
545 (:destructure (id idx)
546 (list 'var id (or idx 0))))
547
548 (p:defrule array-index (and #\[ spaces? (p:? expression) spaces? #\])
549 (:lambda (list)
550 (or (third list) :end)))
551
552 (p:defrule assignment (or kw-assignment plain-assignment)
553 (:destructure (var eq expr)
554 (declare (ignore eq))
555 (list 'set var expr)))
556
557 (p:defrule plain-assignment (and variable spaces? #\= spaces? expression)
558 (:function remove-nil))
559 (p:defrule kw-assignment (and (or (p:~ "let") (p:~ "set")) spaces? plain-assignment)
560 (:function third))
561
562 ;;; Non-string literals
563
564 (p:defrule literal (or qsp-string brace-string number))
565
566 (p:defrule number (+ (or #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
567 (:lambda (list)
568 (parse-integer (p:text list))))
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,194 b''
1
2 (in-package sugar-qsp)
3
4 ;;;; Parenscript macros which make the parser's intermediate
5 ;;;; representation directly compilable by Parenscript
6 ;;;; Some utility macros for other .ps sources too.
7
8 ;;; Utils
9
10 (ps:defpsmacro defm (path args &body body)
11 `(setf ,path (lambda ,args ,@body)))
12
13 (ps:defpsmacro root (&rest path)
14 `(ps:@ *sugar-q-s-p ,@path))
15
16 (ps:defpsmacro conserving-vars (vars &body body)
17 "Calls body with safely stored away VARS, and restores their values after that returning what BODY returns."
18 `(let ((__conserved (list ,@(loop :for var :in vars
19 :collect `(var ,var 0)))))
20 ,@(loop :for var :in vars
21 :collect `(set (var ,var 0) ,(if (char= #\$ (elt (string var) 0))
22 "" 0)))
23 (unwind-protect
24 (progn ,@body)
25 (progn
26 ,@(loop :for var :in vars
27 :for i from 0
28 :collect `(set (var ,var 0) (ps:@ __conserved ,i)))))))
29
30 ;;; Common
31
32 (defmacro defpsintrinsic (name)
33 `(ps:defpsmacro ,name (&rest args)
34 `(funcall (root intrinsics ,',name)
35 ,@args)))
36
37 (defmacro defpsintrinsics (() &rest names)
38 `(progn ,@(loop :for name :in names
39 :collect `(defpsintrinsic ,name))))
40
41 (defpsintrinsics ()
42 killvar obj loc no qspver curloc rand rnd qspmax qspmin killall copyarr arrsize arrpos arrcomp len mid ucase lcase trim replace instr isnum val qspstr strcomp strfind strpos iif gosub func dynamic dyneval main-p main-pl main-nl maintxt main-clear showstat stat-p stat-pl stat-nl stattxt stat-clear cls msg showacts delact curacts cla showobjs addobj delobj killobj countobj getobj menu play isplay close closeall refint view rgb showinput usertxt cmdclear input openqst addqst killqst opengame savegame wait msecscount settimer)
43
44 (ps:defpsmacro api-call (func &rest args)
45 `(funcall (root api ,func) ,@args))
46
47 (ps:defpsmacro label-block (&body body)
48 `(progn
49 (defvar __labels)
50 ,@body
51 (values)))
52
53 (ps:defpsmacro big-block (&body body)
54 `(ps:try
55 (label-block
56 ,@body)
57 (:catch (exit-p)
58 (unless (eq exit-p :exit)
59 (throw exit-p)))))
60
61 (ps:defpsmacro str (&rest forms)
62 (cond ((zerop (length forms))
63 "")
64 ((and (= 1 (length forms))
65 (stringp (first forms)))
66 (first forms))
67 (t
68 `(& ,@forms))))
69
70 ;;; 1loc
71
72 (ps:defpsmacro location ((name) &body body)
73 `(setf (root locations ,name)
74 (big-block
75 (tagbody ,@body))))
76
77 (ps:defpsmacro goto (target &rest args)
78 `(progn
79 (funcall (root intrinsics goto) ,target ,@args)
80 (exit)))
81
82 (ps:defpsmacro xgoto (target &rest args)
83 `(progn
84 (funcall (root intrinsics xgoto) ,target ,@args)
85 (exit)))
86
87 (ps:defpsmacro desc (target)
88 (report-error "DESC is not supported"))
89
90 ;;; 2var
91
92 (ps:defpsmacro var (name index)
93 `(api-call get-var ,(string name) ,index))
94
95 (ps:defpsmacro set ((var vname vindex) value)
96 (assert (eq var 'var))
97 `(api-call set-var ,(string vname) ,vindex ,value))
98
99 ;;; 3expr
100
101 (ps:defpsmacro <> (op1 op2)
102 `(not (equal ,op1 ,op2)))
103
104 (ps:defpsmacro ! (op1 op2)
105 `(not (equal ,op1 ,op2)))
106
107 ;;; 4code
108
109 (ps:defpsmacro exec (&body body)
110 (format nil "javascript: ~{~A~^~%~}" (mapcar #'ps:ps* body)))
111
112 ;;; 5arrays
113
114 ;;; 6str
115
116 (ps:defpsmacro & (&rest args)
117 `(ps:chain "" (concat ,@args)))
118
119 ;;; 7if
120
121 (ps:defpsmacro qspcond (&rest clauses)
122 `(cond ,@(loop :for clause :in clauses
123 :collect (list (first clause)
124 `(tagbody ,@(rest clause))))))
125
126 ;;; 8sub
127
128 ;;; 9loops
129 ;;; Yep, that's a huge kludge since Javascript doesn't support arbitrary labels
130
131 (ps:defpsmacro jump (target)
132 `(return-from ,(intern (string-upcase (second target)))
133 (funcall (ps:getprop __labels ,target))))
134
135 (ps:defpsmacro tagbody (&body body)
136 (let ((funcs (list nil :__nil)))
137 (dolist (form body)
138 (cond ((keywordp form)
139 (setf (first funcs) (reverse (first funcs)))
140 (push form funcs)
141 (push nil funcs))
142 (t
143 (push form (first funcs)))))
144 (setf (first funcs) (reverse (first funcs)))
145 (setf funcs (reverse funcs))
146 (if (= 2 (length funcs))
147 `(progn
148 ,@body)
149 `(progn
150 (setf ,@(loop :for f :on funcs :by #'cddr
151 :append (list `(ps:@ __labels ,(first f))
152 `(block ,(intern (string-upcase (string (first f))))
153 ,@(second f)
154 ,@(when (third f)
155 `((funcall
156 (ps:getprop __labels ,(third f)))))))))
157 (jump (str "__nil"))))))
158
159 (ps:defpsmacro exit ()
160 `(throw :exit))
161
162 ;;; 10dynamic
163
164 (ps:defpsmacro qspblock (&body body)
165 `(lambda ()
166 (defvar __labels)
167 ,@body
168 (values)))
169
170 ;;; 11main
171
172 (ps:defpsmacro act (name &body body)
173 `(api-call add-act ,name (lambda ()
174 (big-block
175 (tagbody
176 ,@body)))))
177
178 ;;; 12aux
179
180 ;;; 13diag
181
182 ;;; 14act
183
184 ;;; 15objs
185
186 ;;; 16menu
187
188 ;;; 17sound
189
190 ;;; 18img
191
192 ;;; 19input
193
194 ;;; 20time
@@ -0,0 +1,9 b''
1
2 (in-package sugar-qsp)
3
4 ;;; 1. Generates parenscript source to write to js
5 ;;; 2. Collects everything into complete file, leaving a neatly marked
6 ;;; place to customize page layout and styles.
7
8 (defun make-javascript (locations)
9 (mapcar #'ps:ps* locations))
@@ -0,0 +1,10 b''
1
2 (defsystem sugar-qsp
3 :description "QSP compiler to monolithic HTML page"
4 :depends-on (:alexandria :esrap :parenscript :cl-uglify-js)
5 :pathname "src/"
6 :serial t
7 :components ((:file "package")
8 (:file "main")
9 (:file "ps-macros")
10 (:file "parser")))
@@ -0,0 +1,3 b''
1 (ql:quickload :qlot)
2 (asdf:load-system :qlot/cli)
3 (qlot/cli:update)
@@ -1,2 +1,44 b''
1 (Russian readme below)
1 2 # sugar-qsp
2 3 Compiler for QSP games which creates monolithic HTML pages.
4
5 ## Usage
6
7 There are three mastery levels
8
9 1. Just build me the game:
10
11 `sugar-qsp game.txt`
12 And it will create the game in game.html
13
14 2. I know what I'm doing:
15 `sugar-qsp game.txt -o game.html --body body.html --js jquery.js my-js-library.js --css styles/*.css`
16 All options are self-explanatory. The result is a monolithic html specified
17 with the `-o` option. Default `body.html` (used by the first mastery level) can
18 be found in `extas` directory.
19
20 3. I'm a frontend developer!
21 `sugar-qsp game.txt -c -o game.js`
22 It just builds the game script into a js you can put on your website. To run
23 the game execute `SugarQSP.start()`
24
25 # sugar-qsp
26 Компилятор для игр на QSP создающий монолитные страницы на HTML.
27
28 ## Инструкции
29
30 Есть три уровня мастерства.
31
32 1. Просто собери мне игру:
33 `sugar-qsp game.txt`
34 Создаст игру в game.html
35
36 2. Я знаю что делаю:
37 `sugar-qsp game.txt -o game.html --body body.html --js jquery.js my-js-library.js --css styles/*.css`
38 Если вы знаете что делаете, то для вас смысл опций очевиден. `body.html`
39 по-умолчанию лежит в каталоге `extras`.
40
41 3. Я - фронтендер!
42 `sugar-qsp game.txt -c -o game.js`
43 Просто соберёт игру в Javascript файл который вы можете разместить на своём
44 сайте как вам угодно. Для запуска игры вызовите `SugarQSP.start()`.
General Comments 0
You need to be logged in to leave comments. Login now