shithub: hammer

ref: e01f61f0d55b37630264e8d74107199752a6e76f
dir: /n.y/

View raw version
%{

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>

#include "dat.h"
#include "fns.h"

int goteof;
int lexline;
char *modname;
char *lexfile = "<stdin>";
int yyparse(void);
void yyerror(char*);

Nlst toplevel;

void
loaduse(char*)
{
	fprint(2, "not implement: loaduse\n");
}

%}

%union
{
	char	*sval;
	long	ival;
	Typ	*typ;
	Nod	*nod;
	Nlst	lst;
}

%token FUNC DEF IF FOR MOD USE OR AND NOTEQ EQ SHIFTL SHIFTR STRUCT ELSE ARROWR ARROWL GTEQ LTEQ INC DEC
%token TYPE NAME NUM

%token	<sval>	NAME;
%token	<ival>	NUM

%type<nod>	defarg expr logexpr shifexpr addexpr mulexpr
%type<nod>	unary prefexpr sufexpr compexpr top stmt
%type<nod>	decl
%type<lst>	defargs prog stmts args
%type<typ>	type return

%%

file
:	prog			{ toplevel = $1; }

prog
:	prog top		{ $$ = append($1, $2); }
|	top			{ $$ = append(ZL, $1); }

top
:	FUNC NAME '(' defargs ')' return '{' stmts '}'
				{ $$ = mkfunc($2, $4, $6, $8); }
|	MOD NAME ';'		{ $$ = nil; modname = $2; }
|	USE NAME ';'		{ $$ = nil; loaduse($2); }
|	def ';'			{ $$ = nil; }

sdecls
:
|	sdecls NAME ':' type ';'

def
:	DEF NAME type		{ }
|	DEF NAME STRUCT '{' sdecls '}'
				{ }

type
:	NAME			{ $$ = mktype($1, Tunkn); }
|	type '[' ']'		{ $$ = mktyslice($1); }
|	type '[' NUM ']'	{ $$ = mktyarray($1, mkintlit($3)); }
|	type '!'		{ $$ = $1; $$->linear = 1; }

return
:	ARROWR type		{ $$ = $2; }
|				{ $$ = mktype("void", Tvoid); }

unary
:	NUM			{ $$ = mkexpr(Olit, mkintlit($1), nil); }
|	NAME			{ $$ = mkexpr(Ovar, mksym($1), nil); }
|	'"' NAME '"'		{ $$ = mkstrlit($2); }
|	'(' expr ')'		{ $$ = $2; }

sufexpr
:	sufexpr DEC		{ $$ = mkexpr(Odec, $1, nil); }
|	sufexpr INC		{ $$ = mkexpr(Oinc, $1, nil); }
|	NAME '(' args ')'	{ $$ = mkcall($1, $3); }
|	unary			{ $$ = $1; }

prefexpr
:	ARROWL sufexpr		{ $$ = mkexpr(Orcv, $2, nil); }
|	sufexpr			{ $$ = $1; }

mulexpr
:	mulexpr '*' prefexpr	{ $$ = mkexpr(Omul, $1, $3); }
|	mulexpr '/' prefexpr	{ $$ = mkexpr(Odiv, $1, $3); }
|	mulexpr '%' prefexpr	{ $$ = mkexpr(Omod, $1, $3); }
|	prefexpr			{ $$ = $1; }

addexpr
:	addexpr '+' mulexpr	{ $$ = mkexpr(Oadd, $1, $3); }
|	addexpr '-' mulexpr	{ $$ = mkexpr(Osub, $1, $3); }
|	mulexpr			{ $$ = $1; }

shifexpr
:	shifexpr SHIFTL addexpr	{ $$ = mkexpr(Oshl, $1, $3); }
|	shifexpr SHIFTR addexpr	{ $$ = mkexpr(Oshr, $1, $3); }
|	addexpr			{ $$ = $1; }

compexpr
:	compexpr '>' shifexpr	{ $$ = mkexpr(Ogt, $1, $3); }
|	compexpr '<' shifexpr	{ $$ = mkexpr(Olt, $1, $3); }
|	compexpr NOTEQ shifexpr	{ $$ = mkexpr(One, $1, $3); }
|	compexpr EQ shifexpr 	{ $$ = mkexpr(Oeq, $1, $3); }
|	compexpr LTEQ shifexpr	{ $$ = mkexpr(Oge, $1, $3); }
|	compexpr GTEQ shifexpr	{ $$ = mkexpr(Ole, $1, $3); }
|	shifexpr		{ $$ = $1; }


logexpr
:	logexpr OR compexpr	{ $$ = mkexpr(Olor, $1, $3); }
|	logexpr AND compexpr	{ $$ = mkexpr(Oland, $1, $3); }
|	compexpr		{ $$ = $1; }

expr
:				{ $$ = nil; }
|	expr '=' logexpr	{ $$ = mkexpr(Oasn, $1, $3); }
|	logexpr			{ $$ = $1; }

stmts
:				{ $$ = ZL; }
|	stmts stmt		{ $$ = append($1, $2); }

