shithub: lpa

Download patch

ref: b69af6813720ca5ca47f267d7e8d832ae0b77172
parent: fe152caa60e1086c3f5b973e83135d04bde81fe1
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Sat Jul 27 05:56:12 EDT 2024

Work on parsing and evaluation

--- a/array.c
+++ b/array.c
@@ -226,6 +226,11 @@
 	char buf[2048]; /* TODO: fixed size :) */
 	char *p = buf;
 
+	if(f->ast == nil){
+		sprint(p, "%s", primsymb(f->prim));
+		return buf;
+	}
+	
 	p += sprint(p, "∇");
 	if(f->ast->funcresult)
 		p += sprint(p, "%s←", f->ast->funcresult->name);
--- a/dat.h
+++ b/dat.h
@@ -161,6 +161,7 @@
 	AstName,
 	AstLocals,
 	AstAssign,
+	AstNiladic,
 	AstMonadic,
 	AstDyadic,
 	AstConst,
@@ -217,14 +218,15 @@
 	IPushPrim,
 	ILookup,
 	IStrand,
+	INiladic,
 	IMonadic,
 	IDyadic,
-	IClear,
 	IParse,
-	IDone,
 	IReturn,
 	IAssign,
 	ILocal,
+	IPop,
+	IDisplay,
 };
 
 typedef struct ValueStack ValueStack;
@@ -268,8 +270,10 @@
 
 enum Valence
 {
-	Monadic = 1<<1,
-	Dyadic = 1<<2,
+	Niladic = 1,
+	Monadic = 2,
+	Dyadic = 4,
+	Variadic = 6,
 };
 
 typedef struct Function Function;
@@ -289,6 +293,8 @@
 	EAny, /* 0 = catch any error */
 	ESyntax,
 	EValue,
+	EInternal,
+	EDomain,
 
 	ErrorMax,
 };
--- a/error.c
+++ b/error.c
@@ -64,6 +64,8 @@
 	switch(c->num){
 	case ESyntax:	return "SYNTAX ERROR";
 	case EValue:	return "VALUE ERROR";
+	case EInternal:	return "INTERNAL ERROR";
+	case EDomain:	return "DOMAIN ERROR";
 	default:	return "ERROR ???";
 	}
 }
--- a/eval.c
+++ b/eval.c
@@ -50,6 +50,7 @@
 	if(assign){
 		emitbyte(c, IAssign);
 		emituvlong(c, id);
+		emitbyte(c, IPop);
 	}
 }
 
@@ -56,17 +57,15 @@
 static void
 codegensub(Session *s, Module *m, ByteCode *c, Ast *a)
 {
-	char *err;
 	uvlong i;
 
 	switch(a->tag){
 	case AstProg:
 		for(i = 0; i < a->childcount; i++){
-			if(i != 0)
-				emitbyte(c, IClear);
 			codegensub(s, m, c, a->children[i]);
+			emitbyte(c, IPop);
+			emitbyte(c, IDisplay);
 		}
-		emitbyte(c, IDone);
 		break;
 	case AstFunc:
 		/* Emit bytecode for the function body */
@@ -77,6 +76,10 @@
 				fn->valence = Dyadic;
 			else if(fn->ast->funcrightarg)
 				fn->valence = Monadic;
+			else
+				fn->valence = Niladic;
+			if(fn->ast->funcresult)
+				fn->hasresult = 1;
 
 			fn->symbol = sym(m->symtab, a->funcname->name);
 			fn->code = alloc(DataByteCode);
@@ -90,7 +93,7 @@
 				emitlocal(fn->code, m->symtab, fn->ast->funclocals->children[i], 0);
 			for(i = 0; i < a->childcount; i++){
 				codegensub(s, m, fn->code, a->children[i]);
-				emitbyte(fn->code, IClear);
+				emitbyte(fn->code, IPop);
 			}
 			if(fn->ast->funcresult)
 				codegensub(s, m, fn->code, fn->ast->funcresult);
@@ -99,10 +102,6 @@
 			emitbyte(c, IPushConst);
 			emitptr(c, fn);
 
-			/* push the value twice so defining a function yields a function value.. */
-			emitbyte(c, IPushConst);
-			emitptr(c, fn);
-
 			emitbyte(c, IAssign);
 			emituvlong(c, fn->symbol);
 		}
@@ -127,6 +126,10 @@
 		emitbyte(c, IStrand);
 		emituvlong(c, a->childcount);
 		break;
+	case AstNiladic:
+		codegensub(s, m, c, a->func);
+		emitbyte(c, INiladic);
+		break;
 	case AstMonadic:
 		codegensub(s, m, c, a->right);
 		codegensub(s, m, c, a->func);
@@ -147,9 +150,7 @@
 		emitptr(c, a->tokens);
 		break;
 	default:
-		err = smprint("Don't know how to do codegen for ast type %d\n", a->tag);
-		appendlog(s, err);
-		free(err);
+		error(EInternal, "Don't know how to do codegen for ast type %d", a->tag);
 		break;
 	}
 
