%{

include <ctype.h>
include "syncalc.h"
include "libsynphot.h"

define	YYMAXDEPTH	64
define	YYOPLEN		1
define	yyparse		synparse

define	MAX_TOKEN	SZ_FNAME
define	BLANK		' '
define	VARCHAR		'$'

# Tokens generated by xyacc have been moved to syncalc.h

%L
include "syncompile.com"

%}


%token		Y_LPAR Y_RPAR Y_COMMA Y_WRONG 
%token		Y_DONE Y_STR Y_FILE Y_NUM Y_VAR Y_FUNC 

%left		Y_ADD Y_SUB
%left		Y_MUL Y_DIV
%right		Y_NEG

%%

program	:	expr Y_DONE {
			# Normal exit. Code a stop instruction
			call synencode (Y_DONE)
			return (OK)
		}
	|	error {
			# Parser error
			return (ERR)
		}
	;

expr	:	Y_STR {
			# Code a push string instruction
			call synencode (Y_STR)
			call synconst (Memi[$1])
		}
	|	Y_FILE {
			# Code a push file instruction
			call synencode (Y_FILE)
			call synconst (Memi[$1])
		}
	|	Y_NUM {
			# Code a push number instruction
			call synencode (Y_NUM)
			call synconst (Memi[$1])
		}
	|	Y_VAR {
			# Code a push variable instruction
			call synencode (Y_VAR)
			call synencode (Memi[$1])
		}
	|	Y_FUNC Y_LPAR alist Y_RPAR {
	    		# Code a function call instruction
			call synencode (Y_FUNC)
			call synencode (Memi[$1])
			call synencode (Memi[$3])
			strict = true
		}
	|	Y_SUB expr %prec Y_NEG {
			# Code a negation instruction
			call synencode (Y_NEG)
		}
	|	expr Y_MUL expr {
			# Code a multiplication instruction
			call synencode (Y_MUL)
		}
	|	expr Y_DIV expr {
			# Code a division instruction
			call synencode (Y_DIV)
		}
	|	expr Y_ADD expr {
			# Code an add instruction
			call synencode (Y_ADD)
		}
	|	expr Y_SUB expr {
			# Code a subtraction instruction
			call synencode (Y_SUB)
		}
	|	Y_LPAR expr Y_RPAR {
			# No action for parenthesized expressions
		}
	|	Y_WRONG {
			# Lexical error
	    		call eprintf ("Unrecognized token (%s)\n")
			call pargstr (Memc[Memi[$1]])
			call mfree (Memi[$1], TY_CHAR)
			return (ERR)
		}
	;

alist	:	expr {
			# Initialize count of number of arguments
			Memi[$$] = 1
		}
	|	alist Y_COMMA expr {
			# Increment count of number of arguments
			Memi[$$] = Memi[$1] + 1
		}
	;

%%

# SYNCOMPILE -- Compile an synphot expression into psedocode instructions

procedure syncompile (expr, pcode, maxcode)

char	expr[ARB]	# i: Expression to be parsed
int	pcode[ARB]	# o: Array of pseudocode instructions
int	maxcode		# i: Maximum length of pseudocode array
#--
include "syncompile.com"

bool	debug
int	len
pointer	sp, code, line, ch

data	debug	/ false /

int	strlen(), synparse()

extern	synlexer

begin
	# Allocate memory for temporary arrays

	len = strlen (expr)

	call smark (sp)
	call salloc (code, maxcode, TY_INT)
	call salloc (line, len, TY_CHAR)

	# Initialize parsing common block

	strict = true
	icode = code
	ncode = maxcode

	# Parse expression to produce pseudocode

	ch = line
	call strcpy (expr, Memc[line], len)

	if (synparse (ch, debug, synlexer) == ERR) {
	    call eprintf ("%s\n%*t^\n")
	    call pargstr (Memc[line])
	    call pargi (ch-line)

	    call error (SYN_ERR, "Syntax error in expression")
	}

	# Clean up and return pseudocode array

	call amovi (Memi[code], pcode, maxcode)
	call sfree (sp)

end

# SYNLEXER -- Lexical analysis of synphot expression

int procedure synlexer (ch, value)

pointer	ch		# u: Pointer to current char in expression
pointer	value		# i: Pointer to current token value
#--
include	"syncompile.com"

bool	digit
char	token[MAX_TOKEN]
int	functype[MAX_FUNC]
int	ic, nc, type, ftype
pointer	sp, tabname, colname
real	rval

string	loosestr  "(),"
string	strictstr "(),*+-"
string	loosefunc "band,cat,icat,spec,thru"
string	funcstr   FUNCNAMES

