#ifndef lint
static char sccsid[] = "@(#)rpc←parse.c 1.1 90/10/29 (C) 1987 SMI";
#endif

/*
 * rpc←parse.c, Parser for the RPC protocol compiler 
 * Copyright (C) 1987 Sun Microsystems, Inc.
 */
#include <stdio.h>
#include "rpc←util.h"
#include "rpc←scan.h"
#include "rpc←parse.h"

/*
 * return the next definition you see
 */
definition *
get←definition()
{
	definition *defp;
	token tok;

	defp = ALLOC(definition);
	get←token(&tok);
	switch (tok.kind) {
	case TOK←STRUCT:
		def←struct(defp);
		break;
	case TOK←UNION:
		def←union(defp);
		break;
	case TOK←TYPEDEF:
		def←typedef(defp);
		break;
	case TOK←ENUM:
		def←enum(defp);
		break;
	case TOK←PROGRAM:
		def←program(defp);
		break;
	case TOK←CONST:
		def←const(defp);
		break;
	case TOK←EOF:
		return (NULL);
	default:
		error("definition keyword expected");
	}
	scan(TOK←SEMICOLON, &tok);
	isdefined(defp);
	return (defp);
}

static
isdefined(defp)
	definition *defp;
{
	STOREVAL(&CurrentContext->defined, defp);
}

static
def←struct(defp)
	definition *defp;
{
	token tok;
	declaration dec;
	decl←list *decls;
	decl←list **tailp;

	defp->def←kind = DEF←STRUCT;

	scan(TOK←IDENT, &tok);
	defp->def←name = tok.str;
	scan(TOK←LBRACE, &tok);
	tailp = &defp->def.st.decls;
	do {
		get←declaration(&dec, DEF←STRUCT);
		decls = ALLOC(decl←list);
		decls->decl = dec;
		*tailp = decls;
		tailp = &decls->next;
		scan(TOK←SEMICOLON, &tok);
		peek(&tok);
	} while (tok.kind != TOK←RBRACE);
	get←token(&tok);
	*tailp = NULL;
}

static
def←program(defp)
	definition *defp;
{
	token tok;
	version←list *vlist;
	version←list **vtailp;
	proc←list *plist;
	proc←list **ptailp;

	defp->def←kind = DEF←PROGRAM;
	scan(TOK←IDENT, &tok);
	defp->def←name = tok.str;
	scan(TOK←LBRACE, &tok);
	vtailp = &defp->def.pr.versions;
	scan(TOK←VERSION, &tok);
	do {
		scan(TOK←IDENT, &tok);
		vlist = ALLOC(version←list);
		vlist->vers←name = tok.str;
		scan(TOK←LBRACE, &tok);
		ptailp = &vlist->procs;
		do {
			plist = ALLOC(proc←list);
			get←type(&plist->res←prefix, &plist->res←type, DEF←PROGRAM);
			if (streq(plist->res←type, "opaque")) {
				error("illegal result type");
			}
			scan(TOK←IDENT, &tok);
			plist->proc←name = tok.str;
			scan(TOK←LPAREN, &tok);
			get←type(&plist->arg←prefix, &plist->arg←type, DEF←PROGRAM);
			if (streq(plist->arg←type, "opaque")) {
				error("illegal argument type");
			}
			scan(TOK←RPAREN, &tok);
			scan(TOK←EQUAL, &tok);
			scan←num(&tok);
			scan(TOK←SEMICOLON, &tok);
			plist->proc←num = tok.str;
			*ptailp = plist;
			ptailp = &plist->next;
			peek(&tok);
		} while (tok.kind != TOK←RBRACE);
		*ptailp = NULL;
		*vtailp = vlist;
		vtailp = &vlist->next;
		scan(TOK←RBRACE, &tok);
		scan(TOK←EQUAL, &tok);
		scan←num(&tok);
		vlist->vers←num = tok.str;
		scan(TOK←SEMICOLON, &tok);
		scan2(TOK←VERSION, TOK←RBRACE, &tok);
	} while (tok.kind == TOK←VERSION);
	scan(TOK←EQUAL, &tok);
	scan←num(&tok);
	defp->def.pr.prog←num = tok.str;
	*vtailp = NULL;
}

static
def←enum(defp)
	definition *defp;
{
	token tok;
	enumval←list *elist;
	enumval←list **tailp;

	defp->def←kind = DEF←ENUM;
	scan(TOK←IDENT, &tok);
	defp->def←name = tok.str;
	scan(TOK←LBRACE, &tok);
	tailp = &defp->def.en.vals;
	do {
		scan(TOK←IDENT, &tok);
		elist = ALLOC(enumval←list);
		elist->name = tok.str;
		elist->assignment = NULL;
		scan3(TOK←COMMA, TOK←RBRACE, TOK←EQUAL, &tok);
		if (tok.kind == TOK←EQUAL) {
			scan←num(&tok);
			elist->assignment = tok.str;
			scan2(TOK←COMMA, TOK←RBRACE, &tok);
		}
		*tailp = elist;
		tailp = &elist->next;
	} while (tok.kind != TOK←RBRACE);
	*tailp = NULL;
}

static
def←const(defp)
	definition *defp;
{
	token tok;

	defp->def←kind = DEF←CONST;
	scan(TOK←IDENT, &tok);
	defp->def←name = tok.str;
	scan(TOK←EQUAL, &tok);
	scan2(TOK←IDENT, TOK←STRCONST, &tok);
	defp->def.co = tok.str;
}

