Add end of presentation marker
[colloquium.git] / libstorycode / storycode.y
1 /*
2  * storycode.y
3  *
4  * Copyright © 2019 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23 %define api.token.prefix {SC_}
24 %define api.prefix {sc}
25 %locations
26
27 %code requires {
28
29   #include "narrative.h"
30   #include "slide.h"
31   #include "stylesheet.h"
32
33   #include "scparse_priv.h"
34 }
35
36 %union {
37   Stylesheet *ss;
38   Narrative *n;
39   Slide *s;
40   char *str;
41   struct length len;
42   struct length lenquad[4];
43   struct frame_geom geom;
44   char character;
45   double val;
46   struct colour col;
47   enum alignment align;
48   enum gradient grad;
49 }
50
51 %{
52   #include <stdio.h>
53   #include <stdlib.h>
54   #include <string.h>
55
56   extern int sclex();
57   extern int scparse();
58   void scerror(struct scpctx *ctx, const char *s);
59   extern int lineno;
60 %}
61
62 %token STYLES
63 %token SLIDE
64 %token EOP
65 %token NARRATIVE
66 %token PRESTITLE
67 %token SLIDETITLE
68 %token FOOTER
69 %token TEXTFRAME
70 %token IMAGEFRAME
71 %token BP
72 %token FONT GEOMETRY PAD ALIGN FGCOL BGCOL PARASPACE
73 %token VERT HORIZ
74 %token LEFT CENTER RIGHT
75 %token STRING
76 %token SQOPEN SQCLOSE
77 %token UNIT VALUE HEXCOL
78
79 %type <n> narrative
80 %type <s> slide
81 %type <ss> stylesheet
82 %type <str> narrative_prestitle
83 %type <str> slide_prestitle
84 %type <str> STRING
85 %type <str> imageframe
86 %type <str> slide_bulletpoint
87 %type <str> narrative_bulletpoint
88 %type <str> frameopt
89 %type <geom> geometry
90 %type <lenquad> lenquad
91 %type <col> colour
92 %type <str> HEXCOL
93 %type <len> length
94 %type <align> alignment
95 %type <str> slidetitle
96 %type <character> UNIT
97 %type <val> VALUE
98 %type <grad> gradtype
99
100 %parse-param { struct scpctx *ctx };
101 %initial-action
102 {
103     ctx->n = narrative_new();
104
105     /* The slide currently being created.
106      * Will be added to the narrative when complete */
107     ctx->s = slide_new();
108
109     ctx->max_str = 32;
110     ctx->str = malloc(ctx->max_str*sizeof(char *));
111     if ( ctx->str == NULL ) ctx->max_str = 0;
112     str_reset(ctx);
113 }
114
115 %{
116
117
118 static void copy_col(struct colour *to, struct colour from)
119 {
120     int i;
121     for ( i=0; i<4; i++ ) to->rgba[i] = from.rgba[i];
122     to->hexcode = from.hexcode;
123 }
124
125
126 static int hex_to_double(const char *v, double *r)
127 {
128     char c[5];
129
130     if ( strlen(v) != 7 ) return 0;
131     if ( v[0] != '#' ) return 0;  /* should've already been blocked by lexxer */
132
133     c[0] = '0';  c[1] = 'x';  c[4] = '\0';
134
135     c[2] = v[1]; c[3] = v[2];
136     r[0] = strtod(c, NULL) / 255.0;
137
138     c[2] = v[3]; c[3] = v[4];
139     r[1] = strtod(c, NULL) / 255.0;
140
141     c[2] = v[5]; c[3] = v[6];
142     r[2] = strtod(c, NULL) / 255.0;
143
144     return 1;
145 }
146
147 void str_reset(struct scpctx *ctx)
148 {
149     ctx->n_str = 0;
150     ctx->mask = 0;
151     ctx->alignment = ALIGN_INHERIT;
152 }
153
154 void add_str(struct scpctx *ctx, char *str)
155 {
156     if ( ctx->n_str == ctx->max_str ) {
157         char **nstr = realloc(ctx->str, (ctx->max_str+32)*sizeof(char *));
158         if ( nstr == NULL ) return;
159         ctx->max_str += 32;
160     }
161
162     ctx->str[ctx->n_str++] = str;
163 }
164
165 void set_style(struct scpctx *ctx, const char *element)
166 {
167     if ( ctx->mask & STYMASK_GEOM ) stylesheet_set_geometry(narrative_get_stylesheet(ctx->n),
168                                                             element, ctx->geom);
169     if ( ctx->mask & STYMASK_FONT ) stylesheet_set_font(narrative_get_stylesheet(ctx->n),
170                                                         element, ctx->font);
171     if ( ctx->mask & STYMASK_ALIGNMENT ) stylesheet_set_alignment(narrative_get_stylesheet(ctx->n),
172                                                                   element, ctx->alignment);
173     if ( ctx->mask & STYMASK_PADDING ) stylesheet_set_padding(narrative_get_stylesheet(ctx->n),
174                                                               element, ctx->padding);
175     if ( ctx->mask & STYMASK_PARASPACE ) stylesheet_set_paraspace(narrative_get_stylesheet(ctx->n),
176                                                                   element, ctx->paraspace);
177     if ( ctx->mask & STYMASK_FGCOL ) stylesheet_set_fgcol(narrative_get_stylesheet(ctx->n),
178                                                           element, ctx->fgcol);
179     if ( ctx->mask & STYMASK_BGCOL ) stylesheet_set_background(narrative_get_stylesheet(ctx->n),
180                                                                element, ctx->bggrad,
181                                                                ctx->bgcol, ctx->bgcol2);
182     ctx->mask = 0;
183     ctx->alignment = ALIGN_INHERIT;
184 }
185
186 %}
187
188 %%
189
190 /* The only thing a "presentation" really needs is narrative */
191 presentation:
192   stylesheet narrative
193 | stylesheet
194 | narrative
195 ;
196
197
198 /* ------ Narrative ------ */
199
200 narrative:
201   narrative_el            { }
202 | narrative narrative_el  { }
203 ;
204
205 narrative_el:
206   narrative_prestitle   { narrative_add_prestitle(ctx->n, $1); }
207 | narrative_bulletpoint { narrative_add_bp(ctx->n, $1); }
208 | slide                 { }
209 | STRING                { narrative_add_text(ctx->n, $1); }
210 | EOP                   { narrative_add_eop(ctx->n); }
211 ;
212
213 narrative_prestitle:
214   PRESTITLE STRING { $$ = $2; }
215 ;
216
217 narrative_bulletpoint:
218   BP STRING { $$ = $2; }
219 ;
220
221
222 /* -------- Slide -------- */
223
224 slide:
225   SLIDE '{' slide_parts '}'  { narrative_add_slide(ctx->n, ctx->s);
226                                /* New work in progress object */
227                                ctx->s = slide_new(); }
228 ;
229
230 slide_parts:
231   %empty
232 | slide_parts slide_part
233 ;
234
235 slide_part:
236   slide_prestitle { slide_add_prestitle(ctx->s, ctx->str, ctx->n_str);
237                     str_reset(ctx); }
238 | imageframe      { slide_add_image(ctx->s, $1, ctx->geom);
239                     str_reset(ctx); }
240 | textframe       { slide_add_text(ctx->s, ctx->str, ctx->n_str,
241                                    ctx->geom, ctx->alignment);
242                     str_reset(ctx); }
243 | FOOTER          { slide_add_footer(ctx->s); }
244 | slidetitle      { slide_add_slidetitle(ctx->s, ctx->str, ctx->n_str);
245                     str_reset(ctx); }
246 ;
247
248 slide_prestitle:
249   PRESTITLE frame_options multi_line_string         { }
250 | PRESTITLE frame_options '{' multi_line_string '}' { }
251 ;
252
253 slidetitle:
254   SLIDETITLE frame_options multi_line_string         { }
255 | SLIDETITLE frame_options '{' multi_line_string '}' { }
256 ;
257
258 imageframe:
259   IMAGEFRAME frame_options STRING { $$ = $STRING; }
260 ;
261
262 textframe:
263   TEXTFRAME frame_options multi_line_string         { }
264 | TEXTFRAME frame_options '{' multi_line_string '}' { }
265 ;
266
267 multi_line_string:
268   STRING                              { add_str(ctx, $1); }
269 | multi_line_string STRING            { add_str(ctx, $2); }
270 | slide_bulletpoint                   { add_str(ctx, $1); }
271 | multi_line_string slide_bulletpoint { add_str(ctx, $2); }
272 ;
273
274 slide_bulletpoint:
275   BP STRING { $$ = $2; }
276 ;
277
278 /* There can be any number of options */
279 frame_options:
280   %empty
281 | frame_options frame_option
282 ;
283
284 /* Each option is enclosed in square brackets */
285 frame_option:
286   SQOPEN frameopt SQCLOSE
287 ;
288
289 frameopt:
290   geometry   { ctx->geom = $1; }
291 | alignment  { ctx->alignment = $1; }
292 ;
293
294 /* Primitives for describing styles (used in frame options and stylesheets) */
295 geometry:
296   length 'x' length '+' length '+' length { $$.w = $1;  $$.h = $3;
297                                             $$.x = $5;  $$.y = $7; }
298 ;
299
300 lenquad:
301   length ',' length ',' length ',' length { $$[0] = $1;  $$[1] = $3;
302                                             $$[2] = $5;  $$[3] = $7; }
303 ;
304
305 colour:
306   VALUE ',' VALUE ',' VALUE ',' VALUE { $$.rgba[0] = $1;  $$.rgba[1] = $3;
307                                         $$.rgba[2] = $5;  $$.rgba[3] = $7;
308                                         $$.hexcode = 0;  }
309 | HEXCOL { double col[3];
310            if ( hex_to_double($1, col) ) {
311                $$.rgba[0] = col[0];  $$.rgba[1] = col[1];
312                $$.rgba[2] = col[2];  $$.rgba[3] = 1.0;
313                $$.hexcode = 1;
314            }
315          }
316 ;
317
318 alignment:
319   LEFT     { $$ = ALIGN_LEFT; }
320 | CENTER   { $$ = ALIGN_CENTER; }
321 | RIGHT    { $$ = ALIGN_RIGHT; }
322 ;
323
324 length:
325   VALUE UNIT  { $$.len = $VALUE;
326                 if ( $UNIT == 'u' ) $$.unit = LENGTH_UNIT;
327                 if ( $UNIT == 'f' ) $$.unit = LENGTH_FRAC; }
328 ;
329
330 gradtype:
331   VERT { $$ = GRAD_VERT; }
332 | HORIZ { $$ = GRAD_HORIZ; }
333 ;
334
335
336 /* ------ Stylesheet ------ */
337
338 stylesheet:
339   STYLES '{'
340    style_narrative
341    style_slide
342   '}' { }
343 ;
344
345 style_narrative:
346   NARRATIVE '{' style_narrative_def '}' { }
347 ;
348
349 style_narrative_def:
350   %empty
351 | style_narrative_def style_narrative_prestitle
352 | style_narrative_def style_narrative_bp
353 | style_narrative_def styledef { set_style(ctx, "NARRATIVE"); }
354 ;
355
356 style_narrative_prestitle:
357   PRESTITLE '{' styledefs '}' { set_style(ctx, "NARRATIVE.PRESTITLE"); }
358 ;
359
360 style_narrative_bp:
361   BP '{' styledefs '}' { set_style(ctx, "NARRATIVE.BP"); }
362 ;
363
364 style_slide:
365   SLIDE '{' style_slide_def '}' { }
366 ;
367
368 style_slide_def:
369   %empty
370   /* Call set_style() immediately */
371 | style_slide_def background            { set_style(ctx, "SLIDE"); }
372 | style_slide_def slide_geom            { set_style(ctx, "SLIDE"); }
373   /* The ones below will call set_style() themselves */
374 | style_slide_def style_slide_prestitle { }
375 | style_slide_def style_slide_text      { }
376 | style_slide_def style_slide_title     { }
377 | style_slide_def style_slide_footer    { }
378 ;
379
380 background:
381   BGCOL colour       { copy_col(&ctx->bgcol, $2);
382                        ctx->bggrad = GRAD_NONE;
383                        ctx->mask |= STYMASK_BGCOL; }
384 | BGCOL gradtype colour colour  { copy_col(&ctx->bgcol, $3);
385                                   copy_col(&ctx->bgcol2, $4);
386                                   ctx->bggrad = $2;
387                                   ctx->mask |= STYMASK_BGCOL; }
388 ;
389
390 slide_geom:
391   GEOMETRY geometry  { ctx->geom = $2;
392                        ctx->mask |= STYMASK_GEOM; }
393 ;
394
395 style_slide_prestitle:
396   PRESTITLE '{' styledefs '}' { set_style(ctx, "SLIDE.PRESTITLE"); }
397 ;
398
399 style_slide_title:
400   SLIDETITLE '{' styledefs '}' { set_style(ctx, "SLIDE.SLIDETITLE"); }
401 ;
402
403 style_slide_text:
404   TEXTFRAME '{' styledefs '}' { set_style(ctx, "SLIDE.TEXT"); }
405 ;
406
407 style_slide_footer:
408   FOOTER '{' styledefs '}' { set_style(ctx, "SLIDE.FOOTER"); }
409 ;
410
411 styledefs:
412   %empty
413 | styledefs styledef
414 ;
415
416 styledef:
417   FONT STRING        { ctx->font = $2;
418                        ctx->mask |= STYMASK_FONT; }
419 | GEOMETRY geometry  { ctx->geom = $2;
420                        ctx->mask |= STYMASK_GEOM; }
421 | PAD lenquad        { for ( int i=0; i<4; i++ ) ctx->padding[i] = $2[i];
422                        ctx->mask |= STYMASK_PADDING; }
423 | PARASPACE lenquad  { for ( int i=0; i<4; i++ ) ctx->paraspace[i] = $2[i];
424                        ctx->mask |= STYMASK_PARASPACE; }
425 | FGCOL colour       { copy_col(&ctx->fgcol, $2);
426                        ctx->mask |= STYMASK_FGCOL; }
427 | background         { /* Handled in rule 'background' */ }
428 | ALIGN alignment    { ctx->alignment = $2;
429                        ctx->mask |= STYMASK_ALIGNMENT; }
430 ;
431
432 %%
433
434 void scerror(struct scpctx *ctx, const char *s) {
435         printf("Storycode parse error at line %i\n", lineno);
436 }