@@ -175,11 +176,19 @@
 popval(ValueStack *s)
 {
 	if(s->count == 0)
-		sysfatal("popval on empty value stack");
+		error(EInternal, "popval on empty value stack");
 	s->count--; /* no realloc */
 	return s->values[s->count];
 }
 
+static void *
+peekval(ValueStack *s)
+{
+	if(s->count == 0)
+		error(EInternal, "peekval on empty value stack");
+	return s->values[s->count-1];
+}
+
 static void
 pushcall(CallStack *s, ByteCode *newcode, ByteCode **c, uvlong *o)
 {
@@ -197,7 +206,7 @@
 popcall(CallStack *s, Symtab *t, ByteCode **c, uvlong *o)
 {
 	if(s->count == 0)
-		sysfatal("popcall on empty call stack");
+		error(EInternal, "popcall on empty call stack");
 	s->count--; /* no realloc */
 	*c = s->frames[s->count].code;
 	*o = s->frames[s->count].offset;
@@ -220,6 +229,26 @@
 	symset(s, id, nil);
 }
 
+static int
+nextinstr(CallStack *calls, ByteCode *c, uvlong o)
+{
+	if(o < c->count && c->instrs[o] != IReturn)
+		return c->instrs[o];
+	if(calls->count == 0)
+		return -1;
+	else{
+		CallFrame f = calls->frames[calls->count-1];
+		return f.code->instrs[f.offset];
+	}
+}
+
+static void
+checkarray(void *val)
+{
+	if(val == nil || getalloctag(val) != DataArray)
+		error(EDomain, "non-array value where an array was expected");
+}
+
 static void *
 evalbc(Session *s, Module *m, ByteCode *c)
 {
@@ -230,12 +259,11 @@
 	uvlong o, v;
 	Function *func;
 	void *r;
+	Array *x, *y, *z;
 
 	values = alloc(DataValueStack);
 	calls = alloc(DataCallStack);
 
-	debugbc(c);
-
 	o = 0;
 	while(o < c->count){
 		int instr = c->instrs[o];
@@ -252,6 +280,7 @@
 				Function *f = alloc(DataFunction);
 				f->prim = v;
 				f->valence = primvalence(v);
+				f->hasresult = 1;
 				pushval(values, f);
 			}
 			break;
@@ -259,10 +288,8 @@
 			o += getuvlong(c->instrs+o, &v);
 			{
 				void *val = symval(m->symtab, v);
-				if(val == nil){
-					appendlog(s, "VALUE ERROR\n");
-					return nil;
-				}
+				if(val == nil)
+					error(EValue, "%s is undefined", symname(m->symtab, v));
 				pushval(values, val);
 			}
 			break;
@@ -271,47 +298,85 @@
 			{
 				Array *x = allocarray(TypeArray, 1, v);
 				setshape(x, 0, v);
-				for(uvlong i = 0; i < v; i++)
-					setarray(x, i, popval(values));
+				for(uvlong i = 0; i < v; i++){
+					z = popval(values);
+					checkarray(z);
+					setarray(x, i, z);
+				}
 				x = simplifyarray(x);
 				pushval(values, x);
 			}
 			break;
-		case IMonadic:
+		case INiladic:
 			func = popval(values);
-			if(!(func->valence & Monadic)){
-				appendlog(s, "ERROR: Function not monadic!\n");
-				return nil;
+			if(func->valence != Niladic){
+				int next = nextinstr(calls, c, o);
+				if(next == IAssign || IPop){
+					pushval(values, func);
+					break;
+				}else
+					error(ESyntax, "Function %s is not niladic", funcname(func));
 			}
 
-			if(func->code)
+			if(func->code){
+				if(!func->hasresult){
+					if(nextinstr(calls, c, o) == IPop)
+						pushval(values, nil); /* fake result */
+					else
+						error(ESyntax, "Function %s does not produce a result", funcname(func));
+				}
 				pushcall(calls, func->code, &c, &o);
-			else{
-				Array *y = popval(values);
-				Array *z = primmonad(func->prim, y);
+			}else{
+				z = primnilad(func->prim);
 				pushval(values, z);
 			}
 			break;
-		case IDyadic:
+		case IMonadic:
+			/* FIXME: more duplicated code with INiladic and IDyadic than i would like */
 			func = popval(values);
-			if(!(func->valence & Dyadic)){
-				appendlog(s, "ERROR: Function not dyadic!\n");
-				return nil;
+			y = popval(values);
+			if(!(func->valence & Monadic))
+				error(ESyntax, "Function %s is not monadic", funcname(func));
+			checkarray(y);
+
+			if(func->code){
+				if(!func->hasresult){
+					if(nextinstr(calls, c, o) == IPop)
+						pushval(values, nil); /* fake result */
+					else
+						error(ESyntax, "Function %s does not produce a result", funcname(func));
+				}
+				pushval(values, y);
+				pushcall(calls, func->code, &c, &o);
+			}else{
+				z = primmonad(func->prim, y);
+				pushval(values, z);
 			}
+			break;
+		case IDyadic:
+			func = popval(values);
+			x = popval(values);
+			y = popval(values);
+			if(!(func->valence & Dyadic))
+				error(ESyntax, "Function %s is not dyadic", funcname(func));
+			checkarray(x);
+			checkarray(y);
 
-			if(func->code)
+			if(func->code){
+				if(!func->hasresult){
+					if(nextinstr(calls, c, o) == IPop)
+						pushval(values, nil); /* fake result */
+					else
+						error(ESyntax, "Function %s does not produce a result", funcname(func));
+				}
+				pushval(values, y);
+				pushval(values, x);
 				pushcall(calls, func->code, &c, &o);
-			else{
-				Array *x = popval(values);
-				Array *y = popval(values);
-				Array *z = primdyad(func->prim, x, y);
+			}else{
+				z = primdyad(func->prim, x, y);
 				pushval(values, z);
 			}
 			break;
-		case IClear: /* TODO: get rid of this instruction. It shouldn't be there, and it is wrong */
-			while(values->count > 0)
-				popval(values);
-			break;
 		case IParse:
 			/* parse at runtime and emit code */
 			o += getuvlong(c->instrs+o, &v);
@@ -324,31 +389,44 @@
 				pushcall(calls, newcode, &c, &o);
 			}
 			break;
-		case IDone:
-			goto done;
-			break;
 		case IReturn:
 			popcall(calls, m->symtab, &c, &o);
 			break;
 		case IAssign:
 			o += getuvlong(c->instrs+o, &v);
-			symset(m->symtab, v, popval(values));
+			{
+				void *val = popval(values);
+				symset(m->symtab, v, val);
+
+				if(nextinstr(calls, c, o) == IPop)
+					val = nil;
+				pushval(values, val);
+			}
 			break;
 		case ILocal:
 			o += getuvlong(c->instrs+o, &v);
 			pushlocal(calls, m->symtab, v);
 			break;
+		case IPop:
+			r = popval(values);
+			if(nextinstr(calls, c, o) == IDisplay && r != nil)
+				appendlog(s, printval(r));
+			break;
+		case IDisplay:
+			/* nothing to do, IPop checks for it */
+			break;
 		default:
-			appendlog(s, "unknown instruction in evalbc\n");
-			return nil;
+			error(EInternal, "unknown instruction in evalbc: %d", instr);
 		}
 	}
 
-done:
 	r = nil;
-	print("Final value stack size: %ulld\n", values->count);
-	print("Final call stack size: %ulld\n", calls->count);
-	if(values->count != 0)
+	if(values->count > 1)
+		error(EInternal, "Value stack size is %ulld", values->count);
+	if(calls->count > 0)
+		error(EInternal, "Call stack size is %ulld", calls->count);
+
+	if(values->count == 1)
 		r = popval(values);
 	return r;
 }
--- a/fns.h
+++ b/fns.h
@@ -43,11 +43,13 @@
 int primclass(int);
 int primvalence(int);
 int primid(char *);
+Array *primnilad(int);
 Array *primmonad(int, Array *);
 Array *primdyad(int, Array *, Array *);
 
 /* scan.c */
 TokenList *scan(char *);
+char *printtok(Token);
 
 /* session.c */
 void initsessions(void);
@@ -74,6 +76,7 @@
 void debugast(Ast *, int);
 void debugbc(ByteCode *);
 int getuvlong(u8int *, uvlong *);
+char *funcname(Function *);
 
 /* value.c */
 char *printval(void *);
--- a/parse.c
+++ b/parse.c
@@ -60,7 +60,7 @@
 match(TokenList *tokens, int tag)
 {
 	if(peek(tokens) != tag)
-		error(ESyntax, "Unexpected token (match failed)");
+		error(ESyntax, "Unexpected token: %s", printtok(tokens->tokens[tokens->offset]));
 	tokens->offset++;
 }
 
@@ -258,7 +258,6 @@
 		class = nameclass(name, symtab, func);
 		t->tokens[i].nameclass = class;
 		if(class == 0){ /* We don't know how to parse it until runtime */
-			print("nameclass 0 name: %s funcname: %s\n", name, func ? func->funcname->name : "<no func>");
 			if(symtab)
 				error(EValue, "%s is undefined", name);
 
@@ -300,13 +299,17 @@
 	if(peekclass(t) == NameclassFunc){
 func:
 		expr = alloc(DataAst);
-		if(val){
-			expr->tag = AstDyadic;
-			expr->left = val;
-		}else
-			expr->tag = AstMonadic;
 		expr->func = parsefunc(t);
-		expr->right = parseexprsub(t);
+		if(val == nil && (isexprsep(t) || peek(t) == TokRparen))
+			expr->tag = AstNiladic;
+		else{
+			if(val){
+				expr->tag = AstDyadic;
+				expr->left = val;
+			}else
+				expr->tag = AstMonadic;
+			expr->right = parseexprsub(t);
+		}
 		val = expr;
 		goto end;
 	}
--- a/prim.c
+++ b/prim.c
@@ -17,13 +17,14 @@
 struct {
 	char *spelling;
 	int nameclass;
+	Array *(*nilad)(void);
 	Array *(*monad)(Array *);
 	Array *(*dyad)(Array *, Array *);
 } primspecs[] = {
-	"⊢", NameclassFunc, primfn_same, primfn_right,
-	"⊣", NameclassFunc, primfn_same, primfn_left,
-	"+", NameclassFunc, nil, nil,
-	"-", NameclassFunc, nil, nil,
+	"⊢", NameclassFunc, nil, primfn_same, primfn_right,
+	"⊣", NameclassFunc, nil, primfn_same, primfn_left,
+	"+", NameclassFunc, nil, nil, nil,
+	"-", NameclassFunc, nil, nil, nil,
 };
 
 char *
@@ -61,14 +62,21 @@
 }
 
 Array *
+primnilad(int id)
+{
+	if(primspecs[id].nilad)
+		return primspecs[id].nilad();
+	else
+		error(EInternal, "primitive %s has no niladic definition", primsymb(id));
+}
+
+Array *
 primmonad(int id, Array *y)
 {
 	if(primspecs[id].monad)
 		return primspecs[id].monad(y);
-	else{
-		print("primitive %s has no monadic definition! (acts like ⊢)\n", primsymb(id));
-		return y;
-	}
+	else
+		error(EInternal, "primitive %s has no monadic definition", primsymb(id));
 }
 
 Array *
@@ -76,10 +84,8 @@
 {
 	if(primspecs[id].dyad)
 		return primspecs[id].dyad(x, y);
-	else{
-		print("primitive %s has no dyadic definition! (acts like ⊣)\n", primsymb(id));
-		return x;
-	}
+	else
+		error(EInternal, "primitive %s has no dyadic definition", primsymb(id));
 }
 
 /* monadic functions */
--- a/scan.c
+++ b/scan.c
@@ -83,4 +83,60 @@
 	}
 	newtok(tokens, TokEnd);
 	return tokens;
+}
+
+char *
+printtok(Token t)
+{
+	char buf[1024];
+	char *p = buf;
+
+	switch(t.tag){
+	case TokNumber:
+		sprint(p, "number");
+		break;
+	case TokName: 
+		sprint(p, "name");
+		break;
+	case TokLparen:
+		sprint(p, "(");
+		break;
+	case TokRparen:
+		sprint(p, ")");
+		break;
+	case TokLbrack:
+		sprint(p, "[");
+		break;
+	case TokRbrack:
+		sprint(p, "]");
+		break;
+	case TokLbrace:
+		sprint(p, "{");
+		break;
+	case TokRbrace:
+		sprint(p, "}");
+		break;
+	case TokNewline:
+		sprint(p, "newline");
+		break;
+	case TokDiamond:
+		sprint(p, "⋄");
+		break;
+	case TokPrimitive:
+		sprint(p, "primitive");
+		break;
+	case TokDel:
+		sprint(p, "∇");
+		break;
+	case TokLarrow:
+		sprint(p, "←");
+		break;
+	case TokSemi:
+		sprint(p, ";");
+		break;
+	default:
+		sprint(p, "???");
+	}
+
+	return buf;
 }