decl
:	NAME ':' type		{ $$ = mkdecl($1, $3, nil); }
|	NAME ':' '=' expr	{ $$ = mkdecl($1, nil, $4); }
|	NAME ':' type '=' expr	{ $$ = mkdecl($1, $3, $5); }

stmt
:	expr ';'		{ $$ = $1; }
|	'{' stmts '}'		{ $$ = mkblk($2); }
|	decl ';'		{ $$ = $1; }
|	FOR '(' unary ARROWL expr ')' stmt
				{ $$ = mkiter($3, $5, $7); }
|	FOR '(' expr ';' expr ';' expr ')' stmt
				{ $$ = mkfor($3, $5, $7, $9); }
|	IF '(' expr ')' stmt ELSE stmt
				{ $$ = mkif($3, $5, $7); }
|	IF '(' expr ')' stmt	{ $$ = mkif($3, $5, nil); }

defarg
:	NAME ':' type	{ $$ = mkdecl($1, $3, nil); }

defargs
:				{ $$ = ZL; }
|	defarg			{ $$ = append(ZL, $1); }
|	defargs ',' defarg	{ $$ = append($1, $3); }

/* TODO: should be expr */
args
:				{ $$ = ZL; }
|	logexpr			{ $$ = append(ZL, $1); }
|	args ',' logexpr	{ $$ = append($1, $3); }

%%

Biobuf *bin;

int
getch(void)
{
	int c;

	c = Bgetrune(bin);
	if(c == Beof){
		goteof = 1;
		return -1;
	}
	if(c == '\n')
		lexline++;
	return c;
}

void
ungetc(void)
{
	Bungetrune(bin);
}

void
wordlex(char *dst, int n)
{
	Rune c;
	char *e;

	for(e = dst + n - UTFmax; dst < e;){
		c = getch();
		if((c >= Runeself)
		|| isalnum(c)){
			dst += runetochar(dst, &c);
			continue;
		}
		ungetc();
		break;
	}
	if(dst > e - UTFmax*2)
		yyerror("symbol buffer overrun");
	*dst = '\0';
}

void
yyerror(char *s)
{
	fprint(2, "%d: %s\n", lexline, s);
	exits(s);
}

struct {
	int c;
	struct {
		int c;
		int t;
	} next[3];
} spectab[] = {
	'-', { '>', ARROWR, '-', DEC },
	'=', { '=', EQ },
	'>', { '>', SHIFTR, '=', GTEQ },
	'<', { '-', ARROWL, '<', SHIFTL, '=', LTEQ },
	'!', { '=', NOTEQ },
	'&', { '&', AND },
	'|', { '|', OR },
	'+', { '+', INC },
};

struct {
	char *s;
	int type;
} keytab[] = {
	"fn", FUNC,
	"type", DEF,
	"if", IF,
	"for", FOR,
	"mod", MOD,
	"use", USE,
	"struct", STRUCT,
	"else", ELSE,
	"→", ARROWR,
	"←", ARROWL,
};

int
yylex2(void)
{
	static char buf[200];
	int c, c2;
	int i, j;

Loop:
	c = getch();
	switch(c){
	case -1:
		return -1;
	case ' ':
	case '\t':
	case '\n':
		goto Loop;
	case '/':
		if(getch() != '*'){
			ungetc();
			goto Loop;
		}
More:
		while((c = getch()) > 0)
			if(c == '*')
				break;
		if(c != '*')
			goto Loop;
		if(getch() == '/')
			goto Loop;
		goto More;
	}
	if(utfrune("\"';{}[]()*/%:,.", c) != nil)
		return c;
	for(i = 0; i < nelem(spectab); i++){
		if(c != spectab[i].c)
			continue;
		c2 = getch();
		for(j = 0; j < 3; j++)
			if(c2 == spectab[i].next[j].c)
				return spectab[i].next[j].t;
		ungetc();
		return c;
	}
	ungetc();
	wordlex(buf, sizeof buf);

	for(i = 0; i < nelem(keytab); i++)
		if(strcmp(keytab[i].s, buf) == 0)
			return keytab[i].type;

	if(isdigit(buf[0])){
		yylval.ival = atoi(buf);
		return NUM;
	}

	yylval.sval = strdup(buf);
	return NAME;
}

int debug;

int
yylex(void)
{
	int c;

	c = yylex2();
	if(!debug)
		return c;
	if(c < Runeself)
		fprint(2, "%c\n", c);
	else if(c == NAME)
		fprint(2, "NAME %s\n", yylval.sval);
	else if(c == TYPE)
		fprint(2, "TYPE %s\n", yylval.sval);
	else if(c == NUM)
		fprint(2, "NUM %ld\n", yylval.ival);
	return c;
}

void
usage(void)
{
	fprint(2, "usage: [-d] %s\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int ast;

	ast = 0;
	ARGBEGIN{
	case 'a':
		ast++;
		break;
	case 'd':
		debug++;
		break;
	default:
		usage();
		break;
	}ARGEND;
	if(argc > 0)
		usage();
	astfmtinstall();
	bin = Bfdopen(0, OREAD);
	goteof = 0;
	while(!goteof)
		yyparse();
	Bterm(bin);
	if(ast)
		print("%L\n", toplevel);
	else
		print("%M\n", toplevel);
	exits(nil);
}