%{
/* This is a LEX file and is compiled via:
	lex FILE.lex
	cc lex.yy.c -ll
  It reads stdin and writes stdout.
*/

/* HPGL Parser -- Written by Taed Nelson (nelson@berlioz.nsc.com)
   Copyright 1992, National Semiconductor Corporation.

   Although this is copyrighted, feel free to distribute and modify, so long
     as the above notice is kept intact.  Please send changes to the author so
     they can be included in the future.

   09 May 92 -- Initial version.
   11 May 92 -- Added support for RS-232 control.
   24 May 92 -- Minor changes for "any character"
   11 Jun 92 -- Added states to properly handle labels.
		I don't know what the story is with {string} since the books
		don't specify single or double quotes.
*/

#define DEBUG		0

#define ALL		0
#define	HPGL1		1
#define HPGL2		2
#define TECHNICAL	4
#define PALETTE		8
#define DUAL_CONTEXT	16
#define DIGITIZING	32
#define RS232		64
#define NOVAJET		128
#define PARSE		512

unsigned long line = 1;
unsigned int errorStringLength = 0;
char errorString[1000];
char LabelTerminator = '\003';

void match (char * string, unsigned long support);
%}


%Start LABEL

%e 10000
%p 20000
%n 2000
%k 15000
%a 10000
%o 10000


digit		[0-9]
int127		(((0)?({digit})?{digit})|(1[012]{digit}))
int		-?{digit}+
float		({int}|({int})?\.{int})
coord		{int}\,{int}
char		(.|\n)
string		['"]([^'"])*['"]


%%


<INITIAL>\033\%\#A						match ("Enter PCL Mode", HPGL2 | DUAL_CONTEXT | NOVAJET);
<INITIAL>\033E							match ("Reset", HPGL2 | DUAL_CONTEXT | NOVAJET);
<INITIAL>\033\.\(						|
<INITIAL>\033\.Y						match ("Plotter On", RS232);
<INITIAL>\033\.\)						|
<INITIAL>\033\.Z						match ("Plotter Off", RS232);
<INITIAL>\033\.\@(({int})?\;({int})?)?\:			match ("Set Plotter Configuration", RS232);
<INITIAL>\033\.B						match ("Output Buffer Space", RS232);
<INITIAL>\033\.E						match ("Output Extended Error", RS232);
<INITIAL>\033\.H(({int})?\;({int127})?(\;{int127}){1,10})?\:	match ("Set Handshake Mode 1", RS232);
<INITIAL>\033\.I(({int})?\;({int127})?(\;{int127}){1,10})?\:	match ("Set Handshake Mode 2", RS232);
<INITIAL>\033\.J						match ("Abort Device Control", RS232);
<INITIAL>\033\.K						match ("Abort Graphic Instruction", RS232);
<INITIAL>\033\.L						match ("Output Buffer Size", RS232);
<INITIAL>\033\.M(({int})?\;({int127})?\;({int127})?\;({int127})?(\;{int127})?\;{int127})?\:		match ("Set Output Mode", RS232);
<INITIAL>\033\.N(({int})?(\;{int127}){1,10})?\:			match ("Set Extended Output and Handshake Mode", RS232);
<INITIAL>\033\.O						match ("Output Extended Status", RS232);
<INITIAL>\033\.R						match ("Reset Handshake", RS232);
<INITIAL>AA{coord}\,{float}(\,{float})?				match ("Arc Absolute", ALL);
<INITIAL>AC({coord})?						match ("Anchor Corner", HPGL2);
<INITIAL>AD([1-7]\,{float}(\,[1-7]\,{float})*)?			match ("Alternate Font Definition", HPGL2 | NOVAJET);
<INITIAL>AR{coord}\,{float}(\,{float})?				match ("Arc Relative", ALL);
<INITIAL>AT{coord}\,{coord}(\,{float})?				match ("Arc Absolute Three Point", HPGL2);
<INITIAL>BP((1\,{string}|2\,{int}|3\,[01]|4\,[01])(\,(1\,{string}|2\,{int}|3\,[01]|4\,[01]))*)?		match ("Begin Plot", HPGL2 | TECHNICAL | PARSE);
<INITIAL>CA{int}						match ("Alternate Character Set", HPGL1);
<INITIAL>CF([0123](\,{int})?)?					match ("Character Fill Mode", HPGL2 | NOVAJET);
<INITIAL>CI{int}(\,{float})?					match ("Circle", ALL);
<INITIAL>CP({float}\,{float})?					match ("Character Plot", ALL);
<INITIAL>CR							match ("Color Range", HPGL2 | PALETTE | PARSE);
<INITIAL>CS{int}						match ("Standard Character Set", HPGL1);
<INITIAL>CT([01])?						match ("Chord Tolerance", HPGL2 | TECHNICAL);
<INITIAL>DC\;							match ("Digitize Clear", DIGITIZING | NOVAJET);
<INITIAL>DF							match ("Default", ALL);
<INITIAL>DI({float}\,{float})?					match ("Absolute Direction", ALL);
<INITIAL>DL							match ("Download Character", HPGL2 | TECHNICAL | PARSE);
<INITIAL>DP\;							match ("Digitize Point", DIGITIZING | NOVAJET);
<INITIAL>DR({float}\,{float})?					match ("Relative Direction", ALL);
<INITIAL>DT\;							{
									LabelTerminator = '\003';
									match ("Define Label Terminator", ALL | NOVAJET);
								}
