Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 17 commits
  • 26 files changed
  • 0 comments
  • 1 contributor
Dec 12, 2011
Werner Almesberger compiler: don't feed TOK_ERROR to the parser
TOK_ERROR returned after a scanner failure was still sent to the
parser, forcing a parse error as well. While this didn't have any
ill effects, it's better to disentangle the two.
5c6bc3e
Werner Almesberger compiler: report the line number and the context of an error
We'll need this when the compiler parses the entire patch, not just
an expression.
6243f9f
Werner Almesberger compiler: remove trailing whitespace
Some lines had trailing tabs and spaces. Get rid of them.
6b64d3a
Werner Almesberger compiler: return error string from fpvm_parse
This way, the caller has access to the full diagnostics.
004c678
Werner Almesberger compiler: fix multiple /* ... */ comments
The regexp was too greedy and treated  /* ... */ ... /* ... */ as
a single long comment. Luckily, others have already solved that
braintwister for us. This is the solution by Stephen Ostermiller.
75e5cf7
Werner Almesberger compiler: bring out errors encountered by things called from parser
This seems awkward but works. Maybe there's a better way. lemon
documentation is silent about handling this kind of errors, and
the results found when googing for lemon and YYABORT aren't
encouraging.
1a02099
Werner Almesberger compiler: moved prologue and epilogue to almost regular .fnp files
They only differ in not having the per_frame= and per_vertex= prefixes.
The files are then converted to a header containing #defines of strings
with the file content inside. The compiler simply calls fpvm_chunk to
compile the prologue and epilogue.
ec9fbb4
Werner Almesberger compiler: ignore [preset]
The original MilkDrop presets are based on INF files and have a
"[preset]" at the beginning that should be ignored.
cad31ca
Werner Almesberger compiler: infrastructure for supporting fragment selection prefixes
There are two changes:
- instead of calling fpvm_do_assign directly, use a callback
- support for pre_frame, per_vertex, and per_pixel prefixes
1d5a9a3
Werner Almesberger compiler: support parsing imagefileN as well
This was tricky: since file names are bare and can contain all kinds of
special characters and keywords, we need to switch the scanner into a
different mode, which is a little awkward with re2c.
1b0a5d4
Werner Almesberger compiler: dirty hacks to work around syntax problems in patches
This commit makes the parser accept the following constructs:

- per_frameinit_1=...	(becomes per_frame=)
- per_frame=		(or per_vertex; is ignored)
- per_frame=//...	(or per_vertex; is ignored)
35c6131
Werner Almesberger compiler: make regression tester support per_frame and per_vertex as …
…well

Trival. Should have done this earlier.
f840e81
Werner Almesberger patches: fix syntax error in Balk Acid (DMX madness) 081b9ee
Werner Almesberger compiler: don't regularly use " in error messages; nicer format
MTK doesn't like strings containing double quotes. We can work around
this by minimizing the use of double quotes sent to MTK.

Since this changes all the error messages in the regression tests
anyway, we can use the opportunity and also make the message format
a little nicer.
10cbd9f
Werner Almesberger compiler: replace the DIY parser in compiler.c 763007f
Werner Almesberger compiler: remove fpvm_assign
We don't need it anymore.
1ecf4d1
Werner Almesberger compiler: parse all patches in the regression test 152aaf7
2  patches/Rovastar & Idiot24-7 - Balk Acid (DMX madness).fnp
@@ -52,7 +52,7 @@ per_frame=rot=rot+0.10*sin(time);
52 52
 per_frame=mv_r=0.5 +0.5*sin(time*1.23);
53 53
 per_frame=mv_b=0.5 + 0.5*sin(time*1.26);
54 54
 per_frame=mv_g=0.5+ 0.5*sin(time*1.19);
55  
-per_frame=wave_g=wave_g*+.20*sin(idmx4*time*.13);
  55
+per_frame=wave_g=wave_g+.20*sin(idmx4*time*.13);
56 56
 per_frame=wave_r=wave_r+.13*sin(idmx4*time);
57 57
 per_frame=wave_b=wave_b*sin(idmx4*time);
58 58
 per_frame=wave_x=idmx3
9  src/Makefile
@@ -77,7 +77,7 @@ bandfilters.h: bandfilters.sce
77 77
 	scilab -nw -nwni -nogui -nb -f bandfilters.sce
78 78
 
79 79
 %.c: %.re
80  
-	re2c -o $@ $<
  80
+	re2c -c -o $@ $<
81 81
 
82 82
 %.c: %.y
83 83
 	lemon $<
@@ -85,11 +85,16 @@ bandfilters.h: bandfilters.sce
85 85
 %.h %.inc: %.ids
86 86
 	cd compiler && ./idgen `basename $<`
87 87
 
  88
+compiler/infra-fnp.h: \
  89
+	  compiler/finish-pfv.fnp compiler/init-pvv.fnp compiler/finish-pvv.fnp
  90
+	compiler/file2h $^ >$@ || { rm -f $@; }
  91
+
88 92
 compiler/parser.h: compiler/parser.c
89 93
 obj/compiler/scanner.o: compiler/parser.h
90 94
 obj/compiler/parser_helper.o: compiler/parser.h
91 95
 obj/compiler/fpvm.o: compiler/parser.h
92 96
 obj/compiler/unique.o: compiler/fnp.inc
  97
+obj/compiler/compiler.o: compiler/infra-fnp.h compiler/parser.h
93 98
 
94 99
 # boot images for Milkymist One
95 100
 $(BINDIR)/flickernoise.bin: $(BINDIR)/flickernoise
@@ -119,6 +124,6 @@ clean:
119 124
 	rm -f $(POBJS)
120 125
 	rm -f compiler/scanner.c
121 126
 	rm -f compiler/parser.c compiler/parser.h compiler/parser.out
122  
-	rm -f compiler/fnp.h compiler/fnp.inc
  127
+	rm -f compiler/fnp.h compiler/fnp.inc compiler/infra-fnp.h
123 128
 
124 129
 .PHONY: clean load flash
309  src/compiler/compiler.c
@@ -29,8 +29,12 @@
29 29
 #include "../pixbuf/pixbuf.h"
30 30
 #include "fpvm.h"
31 31
 #include "unique.h"
  32
+#include "parser_helper.h"
  33
+#include "parser.h"
32 34
 #include "compiler.h"
33 35
 
  36
+#include "infra-fnp.h"
  37