static
def←union(defp)
	definition *defp;
{
	token tok;
	declaration dec;
	case←list *cases;
	case←list **tailp;

	defp->def←kind = DEF←UNION;
	scan(TOK←IDENT, &tok);
	defp->def←name = tok.str;
	scan(TOK←SWITCH, &tok);
	scan(TOK←LPAREN, &tok);
	get←declaration(&dec, DEF←UNION);
	defp->def.un.enum←decl = dec;
	tailp = &defp->def.un.cases;
	scan(TOK←RPAREN, &tok);
	scan(TOK←LBRACE, &tok);
	scan(TOK←CASE, &tok);
	while (tok.kind == TOK←CASE) {
		scan2(TOK←IDENT, TOK←CHARCONST, &tok);
		cases = ALLOC(case←list);
		cases->case←name = tok.str;
		scan(TOK←COLON, &tok);
		get←declaration(&dec, DEF←UNION);
		cases->case←decl = dec;
		*tailp = cases;
		tailp = &cases->next;
		scan(TOK←SEMICOLON, &tok);
		scan3(TOK←CASE, TOK←DEFAULT, TOK←RBRACE, &tok);
	}
	*tailp = NULL;
	if (tok.kind == TOK←DEFAULT) {
		scan(TOK←COLON, &tok);
		get←declaration(&dec, DEF←UNION);
		defp->def.un.default←decl = ALLOC(declaration);
		*defp->def.un.default←decl = dec;
		scan(TOK←SEMICOLON, &tok);
		scan(TOK←RBRACE, &tok);
	} else {
		defp->def.un.default←decl = NULL;
	}
}

static
def←typedef(defp)
	definition *defp;
{
	declaration dec;

	defp->def←kind = DEF←TYPEDEF;
	get←declaration(&dec, DEF←TYPEDEF);
	defp->def←name = dec.name;
	defp->def.ty.old←prefix = dec.prefix;
	defp->def.ty.old←type = dec.type;
	defp->def.ty.rel = dec.rel;
	defp->def.ty.array←max = dec.array←max;
}

static
get←declaration(dec, dkind)
	declaration *dec;
	defkind dkind;
{
	token tok;

	get←type(&dec->prefix, &dec->type, dkind);
	dec->rel = REL←ALIAS;
	if (streq(dec->type, "void")) {
		return;
	}
	scan2(TOK←STAR, TOK←IDENT, &tok);
	if (tok.kind == TOK←STAR) {
		dec->rel = REL←POINTER;
		scan(TOK←IDENT, &tok);
	}
	dec->name = tok.str;
	if (peekscan(TOK←LBRACKET, &tok)) {
		if (dec->rel == REL←POINTER) {
			error("no array-of-pointer declarations -- use typedef");
		}
		dec->rel = REL←VECTOR;
		scan←num(&tok);
		dec->array←max = tok.str;
		scan(TOK←RBRACKET, &tok);
	} else if (peekscan(TOK←LANGLE, &tok)) {
		if (dec->rel == REL←POINTER) {
			error("no array-of-pointer declarations -- use typedef");
		}
		dec->rel = REL←ARRAY;
		if (peekscan(TOK←RANGLE, &tok)) {
			dec->array←max = "~0";	/* unspecified size, use max */
		} else {
			scan←num(&tok);
			dec->array←max = tok.str;
			scan(TOK←RANGLE, &tok);
		}
	}
	if (streq(dec->type, "opaque")) {
		if (dec->rel != REL←ARRAY && dec->rel != REL←VECTOR) {
			error("array declaration expected");
		}
	} else if (streq(dec->type, "string")) {
		if (dec->rel != REL←ARRAY) {
			error("variable-length array declaration expected");
		}
	}
}

static
get←type(prefixp, typep, dkind)
	char **prefixp;
	char **typep;
	defkind dkind;
{
	token tok;

	*prefixp = NULL;
	get←token(&tok);
	switch (tok.kind) {
	case TOK←IDENT:
		*typep = tok.str;
		break;
	case TOK←STRUCT:
	case TOK←ENUM:
	case TOK←UNION:
		*prefixp = tok.str;
		scan(TOK←IDENT, &tok);
		*typep = tok.str;
		break;
	case TOK←UNSIGNED:
		unsigned←dec(typep);
		break;
	case TOK←SHORT:
		*typep = "short";
		(void) peekscan(TOK←INT, &tok);
		break;
	case TOK←LONG:
		*typep = "long";
		(void) peekscan(TOK←INT, &tok);
		break;
	case TOK←VOID:
		if (dkind != DEF←UNION && dkind != DEF←PROGRAM) {
			error("voids allowed only inside union and program definitions");
		}
		*typep = tok.str;
		break;
	case TOK←STRING:
	case TOK←OPAQUE:
	case TOK←CHAR:
	case TOK←INT:
	case TOK←FLOAT:
	case TOK←DOUBLE:
	case TOK←BOOL:
		*typep = tok.str;
		break;
	default:
		error("expected type specifier");
	}
}

static
unsigned←dec(typep)
	char **typep;
{
	token tok;

	peek(&tok);
	switch (tok.kind) {
	case TOK←CHAR:
		get←token(&tok);
		*typep = "u←char";
		break;
	case TOK←SHORT:
		get←token(&tok);
		*typep = "u←short";
		(void) peekscan(TOK←INT, &tok);
		break;
	case TOK←LONG:
		get←token(&tok);
		*typep = "u←long";
		(void) peekscan(TOK←INT, &tok);
		break;
	case TOK←INT:
		get←token(&tok);
		*typep = "u←int";
		break;
	default:
		*typep = "u←int";
		break;
	}
}