<INITIAL>DT{char}(\,[01])?\;					{
									LabelTerminator = yytext[2];
									match ("Define Label Terminator", ALL | NOVAJET);
								}
<INITIAL>DV([0123](\,[01])?)?					match ("Define Variable Text Path", HPGL2 | NOVAJET);
<INITIAL>EA{coord}						match ("Edge Rectangle Absolute", ALL);
<INITIAL>EC({int})?						match ("Enable Cutter", HPGL2 | TECHNICAL);
<INITIAL>EP							match ("Edge Polygon", HPGL2);
<INITIAL>ER{coord}						match ("Edge Rectangle Relative", ALL);
<INITIAL>ES({float}(\,{float})?)?				match ("Extra Space", HPGL2);
<INITIAL>EW{coord}\,{float}\,{float}(\,{float})?		match ("Edge Wedge", ALL);
<INITIAL>FI{int}						match ("Primary Font Selection", HPGL2 | DUAL_CONTEXT | NOVAJET);
<INITIAL>FN{int}						match ("Secondary Font Selection", HPGL2 | DUAL_CONTEXT | NOVAJET);
<INITIAL>FP							match ("Fill Polygon", HPGL2);
<INITIAL>FR							match ("Frame Advance", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>FT(1|2|3|4|10|11)(\,{float}(\,{float})?)?		match ("Fill Type", ALL | NOVAJET);
<INITIAL>IM{int}(\,{int}(\,{int})?)?				match ("Input Mask", HPGL1);
<INITIAL>IN							match ("Initialize", ALL);
<INITIAL>IN({int})?						match ("Initialize", ALL | NOVAJET);
<INITIAL>IP({coord}(\,{coord})?)?				match ("Input Scale Points", ALL);
<INITIAL>IR({coord}(\,{coord})?)?				match ("Input Relative Scale Points", HPGL2);
<INITIAL>IW({coord}\,{coord})?					match ("Input Window", ALL);
<INITIAL>LA((1\,(1|2|3|4)|2\,(1|2|3|4|5|6)|3\,{float})(\,(1\,(1|2|3|4)|2\,(1|2|3|4|5|6)|3\,{float}))*)?		match ("Line Attributes", HPGL2);
<INITIAL>LB							{
									BEGIN LABEL;
									match ("Label", ALL);
								}
<LABEL>.|\n							{
									if (yytext[0] == LabelTerminator)
										BEGIN INITIAL;
								}
<INITIAL>LO(1?[1-9])?						match ("Label Origin", HPGL2);
<INITIAL>LT(99|(\-)?[0-8](\,{float}(\,(0|1))?)?)?		match ("Line Type", ALL);
<INITIAL>MC([01])?						match ("Merge Control", HPGL2 | TECHNICAL);
<INITIAL>MG({string})?						match ("Message", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>MT([0-5])?						match ("Media Type", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>NP({int})?						match ("Number of Pens", HPGL2 | PALETTE);
<INITIAL>NR({int})?						match ("Not Ready", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>OA\;							match ("Output Actual Position", HPGL1);
<INITIAL>OC\;							match ("Output Command Position", HPGL1);
<INITIAL>OD\;							match ("Output Digitized Status", DIGITIZING | NOVAJET);
<INITIAL>OE\;							match ("Output Error", ALL | TECHNICAL);
<INITIAL>OF\;							match ("Output Factors", HPGL1);
<INITIAL>OH\;							match ("Output Hard-Clip Limits", ALL | TECHNICAL);
<INITIAL>OI\;							match ("Output Identification", HPGL1 | TECHNICAL);
<INITIAL>OO\;							match ("Output Options", HPGL1);
<INITIAL>OP\;							match ("Match P1 and P2", ALL | TECHNICAL);
<INITIAL>OS\;							match ("Match Status", ALL | TECHNICAL);
<INITIAL>OW\;							match ("Output Window", HPGL1);
<INITIAL>PA({coord}(\,{coord})*)?				match ("Plot Absolute", ALL);
<INITIAL>PC							match ("Pen Color Assignment", HPGL2 | PALETTE | PARSE);
<INITIAL>PD({coord}(\,{coord})*)?				match ("Pen Down", ALL);
<INITIAL>PE\;							|
<INITIAL>PE							match ("Polyline Encoded", HPGL2 | PARSE);
<INITIAL>PG({int})?\;						match ("Advance Page", HPGL2);
<INITIAL>PM([012])?						match ("Polygon Mode", HPGL2);
<INITIAL>PR({coord}(\,{coord})*)?				match ("Plot Relative", ALL);
<INITIAL>PS({int}(\,{int})?)?					match ("Plot Size", ALL | TECHNICAL | NOVAJET);
<INITIAL>PT{float}						match ("Pen Thickness", HPGL1);
<INITIAL>PU({coord}(\,{coord})*)?				match ("Pen Up", ALL);
<INITIAL>PW({float}(\,{int})?)?					match ("Pen Width", HPGL2);
<INITIAL>QL({int})?						match ("Quality Level", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>RA{coord}						match ("Fill Rectangle Absolute", ALL);
<INITIAL>RF([1-8](\,(8|16|32|64)\,(8|16|32|64)(\,{int})*)?)?	match ("Raster Fill Definition", HPGL2 | NOVAJET);
<INITIAL>RO(0|90|180|270)?					match ("Rotate", ALL);
<INITIAL>RP({int})?\;						match ("Replot", HPGL2);
<INITIAL>RR{coord}						match ("Fill Rectangle Relative", ALL);
<INITIAL>RT{coord}\,{coord}(\,{float})?				match ("Arc Relative Three Point", HPGL2);
<INITIAL>SA							match ("Select Alternate Font", ALL);
<INITIAL>SB([01])?						match ("Scalable or Bitmat Fonts", HPGL2 | DUAL_CONTEXT | NOVAJET);
<INITIAL>SC{coord}\,{coord}(\,[012](\,{float}\,{float})?)?	|
<INITIAL>SC{int}\,{float}\,{int}\,{float}\,[012]		|
<INITIAL>SC							match ("Scale", ALL);
<INITIAL>SD([1-7]\,{float}(\,[1-7]\,{float})*)?			match ("Standard Font Definition", HPGL2 | NOVAJET);
<INITIAL>SI({float}\,{float})?					match ("Absolute Character Size", ALL);
<INITIAL>SL({float})?						match ("Character Slant", ALL);
<INITIAL>SM({char})?						match ("Symbol Mode", ALL);
<INITIAL>SP({int})?						match ("Select Pen", ALL);
<INITIAL>SR({float}\,{float})?					match ("Relative Character Size", ALL);
<INITIAL>SS							match ("Select Standard Font", ALL);
<INITIAL>ST(-1|[0-7])?						match ("Sort", HPGL2 | TECHNICAL | NOVAJET);
<INITIAL>SV							match ("Screened Vectors", HPGL2 | PALETTE | NOVAJET | PARSE);
<INITIAL>TD([01])?						match ("Transparent Data", HPGL2 | NOVAJET);
<INITIAL>TL{float}(\,{float})?					match ("Tick Length", HPGL1);
<INITIAL>TR([01])?						match ("Transparency Mode", HPGL2 | PALETTE);
<INITIAL>UC							match ("User-defined Character", HPGL1 | PARSE);
<INITIAL>UL([1-8](\,{int})*)?					match ("User-defined Line Type", HPGL2);
<INITIAL>VS({int}(\,{int})?)?					match ("Velocity Select", ALL | TECHNICAL | NOVAJET);
<INITIAL>WG{coord}\,{float}\,{float}(\,{float})?		match ("Fill Wedge", ALL);
<INITIAL>WU([12])?						match ("Pen Width Unit Selection", HPGL2);
<INITIAL>XT							match ("X-Tick", HPGL1);
<INITIAL>YT							match ("Y-Tick", HPGL1);


<INITIAL>\;							if (errorStringLength != 0) errorString[errorStringLength++] = yytext[0];
<INITIAL>.							errorString[errorStringLength++] = yytext[0];
<INITIAL>\n							{
									match (NULL, ALL);
									line++;
								}


%%

#include <string.h>
#include <ctype.h>

#define HPGL_PRINT_LENGTH	20


void printify (char * string)
{
	unsigned int i;
	char *temp;
	char c;

	for (i = 0; string[i] != '\0'; i++) {
		if (!isprint(string[i])) {
			c = string[i];
			temp = strdup (string + i + 1);
			strcpy (string + i + 4, temp);
			free (temp);
			string[i++] = '\\';
			string[i++] = '0' + (c / 64);
			string[i++] = '0' + ((c % 64) / 8);
			string[i] = '0' + (c % 8);
		}
	}
}


void match (char * string, unsigned long support)
{
	unsigned long power;
	char text[HPGL_PRINT_LENGTH * 4];


	strncpy (text, yytext, HPGL_PRINT_LENGTH);
	printify (text);

	if (errorStringLength > 0) {
		errorString[errorStringLength++] = '\0';
		printify (errorString);
		printf ("PARSE ERROR (%u):\t%s\n", line, errorString);
		errorStringLength = 0;
	}

	for (power = 1; power != 0; power <<= 1) {
		switch (support & power) {
		case ALL:
			break;
		case HPGL1:
			printf ("WARNING (%u):\t\"%.20s\" is found only in HPGL/1.\n", line, text);
			break;
		case HPGL2:
			printf ("WARNING (%u):\t\"%.20s\" is found only in HPGL/2.\n", line, text);
			break;
		case TECHNICAL:
			printf ("WARNING (%u):\t\"%.20s\" is part of the Technical Extension.\n", line, text);
			break;
		case PALETTE:
			printf ("WARNING (%u):\t\"%.20s\" is part of the Palette Extension.\n", line, text);
			break;
		case DUAL_CONTEXT:
			printf ("WARNING (%u):\t\"%.20s\" is part of the Dual-Context Extension.\n", line, text);
			break;
		case DIGITIZING:
			printf ("WARNING (%u):\t\"%.20s\" is part of the Digitizing Extension.\n", line, text);
			break;
		case RS232:
			printf ("WARNING (%u):\t\"%.20s\" is a RS-232 device control instruction.\n", line, text);
			break;
		case NOVAJET:
			printf ("WARNING (%u):\t\"%.20s\" is not supported by the NOVAJET plotter.\n", line, text);
			break;
		case PARSE:
			printf ("WARNING (%u):\t\"%.20s\" is not fully supported by this parser.\n", line, text);
			break;
		default:
			printf ("INTERNAL ERROR (%u):\"%s\".\n", line, text);
			break;
		}
	}

	if (DEBUG && (string != NULL)) {
		printf ("%s\n", string);
	}
}