+
34 38
 struct compiler_sc {
35 39
 	struct patch *p;
36 40
 
@@ -292,9 +296,8 @@ static bool init_pfv(struct compiler_sc *sc)
292 296
 static bool finalize_pfv(struct compiler_sc *sc)
293 297
 {
294 298
 	/* assign dummy values for output */
295  
-	if(!fpvm_assign(&sc->pfv_fragment, "_Xo", "_Xi")) goto fail_fpvm;
296  
-	if(!fpvm_assign(&sc->pfv_fragment, "_Yo", "_Yi")) goto fail_fpvm;
297  
-	if(!fpvm_finalize(&sc->pfv_fragment)) goto fail_fpvm;
  299
+	if(!fpvm_chunk(&sc->pfv_fragment, FINISH_PFV_FNP))
  300
+		goto fail_fpvm;
298 301
 	#ifdef COMP_DEBUG
299 302
 	printf("per-frame FPVM fragment:\n");
300 303
 	fpvm_dump(&sc->pfv_fragment);
@@ -325,16 +328,6 @@ static bool schedule_pfv(struct compiler_sc *sc)
325 328
 	return true;
326 329
 }
327 330
 
328  
-static bool add_per_frame(struct compiler_sc *sc, char *dest, char *val)
329  
-{
330  
-	if(!fpvm_assign(&sc->pfv_fragment, dest, val)) {
331  
-		comp_report(sc, "failed to add per-frame equation l. %d: %s",
332  
-		    sc->linenr, fpvm_get_last_error(&sc->pfv_fragment));
333  
-		return false;
334  
-	}
335  
-	return true;
336  
-}
337  
-
338 331
 /****************************************************************/
339 332
 /* PER-VERTEX VARIABLES                                         */
340 333
 /****************************************************************/
@@ -443,12 +436,8 @@ static bool init_pvv(struct compiler_sc *sc)
443 436
 	fpvm_set_bind_callback(&sc->pvv_fragment, pvv_bind_callback, sc);
444 437
 
445 438
 	fpvm_set_bind_mode(&sc->pvv_fragment, FPVM_BIND_SOURCE);
446  
-	if(!fpvm_chunk(&sc->pvv_fragment,
447  
-	    "x = i2f(_Xi)*_hmeshsize\n"
448  
-	    "y = i2f(_Yi)*_vmeshsize\n"
449  
-	    "rad = sqrt(sqr(x-0.5)+sqr(y-0.5))"))
  439
+	if(!fpvm_chunk(&sc->pvv_fragment, INIT_PVV_FNP))
450 440
 		goto fail_assign;
451  
-	/* TODO: generate ang */
452 441
 	fpvm_set_bind_mode(&sc->pvv_fragment, FPVM_BIND_ALL);
453 442
 
454 443
 	return true;
@@ -463,52 +452,8 @@ static int finalize_pvv(struct compiler_sc *sc)
463 452
 {
464 453
 	fpvm_set_bind_mode(&sc->pvv_fragment, FPVM_BIND_SOURCE);
465 454
 
466  
-	#define A(dest, val) \
467  
-	    if(!fpvm_assign(&sc->pvv_fragment, dest, val)) goto fail_assign
468  
-
469  
-	/* Zoom */
470  
-	A("_invzoom", "1/zoom");
471  
-	A("_xz", "_invzoom*(x-0.5)+0.5");
472  
-	A("_yz", "_invzoom*(y-0.5)+0.5");
473  
-
474  
-	/* Scale */
475  
-	A("_xs", "(_xz-cx)/sx+cx");
476  
-	A("_ys", "(_yz-cy)/sy+cy");
477  
-
478  
-	/* Warp */
479  
-	A("_warptime", "time*fWarpAnimSpeed");
480  
-	A("_invwarpscale", "1/fWarpScale");
481  
-	A("_f0", "11.68 + 4.0*cos(_warptime*1.413 + 10)");
482  
-	A("_f1", "8.77 + 3.0*cos(_warptime*1.113 + 7)");
483  
-	A("_f2", "10.54 + 3.0*cos(_warptime*1.233 + 3)");
484  
-	A("_f3", "11.49 + 4.0*cos(_warptime*0.933 + 5)");
485  
-	A("_ox2", "2*x-1");
486  
-	A("_oy2", "2*y-1");
487  
-	A("_xw", "_xs+warp*0.0035*("
488  
-		"sin(_warptime*0.333+_invwarpscale*(_ox2*_f0-_oy2*_f3))"
489  
-		"+cos(_warptime*0.753-_invwarpscale*(_ox2*_f1-_oy2*_f2)))");
490  
-	A("_yw", "_ys+warp*0.0035*("
491  
-		"cos(_warptime*0.375-_invwarpscale*(_ox2*_f2+_oy2*_f1))"
492  
-		"+sin(_warptime*0.825+_invwarpscale*(_ox2*_f0+_oy2*_f3)))");
493  
-
494  
-	/* Rotate */
495  
-	A("_cosr", "cos(rot)");
496  
-	A("_sinr", "sin(0-rot)");
497  
-	A("_u", "_xw-cx");
498  
-	A("_v", "_yw-cy");
499  
-	A("_xr", "_u*_cosr-_v*_sinr+cx");
500  
-	A("_yr", "_u*_sinr+_v*_cosr+cy");
501  
-
502  
-	/* Translate */
503  
-	A("_xd", "_xr-dx");
504  
-	A("_yd", "_yr-dy");
505  
-
506  
-	/* Convert to framebuffer coordinates */
507  
-	A("_Xo", "f2i(_xd*_texsize)");
508  
-	A("_Yo", "f2i(_yd*_texsize)");
509  
-
510  
-	#undef A
511  
-
  455
+	if(!fpvm_chunk(&sc->pvv_fragment, FINISH_PVV_FNP))
  456
+		goto fail_assign;
512 457
 	if(!fpvm_finalize(&sc->pvv_fragment)) goto fail_finalize;
513 458
 	#ifdef COMP_DEBUG
514 459
 	printf("per-vertex FPVM fragment:\n");
@@ -545,179 +490,104 @@ static bool schedule_pvv(struct compiler_sc *sc)
545 490
 	return true;
546 491
 }
547 492
 
548  
-static bool add_per_vertex(struct compiler_sc *sc, char *dest, char *val)
549  
-{
550  
-	if(!fpvm_assign(&sc->pvv_fragment, dest, val)) {
551  
-		comp_report(sc, "failed to add per-vertex equation l. %d: %s\n",
552  
-		    sc->linenr, fpvm_get_last_error(&sc->pvv_fragment));
553  
-		return false;
554  
-	}
555  
-	return true;
556  
-}
557  
-
558 493
 /****************************************************************/
559 494
 /* PARSING                                                      */
560 495
 /****************************************************************/
561 496
 
562  
-static bool process_equation(struct compiler_sc *sc, char *equation,
563  
-    bool per_vertex)
  497
+static const char *assign_default(struct parser_comm *comm,
  498
+    const char *label, struct ast_node *node)
564 499
 {
565  
-	char *c, *c2;
566  
-
567  
-	c = strchr(equation, '=');
568  
-	if(!c) {
569  
-		comp_report(sc, "error l.%d: malformed equation (%s)",
570  
-		    sc->linenr, equation);
571  
-		return false;
572  
-	}
573  
-	*c = 0;
574  
-
575  
-	if(*equation == 0) {
576  
-		comp_report(sc, "error l.%d: missing lvalue", sc->linenr);
577  
-		return false;
  500
+	struct compiler_sc *sc = comm->u.sc;
  501
+	int pfv;
  502
+	float v;
  503
+
  504
+	pfv = pfv_from_name(label);
  505
+	if(pfv < 0)
  506
+		return strdup("unknown parameter");
  507
+
  508
+	switch(node->op) {
  509
+	case op_constant:
  510
+		v = node->contents.constant;
  511
+		break;
  512
+	case op_not:
  513
+		if(node->contents.branches.a->op == op_constant) {
  514
+			v = -node->contents.branches.a->contents.constant;
  515
+			break;
  516
+		}
  517
+		/* fall through */
  518
+	default:
  519
+		return strdup("value must be a constant");
578 520
 	}
579  
-	c2 = c - 1;
580  
-	while((c2 > equation) && (*c2 == ' ')) *c2-- = 0;
581  
-
582  
-	c++;
583  
-	while(*c == ' ') c++;
584 521
 
585  
-	if(*equation == 0) {
586  
-		comp_report(sc, "error l.%d: missing lvalue", sc->linenr);
587  
-		return false;
588  
-	}
589  
-	if(*c == 0) {
590  
-		comp_report(sc, "error l.%d: missing rvalue", sc->linenr);
591  
-		return false;
592  
-	}
  522
+	/* patch initial condition or global parameter */
  523
+	pfv_update_patch_requires(sc, pfv);
  524
+	set_initial(sc, pfv, v);
  525
+	return NULL;
  526
+}
593 527
 
594  
-	if(per_vertex)
595  
-		return add_per_vertex(sc, equation, c);
  528
+static const char *assign_fragment(struct fpvm_fragment *frag,
  529
+    const char *label, struct ast_node *node)
  530
+{
  531
+	if(fpvm_do_assign(frag, label, node))
  532
+		return NULL;
596 533
 	else
597  
-		return add_per_frame(sc, equation, c);
  534
+		return strdup(fpvm_get_last_error(frag));
598 535
 }
599 536
 
600  
-static bool process_equations(struct compiler_sc *sc, char *equations,
601  
-    bool per_vertex)
  537
+static const char *assign_per_frame(struct parser_comm *comm,
  538
+    const char *label, struct ast_node *node)
602 539
 {
603  
-	char *c;
604  
-
605  
-	while(*equations) {
606  
-		c = strchr(equations, ';');
607  
-		if(!c)
608  
-			return process_equation(sc, equations, per_vertex);
609  
-		*c = 0;
610  
-		if(!process_equation(sc, equations, per_vertex)) return false;
611  
-		equations = c + 1;
612  
-	}
613  
-	return true;
  540
+	return assign_fragment(&comm->u.sc->pfv_fragment, label, node);
614 541
 }
615 542
 
616  
-static bool process_top_assign(struct compiler_sc *sc, char *left, char *right)
  543
+static const char *assign_per_vertex(struct parser_comm *comm,
  544
+    const char *label, struct ast_node *node)
617 545
 {
618  
-	int pfv;
619  
-
620  
-	while(*right == ' ') right++;
621  
-	if(*right == 0) return true;
622  
-
623  
-	if(strncmp(left, "imagefile", 9) == 0) {
624  
-		int image_n;
625  
-		char *totalname;
626  
-
627  
-		image_n = atoi(left+9);
628  
-		if((image_n < 1) || (image_n > IMAGE_COUNT)) {
629  
-			comp_report(sc, "warning l.%d: ignoring image with out of bounds number %d",
630  
-			     sc->linenr, image_n);
631  
-			return true;
632  
-		}
633  
-		image_n--;
634  
-		if(right[0] == '/')
635  
-			totalname = strdup(right);
636  
-		else {
637  
-			totalname =
638  
-			    malloc(strlen(sc->basedir) + strlen(right) + 1);
639  
-			if(totalname == NULL) return true;
640  
-			strcpy(totalname, sc->basedir);
641  
-			strcat(totalname, right);
642  
-		}
643  
-		pixbuf_dec_ref(sc->p->images[image_n]);
644  
-		sc->p->images[image_n] = pixbuf_get(totalname);
645  
-		free(totalname);
646  
-		return true;
647  
-	}
648  
-
649  
-	pfv = pfv_from_name(left);
650  
-	if(pfv >= 0) {
651  
-		/* patch initial condition or global parameter */
652  
-		pfv_update_patch_requires(sc, pfv);
653  
-		set_initial(sc, pfv, atof(right));
654  
-		return true;
655  
-	}
656  
-
657  
-	if(strncmp(left, "per_frame", 9) == 0)
658  
-		/* per-frame equation */
659  
-		return process_equations(sc, right, false);
660  
-
661  
-	if((strncmp(left, "per_vertex", 10) == 0) ||
662  
-	    (strncmp(left, "per_pixel", 9) == 0))
663  
-		/* per-vertex equation */
664  
-		return process_equations(sc, right, true);
665  
-
666  
-	comp_report(sc, "warning l.%d: ignoring unknown parameter %s",
667  
-	    sc->linenr, left);
668  
-
669  
-	return true;
  546
+	return assign_fragment(&comm->u.sc->pvv_fragment, label, node);
670 547
 }
671 548
 
672  
-static bool process_line(struct compiler_sc *sc, char *line)
  549
+static const char *assign_image_name(struct parser_comm *comm,
  550
+    int number, const char *name)
673 551
 {
674  
-	char *c;
675  
-
676  
-	while(*line == ' ') line++;
677  
-	if(*line == 0) return true;
678  
-	if(*line == '[') return true;
679  
-
680  
-	c = strstr(line, "//");
681  
-	if(c) *c = 0;
682  
-
683  
-	c = line + strlen(line) - 1;
684  
-	while((c >= line) && (*c == ' ')) *c-- = 0;
685  
-	if(*line == 0) return true;
686  
-
687  
-	c = strchr(line, '=');
688  
-	if(!c) {
689  
-		comp_report(sc, "error l.%d: '=' expected", sc->linenr);
690  
-		return false;
  552
+	struct compiler_sc *sc = comm->u.sc;
  553
+	char *totalname;
  554
+
  555
+	if(number > IMAGE_COUNT)
  556
+		return strdup("image number out of bounds");
  557
+	number--;
  558
+	
  559
+	if(*name == '/')
  560
+		totalname = strdup(name);
  561
+	else {
  562
+		totalname = malloc(strlen(sc->basedir) + strlen(name) + 1);
  563
+		if(totalname == NULL)
  564
+			return strdup("out of memory");
  565
+		strcpy(totalname, sc->basedir);
  566
+		strcat(totalname, name);
691 567
 	}
692  
-	*c = 0;
693  
-	return process_top_assign(sc, line, c+1);
  568
+	pixbuf_dec_ref(sc->p->images[number]);
  569
+	sc->p->images[number] = pixbuf_get(totalname);
  570
+	free(totalname);
  571
+	return NULL;
694 572
 }
695 573
 
696  
-static bool parse_patch(struct compiler_sc *sc, char *patch_code)
  574
+static bool parse_patch(struct compiler_sc *sc, const char *patch_code)
697 575
 {
698  
-	char *eol;
699  
-
700  
-	while(*patch_code) {
701  
-		sc->linenr++;
702  
-		eol = strchr(patch_code, '\n');
703  
-		if(!eol) {
704  
-			if(!process_line(sc, patch_code))
705  
-				return false;
706  
-			else
707  
-				return true;
708  
-		}
709  
-		*eol = 0;
710  
-		if(*patch_code == 0) {
711  
-			patch_code = eol + 1;
712  
-			continue;
713  
-		}
714  
-		if(*(eol - 1) == '\r') *(eol - 1) = 0;
715  
-		if(!process_line(sc, patch_code))
716  
-			return false;
717  
-		patch_code = eol + 1;
  576
+	struct parser_comm comm = {
  577
+		.u.sc = sc,
  578
+		.assign_default = assign_default,
  579
+		.assign_per_frame = assign_per_frame,
  580
+		.assign_per_vertex = assign_per_vertex,
  581
+		.assign_image_name = assign_image_name,
  582
+	};
  583
+	const char *error;
  584
+
  585
+	error = fpvm_parse(patch_code, TOK_START_ASSIGN, &comm);
  586
+	if(error) {
  587
+		sc->rmc(error);
  588
+		free((void *) error);
718 589
 	}
719  
-
720  
-	return true;
  590
+	return !error;
721 591
 }
722 592
 
723 593
 struct patch *patch_compile(const char *basedir, const char *patch_code,
@@ -725,7 +595,6 @@ struct patch *patch_compile(const char *basedir, const char *patch_code,
725 595
 {
726 596
 	struct compiler_sc *sc;
727 597
 	struct patch *p;
728  
-	char *patch_code_copy;
729 598
 	int i;
730 599
 
731 600
 	sc = malloc(sizeof(struct compiler_sc));
@@ -753,16 +622,8 @@ struct patch *patch_compile(const char *basedir, const char *patch_code,
753 622
 	if(!init_pfv(sc)) goto fail;
754 623
 	if(!init_pvv(sc)) goto fail;
755 624
 
756  
-	patch_code_copy = strdup(patch_code);
757  
-	if(patch_code_copy == NULL) {
758  
-		rmc("Failed to allocate memory for patch code");
759  
-		goto fail;
760  
-	}
761  
-	if(!parse_patch(sc, patch_code_copy)) {
762  
-		free(patch_code_copy);
  625
+	if(!parse_patch(sc, patch_code))
763 626
 		goto fail;
764  
-	}
765  
-	free(patch_code_copy);
766 627
 	unique_free();
767 628
 
768 629
 	if(!finalize_pfv(sc)) goto fail;
6  src/compiler/compiler.h
@@ -117,7 +117,7 @@ enum {
117 117
 	pfv_osc2,
118 118
 	pfv_osc3,
119 119
 	pfv_osc4,
120  
-	
  120
+
121 121
 	pfv_midi1,
122 122
 	pfv_midi2,
123 123
 	pfv_midi3,
@@ -128,7 +128,7 @@ enum {
128 128
 	pfv_midi8,
129 129
 
130 130
 	pfv_video_a,
131  
-	
  131
+
132 132
 	pfv_image1_a,
133 133
 	pfv_image1_x,
134 134
 	pfv_image1_y,
@@ -191,7 +191,7 @@ enum {
191 191
 	pvv_osc2,
192 192
 	pvv_osc3,
193 193
 	pvv_osc4,
194  
-	
  194
+
195 195
 	pvv_midi1,
196 196
 	pvv_midi2,
197 197
 	pvv_midi3,
9  src/compiler/file2h
... ...
@@ -0,0 +1,9 @@
  1
+#!/bin/sh -e
  2
+while [ "$1" ]; do
  3
+	echo -n "#define "
  4
+	basename $1 | sed 's/[^a-zA-Z0-9]/_/g' | tr a-z A-Z | tr -d '\n'
  5
+	echo ' \'
  6
+	sed 's/\\/\\\\/g;s/"/\\"/g;s/.*/    "&\\n" \\/' <$1
  7
+	echo
  8
+	shift
  9
+done
19  src/compiler/finish-pfv.fnp
... ...
@@ -0,0 +1,19 @@
  1
+/*
  2
+ * Per-Frame Variables Epilogue
  3
+ * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
  4
+ *
  5
+ * This program is free software: you can redistribute it and/or modify
  6
+ * it under the terms of the GNU General Public License as published by
  7
+ * the Free Software Foundation, version 3 of the License.
  8
+ *
  9
+ * This program is distributed in the hope that it will be useful,
  10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+ * GNU General Public License for more details.
  13
+ *
  14
+ * You should have received a copy of the GNU General Public License
  15
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16
+ */
  17
+
  18
+	_Xo = _Xi
  19
+	_Yo = _Yi
57  src/compiler/finish-pvv.fnp
... ...
@@ -0,0 +1,57 @@
  1
+/*
  2
+ * Per-Vertex Variables Epilogue
  3
+ * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
  4
+ *
  5
+ * This program is free software: you can redistribute it and/or modify
  6
+ * it under the terms of the GNU General Public License as published by
  7
+ * the Free Software Foundation, version 3 of the License.
  8
+ *
  9
+ * This program is distributed in the hope that it will be useful,
  10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+ * GNU General Public License for more details.
  13
+ *
  14
+ * You should have received a copy of the GNU General Public License
  15
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16
+ */
  17
+
  18
+	/* Zoom */
  19
+	_invzoom = 1/zoom
  20
+	_xz = _invzoom*(x-0.5)+0.5
  21
+	_yz = _invzoom*(y-0.5)+0.5
  22
+
  23
+	/* Scale */
  24
+	_xs = (_xz-cx)/sx+cx
  25
+	_ys = (_yz-cy)/sy+cy
  26
+
  27
+	/* Warp */
  28
+	_warptime = time*fWarpAnimSpeed
  29
+	_invwarpscale = 1/fWarpScale
  30
+	_f0 = 11.68 + 4.0*cos(_warptime*1.413 + 10)
  31
+	_f1 = 8.77 + 3.0*cos(_warptime*1.113 + 7)
  32
+	_f2 = 10.54 + 3.0*cos(_warptime*1.233 + 3)
  33
+	_f3 = 11.49 + 4.0*cos(_warptime*0.933 + 5)
  34
+	_ox2 = 2*x-1
  35
+	_oy2 = 2*y-1
  36
+	_xw = _xs+warp*0.0035*(
  37
+		sin(_warptime*0.333+_invwarpscale*(_ox2*_f0-_oy2*_f3))
  38
+		+cos(_warptime*0.753-_invwarpscale*(_ox2*_f1-_oy2*_f2)))
  39
+	_yw = _ys+warp*0.0035*(
  40
+		cos(_warptime*0.375-_invwarpscale*(_ox2*_f2+_oy2*_f1))
  41
+		+sin(_warptime*0.825+_invwarpscale*(_ox2*_f0+_oy2*_f3)))
  42
+
  43
+       	/* Rotate */
  44
+	_cosr = cos(rot)
  45
+	_sinr = sin(0-rot)
  46
+	_u = _xw-cx
  47
+	_v = _yw-cy
  48
+	_xr = _u*_cosr-_v*_sinr+cx
  49
+	_yr = _u*_sinr+_v*_cosr+cy
  50
+
  51
+	/* Translate */
  52
+	_xd = _xr-dx
  53
+	_yd = _yr-dy
  54
+
  55
+	/* Convert to framebuffer coordinates */
  56
+	_Xo = f2i(_xd*_texsize)
  57
+	_Yo = f2i(_yd*_texsize)
42  src/compiler/fpvm.c
@@ -15,7 +15,9 @@
15 15
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16
  */
17 17
 
  18
+#include <stdlib.h>
18 19
 #include <stdio.h>
  20
+#include <string.h>
19 21
 
20 22
 #include <fpvm/fpvm.h>
21 23
 #include <fpvm/ast.h>
@@ -42,29 +44,37 @@ void fpvm_init(struct fpvm_fragment *fragment, int vector_mode)
42 44
 }
43 45
 
44 46
 
45  
-int fpvm_assign(struct fpvm_fragment *fragment, const char *dest,
46  
-    const char *expr)
  47
+static const char *assign_default(struct parser_comm *comm,
  48
+    const char *label, struct ast_node *node)
47 49
 {
48  
-	union parser_comm comm;
49  
-	int res;
50  
-
51  
-	if (!fpvm_parse(expr, TOK_START_EXPR, &comm)) {
52  
-		snprintf(fragment->last_error, FPVM_MAXERRLEN, "Parse error");
53  
-		return 0;
54  
-	}
55  
-
56  
-	dest = unique(dest);
  50
+	if(fpvm_do_assign(comm->u.fragment, label, node))
  51
+		return NULL;
  52
+	else
  53
+		return strdup(fpvm_get_last_error(comm->u.fragment));
  54
+}
57 55
 
58  
-	res = fpvm_do_assign(fragment, dest, comm.parseout);
59  
-	fpvm_parse_free(comm.parseout);
60 56
 
61  
-	return res;
  57
+static const char *assign_unsupported(struct parser_comm *comm,
  58
+    const char *label, struct ast_node *node)
  59
+{
  60
+	return strdup("assignment mode not supported yet");
62 61
 }
63 62
 
64 63
 
65 64
 int fpvm_chunk(struct fpvm_fragment *fragment, const char *chunk)
66 65
 {
67  
-	union parser_comm comm = { .fragment = fragment };
  66
+	struct parser_comm comm = {
  67
+		.u.fragment = fragment,
  68
+		.assign_default = assign_default,
  69
+		.assign_per_frame = assign_unsupported,
  70
+		.assign_per_vertex = assign_unsupported,
  71
+	};
  72
+	const char *error;
68 73
 
69  
-	return fpvm_parse(chunk, TOK_START_ASSIGN, &comm);
  74
+	error = fpvm_parse(chunk, TOK_START_ASSIGN, &comm);
  75
+	if(error) {
  76
+		snprintf(fragment->last_error, FPVM_MAXERRLEN, "%s", error);
  77
+		free((void *) error);
  78
+	}
  79
+	return !error;
70 80
 }
3  src/compiler/fpvm.h
@@ -29,9 +29,6 @@
29 29
 
30 30
 void fpvm_init(struct fpvm_fragment *fragment, int vector_mode);
31 31
 
32  
-int fpvm_assign(struct fpvm_fragment *fragment, const char *dest,
33  
-    const char *expr);
34  
-
35 32
 int fpvm_chunk(struct fpvm_fragment *fragment, const char *chunk);
36 33
 
37 34
 #endif /* __FPVM_H */
22  src/compiler/init-pvv.fnp
... ...
@@ -0,0 +1,22 @@
  1
+/*
  2
+ * Per-Vector Variables Prologue
  3
+ * Copyright (C) 2010, 2011 Sebastien Bourdeauducq
  4
+ *
  5
+ * This program is free software: you can redistribute it and/or modify
  6
+ * it under the terms of the GNU General Public License as published by
  7
+ * the Free Software Foundation, version 3 of the License.
  8
+ *
  9
+ * This program is distributed in the hope that it will be useful,
  10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12
+ * GNU General Public License for more details.
  13
+ *
  14
+ * You should have received a copy of the GNU General Public License
  15
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16
+ */
  17
+
  18
+	x = i2f(_Xi)*_hmeshsize
  19
+	y = i2f(_Yi)*_vmeshsize
  20
+	rad = sqrt(sqr(x-0.5)+sqr(y-0.5))
  21
+
  22
+        /* TODO: generate ang */
71  src/compiler/parser.y
@@ -28,6 +28,12 @@
28 28
 	#include "parser.h"
29 29
 
30 30
 
  31
+	struct yyParser;
  32
+	static void yy_parse_failed(struct yyParser *yypParser);
  33
+
  34
+	typedef const char *(*assign_callback)(struct parser_comm *comm,
  35
+	    const char *label, struct ast_node *node);
  36
+
31 37
 	const enum ast_op tok2op[] = {
32 38
 		[TOK_IDENT]	= op_ident,
33 39
 		[TOK_CONSTANT]	= op_constant,
@@ -56,11 +62,10 @@
56 62
 		[TOK_MIN]	= op_min,
57 63
 		[TOK_MAX]	= op_max,
58 64
 		[TOK_INT]	= op_int,
59  
-		
60 65
 	};
61 66
 
62  
-	struct ast_node *node(int token, const char *id, struct ast_node *a,
63  
-	     struct ast_node *b, struct ast_node *c)
  67
+	static struct ast_node *node(int token, const char *id,
  68
+	    struct ast_node *a, struct ast_node *b, struct ast_node *c)
64 69
 	{
65 70
 		struct ast_node *n;
66 71
 
@@ -72,6 +77,14 @@
72 77
 		n->contents.branches.c = c;
73 78
 		return n;
74 79
 	}
  80
+
  81
+	static void syntax_error(struct parser_state *state)
  82
+	{
  83
+		if(!state->error_label) {
  84
+			state->error_label = state->id->label;
  85
+			state->error_lineno = state->id->lineno;
  86
+		}
  87
+	}
75 88
 }
76 89
 
77 90
 %start_symbol start
@@ -85,10 +98,16 @@
85 98
 
86 99
 %type node {struct ast_node *}
87 100
 %destructor node { free($$); }
88  
-%syntax_error { yy_parse_failed(yypParser); }
  101
+
  102
+%type context {assign_callback}
  103
+
  104
+%syntax_error {
  105
+	syntax_error(state);
  106
+	yy_parse_failed(yypParser);
  107
+}
89 108
 
90 109
 start ::= TOK_START_EXPR node(N). {
91  
-	state->comm->parseout = N;
  110
+	state->comm->u.parseout = N;
92 111
 	state->success = 1;
93 112
 }
94 113
 
@@ -101,10 +120,50 @@ assignments ::= assignments assignment.
101 120
 assignments ::= .
102 121
 
103 122
 assignment ::= ident(I) TOK_ASSIGN node(N) opt_semi. {
104  
-	fpvm_do_assign(state->comm->fragment, I->label, N);
  123
+	state->error = state->comm->assign_default(state->comm, I->label, N);
  124
+	if(state->error) {
  125
+		syntax_error(state);
  126
+		yy_parse_failed(yypParser);
  127
+		return;
  128
+	}
105 129
 	fpvm_parse_free(N);
106 130
 }
107 131
 
  132
+assignment ::= TOK_IMAGEFILE(I) TOK_ASSIGN TOK_FNAME(N). {
  133
+	state->error = state->comm->assign_image_name(state->comm,
  134
+	    atoi(I->label+9), N->label);
  135
+	if(state->error) {
  136
+		syntax_error(state);
  137
+		yy_parse_failed(yypParser);
  138
+		return;
  139
+	}
  140
+}
  141
+
  142
+assignment ::= context(C). {
  143
+	/*
  144
+	 * @@@ Vile madness ahead: a lot of patches have per_frame= or
  145
+	 * per_vertex= tags followed by nothing else. We work around the
  146
+	 * syntax issue by making these tags "sticky".
  147
+	 *
  148
+	 * This subtly changes the semantics. Also, changing assign_default
  149
+	 * is not a good idea, since the caller may rely on it staying the
  150
+	 * same.
  151
+	 */
  152
+	state->comm->assign_default = C;
  153
+}
  154
+
  155
+context(C) ::= TOK_PER_FRAME TOK_ASSIGN. {
  156
+	C = state->comm->assign_per_frame;
  157
+}
  158
+
  159
+context(C) ::= TOK_PER_VERTEX TOK_ASSIGN. {
  160
+	C = state->comm->assign_per_vertex;
  161
+}
  162
+
  163
+context(C) ::= TOK_PER_PIXEL TOK_ASSIGN. {
  164
+	C = state->comm->assign_per_vertex;
  165
+}
  166
+
108 167
 opt_semi ::= opt_semi TOK_SEMI.
109 168
 
110 169
 opt_semi ::= .
80  src/compiler/parser_helper.c
@@ -15,6 +15,8 @@
15 15
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16
  */
17 17
 
  18
+#include <stdarg.h>
  19
+#include <stdlib.h>
18 20
 #include <stdio.h>
19 21
 #include <malloc.h>
20 22
 #include <fpvm/ast.h>
@@ -24,17 +26,59 @@
24 26
 #include "parser_itf.h"
25 27
 #include "parser_helper.h"
26 28
 
27  
-int fpvm_parse(const char *expr, int start_token, union parser_comm *comm)
  29
+static char printable_char(unsigned char c)
  30
+{
  31
+	return c < ' ' || c > '~' ? '?' : c;
  32
+}
  33
+
  34
+/*
  35
+ * Since operators don't set "label" properly in unique(), we can't just print
  36
+ * the whole string, but need to cut it at the first non-printable character.
  37
+ */
  38
+
  39
+static int printable_label(const char *s)
  40
+{
  41
+	const char *p;
  42
+
  43
+	for(p = s; *p > ' '; p++);
  44
+	return p-s;
  45
+}
  46
+
  47
+static const char *alloc_printf(const char *fmt, ...)
  48
+{
  49
+	va_list ap;
  50
+	int n;
  51
+	char *s;
  52
+
  53
+	va_start(ap, fmt);
  54
+	n = vsnprintf(NULL, 0, fmt, ap);
  55
+	va_end(ap);
  56
+
  57
+	s = malloc(n+1);
  58
+
  59
+	va_start(ap, fmt);
  60
+	vsnprintf(s, n+1, fmt, ap);
  61
+	va_end(ap);
  62
+
  63
+	return s;
  64
+}
  65
+
  66
+const char *fpvm_parse(const char *expr, int start_token,
  67
+    struct parser_comm *comm)
28 68
 {
29 69
 	struct scanner *s;
30 70
 	struct parser_state state = {
31 71
 		.comm = comm,
32 72
 		.success = 0,
  73
+		.error = NULL,
  74
+		.error_label = NULL,
  75
+		.id = NULL,
33 76
 	};
34 77
 	int tok;
35 78
 	struct id *identifier;
36 79
 	void *p;
37  
-	
  80
+	const char *error = NULL;
  81
+
38 82
 	s = new_scanner((unsigned char *)expr);
39 83
 	p = ParseAlloc(malloc);
40 84
 	Parse(p, start_token, NULL, &state);
@@ -42,19 +86,31 @@ int fpvm_parse(const char *expr, int start_token, union parser_comm *comm)
42 86
 	while(tok != TOK_EOF) {
43 87
 		identifier = malloc(sizeof(struct id));
44 88
 		identifier->token = tok;
45  
-		if(tok == TOK_CONSTANT) {
  89
+		identifier->lineno = s->lineno;
  90
+
  91
+		switch(tok) {
  92
+		case TOK_CONSTANT:
46 93
 			identifier->constant = get_constant(s);
47 94
 			identifier->label = "";
48  
-		} else {
  95
+			break;
  96
+		case TOK_FNAME:
49 97
 			identifier->label = get_token(s);
  98
+			break;
  99
+		default:
  100
+			identifier->label = get_unique_token(s);
  101
+			break;
50 102
 		}
51  
-		Parse(p, tok, identifier, &state);
  103
+
  104
+		state.id = identifier;
52 105
 		if(tok == TOK_ERROR) {
53  
-			printf("FPVM: scan error\n");
  106
+			error = alloc_printf(
  107
+			    "FPVM, line %d: scan error near '%c'",
  108
+			    s->lineno, printable_char(s->cursor[-1]));
54 109
 			ParseFree(p, free);
55 110
 			delete_scanner(s);
56  
-			return 0;
  111
+			return error;
57 112
 		}
  113
+		Parse(p, tok, identifier, &state);
58 114
 		tok = scan(s);
59 115
 	}
60 116
 	Parse(p, TOK_EOF, NULL, &state);
@@ -62,11 +118,15 @@ int fpvm_parse(const char *expr, int start_token, union parser_comm *comm)
62 118
 	delete_scanner(s);
63 119
 
64 120
 	if(!state.success) {
65  
-		printf("FPVM: parse error\n");
66  
-		return 0;
  121
+		error = alloc_printf(
  122
+		    "FPVM, line %d: %s near '%.*s'",
  123
+		    state.error_lineno,
  124
+		    state.error ? state.error : "parse error",
  125
+		    printable_label(state.error_label), state.error_label);
  126
+		free((void *) state.error);
67 127
 	}
68 128
 
69  
-	return state.success;
  129
+	return error;
70 130
 }
71 131
 
72 132
 void fpvm_parse_free(struct ast_node *node)
23  src/compiler/parser_helper.h
@@ -21,13 +21,26 @@
21 21
 #include <fpvm/ast.h>
22 22
 #include <fpvm/fpvm.h>
23 23
 
24  
-union parser_comm {
25  
-	struct ast_node *parseout;
26  
-	struct fpvm_fragment *fragment;
  24
+struct compiler_sc;
  25
+
  26
+struct parser_comm {
  27
+	union {
  28
+		struct ast_node *parseout;
  29
+		struct fpvm_fragment *fragment;
  30
+		struct compiler_sc *sc;
  31
+	} u;
  32
+	const char *(*assign_default)(struct parser_comm *comm,
  33
+	    const char *label, struct ast_node *node);
  34
+	const char *(*assign_per_frame)(struct parser_comm *comm,
  35
+	    const char *label, struct ast_node *node);
  36
+	const char *(*assign_per_vertex)(struct parser_comm *comm,
  37
+	    const char *label, struct ast_node *node);
  38
+	const char *(*assign_image_name)(struct parser_comm *comm,
  39
+	    int number, const char *name);
27 40
 };
28 41
 
29  
-int fpvm_parse(const char *expr, int start_token,
30  
-    union parser_comm *comm);
  42
+const char *fpvm_parse(const char *expr, int start_token,
  43
+    struct parser_comm *comm);
31 44
 void fpvm_parse_free(struct ast_node *node);
32 45
 
33 46
 #endif /* __PARSER_HELPER_H */
7  src/compiler/parser_itf.h
@@ -29,11 +29,16 @@ struct id {
29 29
 	int token;
30 30
 	const char *label;
31 31
 	float constant;
  32
+	int lineno;
32 33
 };
33 34
 
34 35
 struct parser_state {
35 36
 	int success;
36  
-	union parser_comm *comm;
  37
+	struct parser_comm *comm;
  38
+	const char *error;	/* malloc'ed error message or NULL */
  39
+	const char *error_label;/* details about the failing token */
  40
+	int error_lineno;
  41
+	const struct id *id;	/* input, for error handling */
37 42
 };
38 43
 
39 44
 void *ParseAlloc(void *(*mallocProc)(size_t));
2  src/compiler/ptest/Makefile
@@ -37,7 +37,7 @@ ptest:		$(OBJS)
37 37
 		$(CC) $(CFLAGS) -c -o $@ $<
38 38
 
39 39
 %.c:		%.re
40  
-		$(GEN) re2c -o $@ $<
  40
+		$(GEN) re2c -c -o $@ $<
41 41
 
42 42
 %.c:		%.y
43 43
 		$(GEN) lemon $<
76  src/compiler/ptest/ptest.c
@@ -12,6 +12,7 @@
12 12
 #include <stdlib.h>
13 13
 #include <stdio.h>
14 14
 #include <unistd.h>
  15
+#include <string.h>
15 16
 
16 17
 #include "../fpvm.h"
17 18
 #include "../parser_helper.h"
@@ -19,6 +20,7 @@
19 20
 
20 21
 
21 22
 static int quiet = 0;
  23
+static const char *fail = NULL;
22 24
 
23 25
 
24 26
 static void dump_ast(const struct ast_node *ast);
@@ -133,18 +135,58 @@ static void dump_ast(const struct ast_node *ast)
133 135
 }
134 136
 
135 137
 
136  
-int fpvm_do_assign(struct fpvm_fragment *fragment, const char *dest,
137  
-    struct ast_node *ast)
  138
+const char *fpvm_get_last_error(struct fpvm_fragment *fragment)
138 139
 {
  140
+	return fragment->last_error;
  141
+}
  142
+
  143
+
  144
+static const char *assign_default(struct parser_comm *comm,
  145
+    const char *label, struct ast_node *node)
  146
+{
  147
+	if (fail)
  148
+		return strdup(fail);
139 149
 	if (!quiet) {
140  
-		printf("%s = ", dest);
141  
-		dump_ast(ast);
  150
+		printf("%s = ", label);
  151
+		dump_ast(node);
142 152
 		putchar('\n');
143 153
 	}
144  
-	return 1;
  154
+	return NULL;
  155
+}
  156
+
  157
+
  158
+static const char *assign_per_frame(struct parser_comm *comm,
  159
+    const char *label, struct ast_node *node)
  160
+{
  161
+	if (!quiet) {
  162
+		printf("per_frame = %s = ", label);
  163
+		dump_ast(node);
  164
+		putchar('\n');
  165
+	}
  166
+	return NULL;
145 167
 }
146 168
 
147 169
 
  170
+static const char *assign_per_vertex(struct parser_comm *comm,
  171
+    const char *label, struct ast_node *node)
  172
+{
  173
+	if (!quiet) {
  174
+		printf("per_vertex = %s = ", label);
  175
+		dump_ast(node);
  176
+		putchar('\n');
  177
+	}
  178
+	return NULL;
  179
+}
  180
+
  181
+
  182
+static const char *assign_image_name(struct parser_comm *comm,
  183
+    int number, const char *name)
  184
+{
  185
+	if (!quiet)
  186
+		printf("image %d = \"%s\"\n", number, name);
  187
+	return NULL;
  188
+}
  189
+
148 190
 static const char *read_stdin(void)
149 191
 {
150 192
 	char *buf = NULL;
@@ -177,7 +219,7 @@ static const char *read_stdin(void)
177 219
 
178 220
 static void usage(const char *name)
179 221
 {
180  
-	fprintf(stderr, "usage: %s [-q] [expr]\n", name);
  222
+	fprintf(stderr, "usage: %s [-f error] [-q] [expr]\n", name);
181 223
 	exit(1);
182 224
 }
183 225
 
@@ -186,10 +228,21 @@ int main(int argc, char **argv)
186 228
 {
187 229
 	int c;
188 230
 	const char *buf;
189  
-	union parser_comm comm;
  231
+	struct fpvm_fragment fragment;
  232
+	struct parser_comm comm = {
  233
+		.u.fragment = &fragment,
  234
+		.assign_default = assign_default,
  235
+		.assign_per_frame = assign_per_frame,
  236
+		.assign_per_vertex = assign_per_vertex,
  237
+		.assign_image_name = assign_image_name,
  238
+	 };
  239
+	const char *error;
190 240
 
191  
-	while ((c = getopt(argc, argv, "q")) != EOF)
  241
+	while ((c = getopt(argc, argv, "f:q")) != EOF)
192 242
 		switch (c) {
  243
+		case 'f':
  244
+			fail = optarg;
  245
+			break;
193 246
 		case 'q':
194 247
 			quiet = 1;
195 248
 			break;
@@ -207,5 +260,10 @@ int main(int argc, char **argv)
207 260
 		usage(*argv);
208 261
 	}
209 262
 
210  
-	return !fpvm_parse(buf, TOK_START_ASSIGN, &comm);
  263
+	error = fpvm_parse(buf, TOK_START_ASSIGN, &comm);
  264
+	if (!error)
  265
+		return 0;
  266
+	fflush(stdout);
  267
+	fprintf(stderr, "%s\n", error);
  268
+	return 1;
211 269
 }
11  src/compiler/scanner.h
@@ -23,11 +23,19 @@
23 23
 
24 24
 #include "parser.h"
25 25
 
  26
+enum scanner_cond {
  27
+	yycN = 1,
  28
+	yycFNAME1,
  29
+	yycFNAME2,
  30
+};
  31
+
26 32
 struct scanner {
  33
+	enum scanner_cond cond;
27 34
 	unsigned char *marker;
28 35
 	unsigned char *old_cursor;
29 36
 	unsigned char *cursor;
30 37
 	unsigned char *limit;
  38
+	int lineno;
31 39
 };
32 40
 
33 41
 struct scanner *new_scanner(unsigned char *input);
@@ -38,6 +46,9 @@ int scan(struct scanner *s);
38 46
 
39 47
 /* get the unique string comprising the current token
40 48
  */
  49
+const char *get_unique_token(struct scanner *s);
  50
+
  51
+/* like get_unique_token, but malloc'ed non-unique string */
41 52
 const char *get_token(struct scanner *s);
42 53
 
43 54
 float get_constant(struct scanner *s);
150  src/compiler/scanner.re
@@ -15,6 +15,7 @@
15 15
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16
  */
17 17
 
  18
+#include <stdlib.h>
18 19
 #include <stdio.h>
19 20
 #include <string.h>
20 21
 #include <malloc.h>
@@ -30,18 +31,24 @@
30 31
 #define YYMARKER s->marker
31 32
 #define YYFILL(n)
32 33
 
  34
+#define	YYCONDTYPE		enum scanner_cond
  35
+#define	YYGETCONDITION()	s->cond
  36
+#define	YYSETCONDITION(c)	s->cond = (c)
  37
+
33 38
 struct scanner *new_scanner(unsigned char *input)
34 39
 {
35 40
 	struct scanner *s;
36  
-	
  41
+
37 42
 	s = malloc(sizeof(struct scanner));
38 43
 	if(s == NULL) return NULL;
39  
-	
  44
+
  45
+	s->cond = yycN;
40 46
 	s->marker = input;
41 47
 	s->old_cursor = input;
42 48
 	s->cursor = input;
43 49
 	s->limit = input + strlen((char *)input);
44  
-	
  50
+	s->lineno = 1;
  51
+
45 52
 	return s;
46 53
 }
47 54
 
@@ -50,64 +57,113 @@ void delete_scanner(struct scanner *s)
50 57
 	free(s);
51 58
 }
52 59
 
  60
+static int nls(const unsigned char *s, const unsigned char *end)
  61
+{
  62
+	int n = 0;
  63
+
  64
+	while(s != end)
  65
+		if(*s++ == '\n')
  66
+			n++;
  67
+	return n;
  68
+}
  69
+
  70
+/*
  71
+ * Regular expression for C-style comments by Stephen Ostermiller, from
  72
+ * http://ostermiller.org/findcomment.html
  73
+ */
  74
+
53 75
 int scan(struct scanner *s)
54 76
 {
55 77
 	std:
56 78
 	if(s->cursor == s->limit) return TOK_EOF;
57 79
 	s->old_cursor = s->cursor;
58  
-	
  80
+
59 81
 	/*!re2c
60  
-		[\x20\n\r\t]		{ goto std; }
61  
-
62  
-		"//"[^\n\x00]*		{ goto std; }
63  
-		"/*"("*"*[^/\x00]|[^*\x00])*"*"+"/"
64  
-					{ goto std; }
65  
-
66  
-		[0-9]+			{ return TOK_CONSTANT; }
67  
-		[0-9]+ "." [0-9]*	{ return TOK_CONSTANT; }
68  
-		[0-9]* "." [0-9]+	{ return TOK_CONSTANT; }
69  
-
70  
-		"above"			{ return TOK_ABOVE; }
71  
-		"abs"			{ return TOK_ABS; }
72  
-		"below"			{ return TOK_BELOW; }
73  
-		"cos"			{ return TOK_COS; }
74  
-		"equal"			{ return TOK_EQUAL; }
75  
-		"f2i"			{ return TOK_F2I; }
76  
-		"icos"			{ return TOK_ICOS; }
77  
-		"i2f"			{ return TOK_I2F; }
78  
-		"if"			{ return TOK_IF; }
79  
-		"int"			{ return TOK_INT; }
80  
-		"invsqrt"		{ return TOK_INVSQRT; }
81  
-		"isin"			{ return TOK_ISIN; }
82  
-		"max"			{ return TOK_MAX; }
83  
-		"min"			{ return TOK_MIN; }
84  
-		"quake"			{ return TOK_QUAKE; }
85  
-		"sin"			{ return TOK_SIN; }
86  
-		"sqr"			{ return TOK_SQR; }
87  
-		"sqrt"			{ return TOK_SQRT; }
88  
-		"tsign"			{ return TOK_TSIGN; }
89  
-
90  
-		[a-zA-Z_0-9]+		{ return TOK_IDENT; }
91  
-		"+"			{ return TOK_PLUS; }
92  
-		"-"			{ return TOK_MINUS; }
93  
-		"*"			{ return TOK_MULTIPLY; }
94  
-		"/"			{ return TOK_DIVIDE; }
95  
-		"%"			{ return TOK_PERCENT; }
96  
-		"("			{ return TOK_LPAREN; }
97  
-		")"			{ return TOK_RPAREN; }
98  
-		","			{ return TOK_COMMA; }
99  
-		"="			{ return TOK_ASSIGN; }
100  
-		";"			{ return TOK_SEMI; }
101  
-		[\x00-\xff]		{ return TOK_ERROR; }
  82
+		<*>[\x20\r\t]		{ goto std; }
  83
+		<*>"\n"			{ s->lineno++;
  84
+					  YYSETCONDITION(yycN);
  85
+					  goto std; }
  86
+
  87
+		<N>"//"[^\n\x00]*	{ goto std; }
  88
+		<N>"/*"([^*\x00]|("*"+([^*/\x00])))*"*"+"/"
  89
+					{ s->lineno += nls(s->old_cursor,
  90
+					      s->cursor);
  91
+					  goto std; }
  92
+
  93
+		<N>"[preset]"		{ goto std; }
  94
+
  95
+		<N>[0-9]+		{ return TOK_CONSTANT; }
  96
+		<N>[0-9]+ "." [0-9]*	{ return TOK_CONSTANT; }
  97
+		<N>[0-9]* "." [0-9]+	{ return TOK_CONSTANT; }
  98
+
  99
+		<N>"above"		{ return TOK_ABOVE; }
  100
+		<N>"abs"		{ return TOK_ABS; }
  101
+		<N>"below"		{ return TOK_BELOW; }
  102
+		<N>"cos"		{ return TOK_COS; }
  103
+		<N>"equal"		{ return TOK_EQUAL; }
  104
+		<N>"f2i"		{ return TOK_F2I; }
  105
+		<N>"icos"		{ return TOK_ICOS; }
  106
+		<N>"i2f"		{ return TOK_I2F; }
  107
+		<N>"if"			{ return TOK_IF; }
  108
+		<N>"int"		{ return TOK_INT; }
  109
+		<N>"invsqrt"		{ return TOK_INVSQRT; }
  110
+		<N>"isin"		{ return TOK_ISIN; }
  111
+		<N>"max"		{ return TOK_MAX; }
  112
+		<N>"min"		{ return TOK_MIN; }
  113
+		<N>"quake"		{ return TOK_QUAKE; }
  114
+		<N>"sin"		{ return TOK_SIN; }
  115
+		<N>"sqr"		{ return TOK_SQR; }
  116
+		<N>"sqrt"		{ return TOK_SQRT; }
  117
+		<N>"tsign"		{ return TOK_TSIGN; }
  118
+
  119
+		<N>"per_frame"[a-z_0-9]*
  120
+					{ return TOK_PER_FRAME; }
  121
+		<N>"per_vertex"		{ return TOK_PER_VERTEX; }
  122
+		<N>"per_pixel"		{ return TOK_PER_PIXEL; }
  123
+
  124
+		<N>"imagefile"[1-9]	{ YYSETCONDITION(yycFNAME1);
  125
+					  return TOK_IMAGEFILE; }
  126
+
  127
+		<N>[a-zA-Z_0-9]+	{ return TOK_IDENT; }
  128
+
  129
+		<N>"+"			{ return TOK_PLUS; }
  130
+		<N>"-"			{ return TOK_MINUS; }
  131
+		<N>"*"			{ return TOK_MULTIPLY; }
  132
+		<N>"/"			{ return TOK_DIVIDE; }
  133
+		<N>"%"			{ return TOK_PERCENT; }
  134
+		<N>"("			{ return TOK_LPAREN; }
  135
+		<N>")"			{ return TOK_RPAREN; }
  136
+		<N>","			{ return TOK_COMMA; }
  137
+		<N,FNAME1>"="		{ if (YYGETCONDITION() == yycFNAME1)
  138
+						YYSETCONDITION(yycFNAME2);
  139
+					  return TOK_ASSIGN; }
  140
+		<N>";"			{ return TOK_SEMI; }
  141
+
  142
+		<FNAME2>[^ \x00\n\r\t]|[^ \x00\n\r\t][^\n\x00]*[^ \x00\n\r\t]
  143
+					{ return TOK_FNAME; }
  144
+
  145
+		<*>[\x00-\xff]		{ return TOK_ERROR; }
102 146
 	*/
103 147
 }
104 148
 
105  
-const char *get_token(struct scanner *s)
  149
+const char *get_unique_token(struct scanner *s)
106 150
 {
107 151
 	return unique_n((const char *) s->old_cursor,
108 152
 	    s->cursor - s->old_cursor);
109 153
 }
110 154
 
  155
+const char *get_token(struct scanner *s)
  156
+{
  157
+	char *buf;
  158
+	int n;
  159
+
  160
+	n = s->cursor - s->old_cursor;
  161
+	buf = malloc(n+1);
  162
+	memcpy(buf, s->old_cursor, n);
  163
+	buf[n] = 0;
  164
+	return buf;
  165
+}
  166
+
111 167
 float get_constant(struct scanner *s)
112 168
 {
113 169
 	const unsigned char *p;
27  src/compiler/test/comment
@@ -113,7 +113,7 @@ a = 9 /*/
113 113
 b = a
114 114
 EOF
115 115
 expect <<EOF
116  
-FPVM: parse error
  116
+FPVM, line 1: parse error near '*/'
117 117
 EOF
118 118
 
119 119
 #------------------------------------------------------------------------------
@@ -123,14 +123,35 @@ a = b + c /* comment
123 123
 d = e + f
124 124
 EOF
125 125
 expect <<EOF
126  
-FPVM: parse error
  126
+FPVM, line 1: parse error near '*'
127 127
 EOF
128 128
 
129 129
 #------------------------------------------------------------------------------
130 130
 
131 131
 ptest_fail "comment: unterminated /* ... without newline" "a = b+c /* comment"
132 132
 expect <<EOF
133  
-FPVM: parse error
  133
+FPVM, line 1: parse error near '*'
  134
+EOF
  135
+
  136
+#------------------------------------------------------------------------------
  137
+
  138
+ptest "comment: multiple /*...*/ comments" <<EOF
  139
+/* */ a /* */
  140
+/***/ =  /**/
  141
+/**/  b /* */
  142
+EOF
  143
+expect <<EOF
  144
+a = b
  145
+EOF
  146
+
  147
+#------------------------------------------------------------------------------
  148
+
  149
+ptest "comment: [preset]" <<EOF
  150
+  [preset]
  151
+x=y
  152
+EOF
  153
+expect <<EOF
  154
+x = y
134 155
 EOF
135 156
 
136 157
 ###############################################################################
15  src/compiler/test/error
@@ -7,7 +7,7 @@ ptest_fail "syntax error: x = backtick" <<EOF
7 7
 x = \`
8 8
 EOF
9 9
 expect <<EOF
10  
-FPVM: scan error
  10
+FPVM, line 1: scan error near '\`'
11 11
 EOF
12 12
 
13 13
 #------------------------------------------------------------------------------
@@ -17,7 +17,7 @@ x = a b
17 17
 EOF
18 18
 expect <<EOF
19 19
 x = a
20  
-FPVM: parse error
  20
+FPVM, line 1: parse error near 'b'
21 21
 EOF
22 22
 
23 23
 #------------------------------------------------------------------------------
@@ -26,7 +26,16 @@ ptest_fail "syntax error: x = a + + b" <<EOF
26 26
 x = a + + b
27 27
 EOF
28 28
 expect <<EOF
29  
-FPVM: parse error
  29
+FPVM, line 1: parse error near '+'
  30
+EOF
  31
+
  32
+#------------------------------------------------------------------------------
  33
+
  34
+ptest_fail "code generation error" -f "codegen" <<EOF
  35
+x = a + b
  36
+EOF
  37
+expect <<EOF
  38
+FPVM, line 1: codegen near 'b'
30 39
 EOF
31 40
 
32 41
 ###############################################################################
69  src/compiler/test/image
... ...
@@ -0,0 +1,69 @@
  1
+#!/bin/sh
  2
+. ./Common
  3
+
  4
+# WORK IN PROGRESS
  5
+
  6
+###############################################################################
  7
+
  8
+ptest "image: imagefile1=hello" <<EOF
  9
+imagefile1=hello
  10
+EOF
  11
+expect <<EOF
  12
+image 1 = "hello"
  13
+EOF
  14
+
  15
+#------------------------------------------------------------------------------
  16
+
  17
+ptest "image: imagefile2 = hello" <<EOF
  18
+imagefile2 = hello
  19
+EOF
  20
+expect <<EOF
  21
+image 2 = "hello"
  22
+EOF
  23
+
  24
+#------------------------------------------------------------------------------
  25
+
  26
+ptest "image: imagefile1 = hel lo" <<EOF
  27
+imagefile1 = hel lo
  28
+EOF
  29
+expect <<EOF
  30
+image 1 = "hel lo"
  31
+EOF
  32
+
  33
+#------------------------------------------------------------------------------
  34
+
  35
+ptest "image: imagefile2 = hel<nl>lo" <<EOF
  36
+imagefile2 = hel
  37
+lo = u
  38
+EOF
  39
+expect <<EOF
  40
+image 2 = "hel"
  41
+lo = u
  42
+EOF
  43
+
  44
+#------------------------------------------------------------------------------
  45
+
  46
+ptest "image: imagefile1 = foo<spc><spc>" "imagefile1 = foo  "
  47
+expect <<EOF
  48
+image 1 = "foo"
  49
+EOF
  50
+
  51
+#------------------------------------------------------------------------------
  52
+
  53
+ptest "image: imagefile2 = /dev/null" <<EOF
  54
+imagefile2 = /dev/null
  55
+EOF
  56
+expect <<EOF
  57
+image 2 = "/dev/null"
  58
+EOF
  59
+
  60
+#------------------------------------------------------------------------------
  61
+
  62
+ptest "image: imagefile1 = *** test  -  robust & ness ***" <<EOF
  63
+imagefile1 = *** test  -  robust & ness ***
  64
+EOF
  65
+expect <<EOF
  66
+image 1 = "*** test  -  robust & ness ***"
  67
+EOF
  68
+
  69
+###############################################################################
158  src/compiler/test/location
... ...
@@ -0,0 +1,158 @@
  1
+#!/bin/sh
  2
+. ./Common
  3
+
  4
+###############################################################################
  5
+
  6
+ptest_fail "location: scanner, inside line" <<EOF
  7
+a = b
  8
+x = \` y
  9
+c = d
  10
+EOF
  11
+expect <<EOF
  12
+a = b
  13
+FPVM, line 2: scan error near '\`'
  14
+EOF
  15
+
  16
+#------------------------------------------------------------------------------
  17
+
  18
+ptest_fail "location: scanner, beginning of line" <<EOF
  19
+a = b
  20
+\`x = y
  21
+c = d
  22
+EOF
  23
+expect <<EOF
  24
+FPVM, line 2: scan error near '\`'
  25
+EOF
  26
+
  27
+#------------------------------------------------------------------------------
  28
+
  29
+ptest_fail "location: scanner, end of line" <<EOF
  30
+a = b
  31
+x = y\`
  32
+c = d
  33
+EOF
  34
+expect <<EOF
  35
+a = b
  36
+FPVM, line 2: scan error near '\`'
  37
+EOF
  38
+
  39
+#------------------------------------------------------------------------------
  40
+
  41
+ptest_fail "location: parser, inside line" <<EOF
  42
+a = b
  43
+x = * y
  44
+c = d
  45
+EOF
  46
+expect <<EOF
  47
+a = b
  48
+FPVM, line 2: parse error near '*'
  49
+EOF
  50
+
  51
+#------------------------------------------------------------------------------
  52
+
  53
+ptest_fail "location: parser, beginning of line" <<EOF
  54
+a = b
  55
+)x = y
  56
+c = d
  57
+EOF
  58
+expect <<EOF
  59
+a = b
  60
+FPVM, line 2: parse error near ')x'
  61
+EOF
  62
+
  63
+#------------------------------------------------------------------------------
  64
+
  65
+ptest_fail "location: parser, end of line (1)" <<EOF