\ No newline at end of file
--- a/session.c
+++ b/session.c
@@ -42,7 +42,7 @@
 
 		if(strlen(buf) > 0 && buf[0] == ')')
 			systemcmd(s, buf+1, 0);
-		else{	
+		else{
 			if(trap(EAny)){
 				appendlog(s, errdesc());
 				appendlog(s, ": ");
@@ -53,11 +53,7 @@
 
 			TokenList *tokens = scan(buf);
 			Ast *ast = parse(tokens, 0);
-			debugast(ast, 0);
-			void *val = eval(s, ast);
-			if(val)
-				appendlog(s, printval(val));
-
+			eval(s, ast);
 			endtrap();
 		}
 	}
--- a/util.c
+++ b/util.c
@@ -158,6 +158,9 @@
 			o += getuvlong(c->instrs+o, &v);
 			print("STRAND %ulld\n", v);
 			break;
+		case INiladic:
+			print("NILADIC CALL\n");
+			break;
 		case IMonadic:
 			print("MONADIC CALL\n");
 			break;
@@ -164,16 +167,10 @@
 		case IDyadic:
 			print("DYADIC CALL\n");
 			break;
-		case IClear:
-			print("CLEAR\n");
-			break;
 		case IParse:
 			o += getuvlong(c->instrs+o, &v);
 			print("PARSE %ulld\n", v);
 			break;
-		case IDone:
-			print("DONE\n");
-			break;
 		case IReturn:
 			print("RETURN\n");
 			break;
@@ -185,6 +182,12 @@
 			o += getuvlong(c->instrs+o, &v);
 			print("LOCAL %ulld\n", v);
 			break;
+		case IPop:
+			print("POP\n");
+			break;
+		case IDisplay:
+			print("DISPLAY\n");
+			break;
 		default:
 			print("???");
 			return;
@@ -191,4 +194,13 @@
 		}
 	}
 	print("\n");
+}
+
+char *
+funcname(Function *f)
+{
+	if(f->ast)
+		return f->ast->funcname->name;
+	else
+		return primsymb(f->prim);
 }
\ No newline at end of file