%{

include <ctype.h>
include "addmasks.h"

define	YYMAXDEPTH	64
define	YYOPLEN		1
define	yyparse		addparse

define	MAX_CODE	SZ_COMMAND
define	MAX_TOKEN	SZ_FNAME

# Characters

define	DOT		'.'
define	BLANK		' '
define	CMTCHAR		'#'

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

%L

%}

%token		Y_WRONG  Y_LPAR  Y_RPAR  Y_IDENT  
%token		Y_IF     Y_DONE  Y_THEN  Y_ELSE 
%token		Y_PUSH   Y_CONST 

%left		Y_OR
%left		Y_AND
%right		Y_NOT
%nonassoc	Y_EQ Y_NE
%nonassoc	Y_LT Y_GT Y_LE Y_GE
%%

command	:	ifexpr	Y_DONE {
			# Normal exit. Code a stop instruction
			call addencode (Y_DONE)
			return (OK)
		}
	|	error {
			# Parser error
			return (ERR)
		}
	;

ifexpr	:	Y_IF expr Y_THEN expr Y_ELSE expr {
			# Code an if_then_else instruction
			call addencode (Y_ELSE)
		}
	|	Y_IF expr Y_THEN expr {
			# Code an if_then instruction
			call addencode (Y_THEN)
		}
	|	expr {
			# No action
		}
	;
expr	:	Y_IDENT {
			# Code a push instruction
			call addencode (Y_PUSH)
			call addencode (Memi[$1])
		}
	|	Y_CONST	{
			# Code a push constant instruction
			call addencode (Y_CONST)
			call addnumber (Memi[$1])
		}
	|	expr Y_AND expr {
			# Code a logical and instruction
			call addencode (Y_AND)
		}
	|	expr Y_OR expr {
			# Code a logical or instruction
			call addencode (Y_OR)
		}
	|	Y_NOT expr {
			# Code a logical negation instruction
			call addencode (Y_NOT)
		}
	|	expr Y_EQ expr {
			# Code a equals instruction
			call addencode (Y_EQ)
		}
	|	expr Y_NE expr {
			# Code a not equals instruction
			call addencode (Y_NE)
		}
	|	expr Y_LT expr {
			# Code a less than instruction
			call addencode (Y_LT)
		}
	|	expr Y_GT expr {
			# Code a greater than instruction
			call addencode (Y_GT)
		}
	|	expr Y_LE expr {
			# Code a less than or equals instruction
			call addencode (Y_LE)
		}
	|	expr Y_GE expr {
			# Code a greater than or equals instruction
			call addencode (Y_GE)
		}
	|	Y_LPAR expr Y_RPAR {
			# No action
		}
	|	Y_WRONG {
			# Lexical error
	    		call eprintf ("Unrecognized token (%s)\n")
			call pargstr (Memc[Memi[$1]])
			call mfree (Memi[$1], TY_CHAR)
			return (ERR)
		}
	;

%%

# ADDCOMPILE -- Compile an addmasks expression into RPN instructions

pointer procedure addcompile (expr)

char	expr[ARB]	# i: Expression to be parsed
#--
include "addcompile.com"

int	ic, fd, len, debug
pointer	sp, pcode

data	debug	/ NO /

int	open(), stropen(), strlen(), addparse()

extern	addlexer

begin
	# Create pseudocode array

	call malloc (pcode, MAX_CODE, TY_INT)

	# Initialize parsing common block

	call smark (sp)
	call salloc (line, SZ_LINE, TY_CHAR)

	ch = line
	Memc[line] = EOS

	ncode = 0
	code = pcode

	# Open the expression as a file

	for (ic = 1; IS_WHITE(expr[ic]); ic = ic + 1)
	    ;

	if (expr[ic] == '@')
	    fd = open (expr[ic+1], READ_ONLY, TEXT_FILE)
	else {
	    len = strlen (expr[ic]) + 1
	    fd = stropen (expr[ic], len, READ_ONLY)
	}

	# Parse expression to produce reverse polish code

	if (addparse (fd, debug, addlexer) == ERR) {
	    if (Memc[line] != EOS) {
		call eprintf ("%s\n%*t^\n")
		call pargstr (Memc[line])
		call pargi (ch-line)
	    }
	    call error (1, "Syntax error in expression")
	}

	# Clean up and return pseudocode array

	call close (fd)
	call sfree (sp)
	return (pcode)
end

# ADDLEXER -- Lexical analysis of addmasks expression

int procedure addlexer (fd, value)

int	fd		# i: File containing expression to be lexed
pointer	value		# i: Pointer to current token value
#--
include	"addcompile.com"

char	token[MAX_TOKEN]
int	ic, jc, nc, type, ktype
int	keytype[12]

string	keytok  "or and eq ne lt gt le ge not if then else"
data	keytype / Y_OR, Y_AND, Y_EQ, Y_NE, Y_LT, Y_GT, Y_LE, 
		  Y_GE, Y_NOT, Y_IF, Y_THEN, Y_ELSE /