data	functype  / FN_BAND, FN_BB, FN_BOX, FN_CAT, FN_EBMV, 
		    FN_EBMVX, FN_EM, FN_GAUSS, FN_GRID, FN_HI, 
	            FN_ICAT, FN_LGAUSS, FN_PL, FN_POLY, FN_RN, 
		    FN_SPEC, FN_THRU, FN_TILT, FN_UNIT, FN_Z /

int	ctor(), stridx(), tbtacc(), word_match()

begin
	# Allocate memory for temporary strings

	call smark (sp)
	call salloc (tabname, SZ_FNAME, TY_CHAR)
	call salloc (colname, SZ_FNAME, TY_CHAR)

	# Skip over leading white space 

	while (Memc[ch] <= BLANK) {
	    if (Memc[ch] == EOS)
		break

	    ch = ch + 1
	}

	# Gather characters in token. Numbers are handled separately
	# because of their complicated syntax.

	digit = strict && (Memc[ch] == '.' || IS_DIGIT(Memc[ch]))

	if (digit) {
	    ic = 1
	    nc = ctor (Memc[ch], ic, rval)
	    nc = min (nc, MAX_TOKEN)

	    ic = nc
	    call strcpy (Memc[ch], token, nc)

	} else {
	    ic = 0

	    while (Memc[ch+ic] > BLANK) {
		if (strict) {
		    if (stridx (Memc[ch+ic], strictstr) > 0)
			break
		} else {
		    if (stridx (Memc[ch+ic], loosestr) > 0)
			break
		}

		if (ic == MAX_TOKEN)
		    break

		token[ic+1] = Memc[ch+ic]
		ic = ic + 1
	    }
	}

	# Token is a char in the stop set

	if (ic == 0) {
	    token[1] = Memc[ch]
	    ic = 1
	}
	token[ic+1] = EOS

	# One letter tokens are symbols (probably)

	type = 0
	Memi[value] = NULL

	if (ic == 1) {
	    switch (token[1]) {
	    case EOS:
		type = Y_DONE
	    case '(':
		type = Y_LPAR
	    case ')':
		type = Y_RPAR
	    case ',':
		type = Y_COMMA
	    case '+':
		type = Y_ADD
	    case '-':
		type = Y_SUB
	    case '*':
		type = Y_MUL
	    case '/':
		type = Y_DIV
	    }
	}

	# Multicharacter tokens are function names, filenames, 
	# strings, variables or numeric constants

	if (type == 0) {
	    if (digit) {
		type = Y_NUM

	    } else if (! strict) {
		type = Y_STR

	    } else {
		ftype = word_match (token, funcstr)
		call breakcomp (token, Memc[tabname], Memc[colname], SZ_FNAME)

		if (ftype > 0) {
		    Memi[value] = functype[ftype] 
		    strict = word_match (token, loosefunc) == 0
		    type = Y_FUNC

		} else if (tbtacc (Memc[tabname]) == YES) {
		    type = Y_FILE

		} else if (IS_ALPHA(token[1])){
		    type = Y_STR

		} else if (token[1] == VARCHAR) {
		    ic = 2
		    if (! IS_DIGIT(token[ic])) {
			Memi[value] = NULL
			type = Y_WRONG
		    } else {
			Memi[value] = TO_INTEG(token[ic])
			token[ic+1] = EOS
			type = Y_VAR
		    }

		} else {
		    type = Y_WRONG
		}
	    }
	
	    if (type != Y_FUNC && type != Y_VAR) {
		call malloc (Memi[value], ic, TY_CHAR)
		call strcpy (token, Memc[Memi[value]], ic)
	    }
	}

	ch = ch + ic
	call sfree (sp)

	return (type)
end

# SYNENCODE -- Add an instruction to the code array

procedure synencode (token)

int	token		# i: Instruction token
#--
include	"syncompile.com"

begin

	if (ncode == 0)
	    call error (SYN_ERR, "Expression too complex")
	else {
	    Memi[icode] = token
	    icode = icode + 1
	    ncode = ncode - 1
	}

end

# SYNCONST -- Add a constant to the code array

procedure synconst (const)

pointer	const		# i: Pointer to constant value
#--
include	"syncompile.com"

pointer	chr

begin
	chr = const - 1

	repeat {
	    chr = chr + 1

	    if (ncode == 0)
		call error (SYN_ERR, "Expression too complex")

	    Memi[icode] = Memc[chr]
	    icode = icode + 1
	    ncode = ncode - 1

	} until (Memc[chr] == EOS)

	call mfree (const, TY_CHAR)
end