int	getline(), ctoi(),  word_match()

begin

	# Skip over leading white space and comments

	while (Memc[ch] <= BLANK || Memc[ch] == CMTCHAR) {

	    # If all characters have been read from the current line 
	    # or a comment character was found, get the next line

	    if (Memc[ch] == EOS || Memc[ch] == CMTCHAR) {
		ch = line
		if (getline (fd, Memc[line]) == EOF) {
		    Memc[ch] = EOS
		    break
		}
	    } else {
		ch = ch + 1
	    }
	}

	# The token type is determined from the first character in the token

	ic = 1
	Memi[value] = NULL

	# End of expression token

	if (Memc[ch] == EOS) {
	    type = Y_DONE

	# Numeric constant 

	} else if (IS_DIGIT(Memc[ch])) {
	    for (ic = 1; ic <= MAX_TOKEN; ic = ic + 1) {
		if (! IS_DIGIT(Memc[ch]))
		    break

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

	    call malloc (Memi[value], ic, TY_CHAR)
	    call strcpy (token, Memc[Memi[value]], ic)
	    type = Y_CONST

	# Token is alphanumeric. Keyword or variable.

	} else if (IS_ALPHA (Memc[ch])) {

	    # Gather characters in token

	    for (ic = 1; ic <= MAX_TOKEN; ic = ic + 1) {
		if (Memc[ch] != '_' && ! IS_ALNUM(Memc[ch]))
		    break

		if (IS_UPPER(Memc[ch]))	
		    token[ic] = TO_LOWER(Memc[ch])
		else
		    token[ic] = Memc[ch]
		ch = ch + 1
	    }
	    token[ic] = EOS

	    ktype = word_match (token, keytok)
	    if (ktype > 0) {
		type = keytype[ktype]

	    } else if (token[1] == 'i' && token[2] == 'm') {
		jc = 3
		type = Y_IDENT
		nc = ctoi (token, jc, Memi[value])
		if (token[jc] != EOS) {
		    type = Y_WRONG
		    call malloc (Memi[value], ic, TY_CHAR)
		    call strcpy (token, Memc[Memi[value]], ic)
		}

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

	# Tokens beginning with a dot are keywords

	} else if (Memc[ch] == DOT) {

	    # Gather characters in token

	    ch = ch + 1
	    for (ic = 1; ic < MAX_TOKEN && Memc[ch] != DOT; ic = ic + 1) {
		if (Memc[ch] == EOS)
		    break
		if (IS_UPPER(Memc[ch]))	
		    token[ic] = TO_LOWER(Memc[ch])
		else
		    token[ic] = Memc[ch]
		ch = ch + 1
	    }
	    token[ic] = EOS
	    ch = ch + 1

	    ktype = word_match (token, keytok)
	    if (ktype > 0) {
		type = keytype[ktype]

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

	# Anything else is a symbol
	
	} else {

	    switch (Memc[ch]) {
	    case '(':
		type = Y_LPAR
	    case ')':
		type = Y_RPAR
	    case '<':
		if (Memc[ch+1] != '=') {
		   type = Y_LT
		} else {
		   type = Y_LE
		   ch = ch + 1
		}
	    case '>':
		if (Memc[ch+1] != '=') {
		   type = Y_GT
		} else {
		   type = Y_GE
		   ch = ch + 1
		}
	    case '|':
		if (Memc[ch+1] != '|') {
		   type = Y_WRONG
		} else {
		   type = Y_OR
		   ch = ch + 1
		}
	    case '&':
		if (Memc[ch+1] != '&') {
		   type = Y_WRONG
		} else {
		   type = Y_AND
		   ch = ch + 1
		}
	    case '=':
		if (Memc[ch+1] != '=') {
		   type = Y_WRONG
		} else {
		   type = Y_EQ
		   ch = ch + 1
		}
	    case '!':
		if (Memc[ch+1] != '=') {
		   type = Y_NOT
		} else {
		   type = Y_NE
		   ch = ch + 1
		}
	    default:
		type = Y_WRONG
	    }

            if (type == Y_WRONG) {
		call malloc (Memi[value], 2, TY_CHAR)
                call strcpy (Memc[ch], Memc[Memi[value]], 2)
            }
	    ch = ch + 1
	}

	return (type)
end

# ADDENCODE -- Add an instruction to the code array

procedure addencode (token)

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

begin

	if (ncode == MAX_CODE)
	    call error (1, "Expression too complex")
	else {
	    Memi[code] = token
	    code = code + 1
	    ncode = ncode + 1
	}

end

# ADDNUMBER -- Add a numeric constant to the code array

procedure addnumber (const)

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

pointer	chr

begin
	chr = const - 1

	repeat {
	    chr = chr + 1

	    if (ncode == MAX_CODE)
		call error (1, "Expression too complex")

	    Memi[code] = Memc[chr]
	    code = code + 1
	    ncode = ncode + 1

	} until (Memc[chr] == EOS)

	call mfree (const, TY_CHAR)
end

