16fafd86fc04 — pouya@nohup.io 2 years ago
fix lexer and add cmd parser + examples
A => sky/WIP +0 -0

A => sky/cmd/cmd.c +157 -0
@@ 0,0 1,157 @@ 
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+
+#include "debug.h"
+#include "lex.h"
+
+#include "dat.h"
+#include "fns.h"
+
+struct Cmdnode *mknode(void);
+int eqsig(Cmd *c1, Cmd *c2);
+int (*(getfun)(struct Cmdnode*, Cmd*))(Tok*, int);
+
+Cmdctxt*
+mkcmdctxt(int fd)
+{
+	Cmdctxt *ctxt;
+	if((ctxt=malloc(sizeof(Cmdctxt)))==nil)
+		return nil;
+	ctxt->fd = fd;
+	if((ctxt->head=mknode())==nil){
+		free(ctxt);
+		return nil;
+	}
+
+	return ctxt;
+}
+
+
+int
+cmdreg(Cmdctxt *ctxt, Cmd *c)
+{
+	struct Cmdnode *head;
+	head = ctxt->head;
+
+	/* the tail node is always pre-allocated but unset */
+	while(head->next){
+		if(eqsig(c, head->c)){
+			head->c->fun = c->fun;
+			return 0;
+		}
+		head=head->next;
+	}
+
+	if((head->next=mknode())==nil)
+		return Loverflow;
+
+	*(head->c) = *c;
+
+	return 0;
+}
+
+void
+cmdterm(Cmdctxt *ctxt)
+{
+	struct Cmdnode *head, *nxt;
+	head = ctxt->head;
+
+	while(head){
+		nxt = head->next;
+		free(head->c);
+		free(head);
+		head = nxt;
+	}
+}
+
+void
+cmdproc(void *ctxt)
+{
+	struct Cmdnode *head;
+	int fd;
+	Lexer l;
+	Tok t, argv[Cmaxargs];
+	Cmd c;
+	int (*efun)(Tok*, int);
+	char *err;
+
+	head = ((Cmdctxt*)ctxt)->head;
+	fd = ((Cmdctxt*)ctxt)->fd;
+	memset(&c, 0, sizeof(Cmd));
+
+	linit(&l, fd, lexstart());
+
+	while(t = lnexttok(&l), t.typ != Teof){
+		DBPRINT(10, "tok: %d(%S)\n", t.typ, t.str);
+		switch(t.typ){
+		case Terror:
+			err = "parse error";
+error:		fprint(2, "%s in cmd %S (last token: %S)\n", err, *c.str ? c.str : L"nil", t.str);
+			memset(&c, 0, sizeof(Cmd));
+			break;
+		case Tcmd:
+			runestrncpy(c.str, t.str, Ltoksize+1);
+			break;
+		case Teos:
+			if((efun = getfun(head, &c)) == nil){
+				err = "unrecognised function signature";
+				goto error;
+			}
+			if((*efun)(argv, c.argc) < 0){
+				err = "invalid data";
+				goto error;
+			}
+			memset(&c, 0, sizeof(Cmd));
+			break;
+		case Tidentifier:
+		case Tdecimal:
+		case Tstr:
+			c.argtyp[c.argc] = t.typ;
+			argv[c.argc++] = t;
+			break;
+			break;
+		}
+	}
+}
+
+struct Cmdnode*
+mknode(void)
+{
+	struct Cmdnode *h;
+	if((h=malloc(sizeof(struct Cmdnode)))==nil)
+		return nil;
+	memset(h, 0, sizeof(struct Cmdnode));
+	if((h->c=malloc(sizeof(Cmd)))==nil){
+		free(h);
+		return nil;
+	}
+	memset(h->c, 0, sizeof(Cmd));
+
+	return h;
+}
+
+int
+eqsig(Cmd *c1, Cmd *c2)
+{
+	int i, *a1, *a2;
+
+	if(!c1 || !c2) return 0;
+	if(runestrncmp(c1->str, c2->str, Ltoksize+1) || c1->argc != c2->argc) return 0;
+	for(i=0, a1=c1->argtyp, a2=c2->argtyp; i<c1->argc; i++)
+		if(*a1++ != *a2++) return 0;
+
+	return 1;	
+}
+
+int (*(getfun)(struct Cmdnode *h, Cmd *c))(Tok*, int)
+{
+	/* last node is always unset */
+	while(h->next){
+		if(eqsig(c, h->c)) return h->c->fun;
+		h = h->next;
+	}
+
+	return nil;
+}

          
A => sky/cmd/dat.h +40 -0
@@ 0,0 1,40 @@ 
+typedef struct Cmd Cmd;
+typedef struct Cmdctxt Cmdctxt;
+
+enum
+{
+	Teof = Leof,
+	Terror = -10,
+	Tcomment = 0,
+	Tcmd,
+	Teos,
+	Tidentifier,
+	Tdecimal,
+	Tstr,
+};
+
+enum
+{
+	Cmaxargs = 32,
+	Coverflow = -10,
+};
+
+struct Cmd
+{
+	Rune str[Ltoksize+1];
+	int argc, argtyp[Cmaxargs];
+	int (*fun)(Tok*, int);
+};
+
+struct Cmdnode
+{
+	Cmd *c;
+	struct Cmdnode *next;
+};
+
+struct Cmdctxt
+{
+	struct Cmdnode *head;
+	int fd;
+};
+

          
A => sky/cmd/fns.h +8 -0
@@ 0,0 1,8 @@ 
+/* lex.c */
+State lexstart(void);
+
+/* cmd.c */
+Cmdctxt *mkcmdctxt(int fd);
+int cmdreg(Cmdctxt *ctxt, Cmd *c);
+void cmdproc(void *ctxt);
+void cmdterm(Cmdctxt *ctxt);
  No newline at end of file

          
A => sky/cmd/lex.c +255 -0
@@ 0,0 1,255 @@ 
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+
+#include "debug.h"
+#include "lex.h"
+
+#include "dat.h"
+#include "fns.h"
+
+State seof(Lexer *l, void *aux);
+State serror(Lexer *l, void *aux);
+State scmd(Lexer *l, void *aux);
+State scomment(Lexer *l, void *aux);
+State sswtch(Lexer *l, void *aux);
+State seos(Lexer *l, void *aux);
+State sidentifier(Lexer *l, void *aux);
+State sdecimal(Lexer *l, void *aux);
+State sstr(Lexer *l, void *aux);
+
+State
+lexstart(void)
+{
+	State nxt;
+
+	nxt.fun = &scmd;
+	return nxt;
+}
+
+State
+seof(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lemit(l, Teof);
+
+	nxt.fun = &seof;
+	return nxt;
+}
+
+State
+serror(Lexer *l, void *aux)
+{
+	State nxt;
+
+	if(lpeek(l) == Leof){
+		nxt.fun = &seof;
+		return nxt;
+	}
+
+	lemit(l, Terror);
+
+	nxt.fun = &scmd;
+	return nxt;
+}
+
+State
+scmd(Lexer *l, void *aux)
+{
+	State nxt;
+	Rune *s;
+	long len;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	if(laccept(l, L";")){
+		lbackup(l);
+		nxt.fun = &seos;
+		return nxt;
+	}
+
+	lacceptuntil(l, L" \t\n");
+
+	if((len = llen(l)) < 0){
+		nxt.fun = &serror;
+		nxt.aux = "cmd";
+		return nxt;
+	}else if(len == 0){
+		nxt.fun = &scmd;
+		return nxt;
+	}
+		
+	if(*l->b == L'#')
+		nxt.fun = &scomment;
+	else{
+		lemit(l, Tcmd);
+		nxt.fun = &sswtch;
+	}
+
+	return nxt;
+}
+
+State
+scomment(Lexer *l, void *aux)
+{
+	State nxt;
+
+	if(lacceptuntil(l, L"\n") < 0){
+		nxt.fun = &serror;
+		nxt.aux = "comment";
+		return nxt;
+	}
+	laccept(l, L"\n");
+	lignore(l);
+
+	nxt.fun = &scmd;
+	return nxt;
+}
+
+State
+sswtch(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	switch(lpeek(l)){
+	case L';':
+		nxt.fun = &seos;
+		return nxt;
+	case L'#':
+		nxt.fun = &scomment;
+		return nxt;
+	case L'"':
+		nxt.fun = &sstr;
+		return nxt;
+	}
+
+	if(laccept(l, L"0123456789-+.")){
+		lbackup(l);
+		nxt.fun = &sdecimal;
+		return nxt;
+	}
+
+	if(laccept(l, L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")){
+		lbackup(l);
+		nxt.fun = &sidentifier;
+		return nxt;
+	}
+
+	nxt.fun = &serror;
+	nxt.aux = "sswtch";
+	return nxt;
+}
+
+State
+seos(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	if(!laccept(l, L";")){
+		nxt.fun = &serror;
+		nxt.aux = "eos";
+		return nxt;
+	}
+
+	lemit(l, Teos);
+
+	nxt.fun = &scmd;
+	return nxt;
+}
+
+State
+sidentifier(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	if(!laccept(l, L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")){
+		nxt.fun = &serror;
+		nxt.aux = "identifier";
+		return nxt;
+	}
+
+	if(lacceptrun(l, L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_.0123456789") < 0){
+		nxt.fun = &serror;
+		nxt.aux = "identifier";
+		return nxt;
+	}
+
+	lemit(l, Tidentifier);
+
+	nxt.fun = &sswtch;
+	return nxt;
+}
+
+State
+sdecimal(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	if(!laccept(l, L"0123456789.+-")){
+		nxt.fun = &serror;
+		nxt.aux = "decimal";
+		return nxt;
+	}
+	lbackup(l);
+
+	laccept(l, L"+-");
+	lacceptrun(l, L"0123456789");
+	laccept(l, L".");
+	lacceptrun(l, L"0123456789");
+	laccept(l, L"eE");
+	laccept(l, L"+-");
+	if(lacceptrun(l, L"0123456789") < 0){
+		nxt.fun = &serror;
+		nxt.aux = "decimal";
+		return nxt;
+	}
+
+	lemit(l, Tdecimal);
+
+	nxt.fun = &sswtch;
+	return nxt;
+}
+
+State
+sstr(Lexer *l, void *aux)
+{
+	State nxt;
+
+	lacceptrun(l, L" \t\n");
+	lignore(l);
+
+	if(!laccept(l, L"\"")){
+		nxt.fun = &serror;
+		nxt.aux = "str";
+		return nxt;
+	}
+	lignore(l);
+
+	if(lacceptuntil(l, L"\"") < 0){
+		nxt.fun = &serror;
+		nxt.aux = "str";
+		return nxt;
+	}
+
+	lemit(l, Tstr);
+	laccept(l, L"\"");
+
+	nxt.fun = &sswtch;
+	return nxt;
+}
+

          
A => sky/cmd/mkfile +23 -0
@@ 0,0 1,23 @@ 
+</$objtype/mkfile
+
+TARG=test_lex test_cmd
+
+INCDIR=../include
+
+CFLAGS=-I$INCDIR
+LIB=../liblex/liblex.a
+
+OFILES=\
+	cmd.$O\
+	lex.$O\
+
+HFILES=\
+	dat.h\
+	fns.h\
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+
+</sys/src/cmd/mkmany

          
A => sky/cmd/test_cmd.c +92 -0
@@ 0,0 1,92 @@ 
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+
+#include "lex.h"
+
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+	STACK = 256*1024,
+};
+
+double
+runeatof(Rune *s)
+{
+	char cs[Ltoksize+1], *c;
+	int i;
+
+	for(i=0, c=cs; i<Ltoksize && *s; i++)
+		*c++ = *s++;
+	*c = '\0';
+
+	return atof(cs);
+}
+
+int
+add(Tok *argv, int argc)
+{
+	int i;
+	double a, sum;
+
+	print("add ");
+	for(i=0, sum=0; i<argc; i++, argv++){
+		print("%S", argv->str);
+		a = runeatof(argv->str);
+		print("(%f) ", a);
+		sum += a;
+	}
+	print("=> %f\n", sum);
+	return 0;
+}
+
+int
+mult(Tok *argv, int argc)
+{
+	int i;
+	double a, prod;
+
+	print("mult ");
+	for(i=0, prod=1; i<argc; i++, argv++){
+		print("%S", argv->str);
+		a = runeatof(argv->str);
+		print("(%f) ", a);
+		prod *= a;
+	}
+	print("=> %f\n", prod);
+	return 0;
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	Cmdctxt *ctxt;
+	Cmd c;
+
+	if((ctxt = mkcmdctxt(0)) == nil)
+		sysfatal("out of memory!");
+
+	runestrcpy(c.str, L"add");
+	c.argc = 2;
+	c.argtyp[0] =  c.argtyp[1] = c.argtyp[2] = Tdecimal;
+	c.fun = &add;
+	cmdreg(ctxt, &c);
+
+	c.argc = 3;
+	cmdreg(ctxt, &c);
+
+	runestrcpy(c.str, L"mult");
+	c.argc = 2;
+	c.fun = &mult;
+	cmdreg(ctxt, &c);
+
+	c.argc = 3;
+	cmdreg(ctxt, &c);
+
+	proccreate(&cmdproc, ctxt, STACK);
+
+	for(;;);
+}

          
A => sky/cmd/test_lex.c +61 -0
@@ 0,0 1,61 @@ 
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <bio.h>
+
+#include "lex.h"
+
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+	STACK = 64*1024,
+};
+
+void
+parseproc(void *arg)
+{
+	Lexer l;
+	Tok t;
+
+	linit(&l, 0, lexstart());
+
+	while(t = lnexttok(&l), t.typ != Teof){
+		print("next: %d > ", t.typ);
+		switch(t.typ){
+		case Teof:
+			print("Teof: ");
+			break;
+		case Terror:
+			print("Terror: ");
+			break;
+		case Tcmd:
+			print("Tcmd: ");
+			break;
+		case Teos:
+			print("Teos: ");
+			break;
+		case Tidentifier:
+			print("Tidentifier: ");
+			break;
+		case Tdecimal:
+			print("Tdecimal: ");
+			break;
+		case Tstr:
+			print("Tstr: ");
+			break;
+		}
+		print("%S\n", t.str);
+	}
+
+	exits(0);
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+	proccreate(&parseproc, nil, STACK);
+
+	for(;;);
+}

          
A => sky/include/debug.h +6 -0
@@ 0,0 1,6 @@ 
+#ifndef DEBUG_LEVEL
+#define DEBUG_LEVEL 0
+#endif
+
+#define DBIF(l) if((l)<=DEBUG_LEVEL)
+#define DBPRINT(l, ...) if((l)<=DEBUG_LEVEL) fprint(2, __VA_ARGS__);

          
M sky/include/lex.h +16 -7
@@ 2,24 2,29 @@ typedef struct State State;
 typedef struct Tok Tok;
 typedef struct Lexer Lexer;
 
-enum {
-	Ltoksize = 256,
+enum
+{
+	Ltoksize = 512,
 
 	Leof = -1,
 	Loverflow = -10,
 };
 
 /* wrapper to simulate recursive function type */
-struct State {
-	State (*next)(Lexer *l);
+struct State
+{
+	State (*fun)(Lexer*, void*);
+	void *aux;
 };
 
-struct Tok {
+struct Tok
+{
 	int typ;
 	Rune str[Ltoksize+1];
 };
 	
-struct Lexer {
+struct Lexer
+{
 	Biobuf *bp;
 	Rune b[Ltoksize+1], *cur;
 	int start, pos;

          
@@ 36,7 41,11 @@ long lpeek(Lexer *l);
 
 void lignore(Lexer *l);
 int laccept(Lexer *l, Rune *s);
-void lacceptrun(Lexer *l, Rune *s);
+long lacceptrun(Lexer *l, Rune *s);
+long lacceptuntil(Lexer *l, Rune *s);
 
 void lemit(Lexer *l, int typ);
 Tok lnexttok(Lexer *l);
+
+long llen(Lexer *l);
+Rune *lestr(Lexer *l);

          
M sky/include/sky.h +17 -11
@@ 1,4 1,4 @@ 
-/** dat **/
+#pragma lib "libsky.a"
 
 typedef struct Gdloc Gdloc;
 typedef struct Eqloc Eqloc;

          
@@ 10,49 10,57 @@ typedef struct Viewangle Viewangle;
 typedef struct Camera Camera;
 
 /* geodetic location */
-struct Gdloc {
+struct Gdloc
+{
 	double lat;	/* in rad */
 	double lng;	/* in rad */
 	double alt;	/* in meters from mean sea level */
 };
 
 /* equatorial location */
-struct Eqloc {
+struct Eqloc
+{
 	double ra;		/* right ascension in rad */
 	double dec;	/* declination in rad */
 };
 
 /* horizontal location */
-struct Hzloc {
+struct Hzloc
+{
 	double az;	/* azimuth in rad */
 	double z;		/* zenith in rad (complement of elevation) */
 };
 
 /* galactic location */
-struct Gxloc {
+struct Gxloc
+{
 	double b;		/* galactic latitude in rad */
 	double l;		/* galactic longitude in rad */
 };
 
 /* 2D projection */
-struct Planar {
+struct Planar
+{
 	double x, y;
 };
 
-struct Obs {
+struct Obs
+{
 	Gdloc g;
 	double P;		/* pressure in kPa */
 	double T;		/* temperature in K */
 	double rh;		/* relative humidity */
 };
 
-struct Viewangle {
+struct Viewangle
+{
 	double az;	/* azimuth in rad */
 	double z;		/* zenith in rad (complement of elevation) */
 	double roll;	/* roll in rad */
 };
 
-struct Camera {
+struct Camera
+{
 	double f;		/* focal length in mm */
 	double w, h;	/* sensor dimensions in mm */
 	int wpx, hpx;	/* sensor dimensions in pixels */		

          
@@ 77,8 85,6 @@ struct Camera {
 #define NTP_K 293.15d
 
 
-/** fns **/
-
 Eqloc gx2eq00(Gxloc gx);
 Gxloc eq2gx00(Eqloc eq);
 

          
M sky/liblex/lex.c +60 -6
@@ 3,6 3,8 @@ 
 #include <thread.h>
 #include <bio.h>
 
+#include "debug.h"
+
 #include "lex.h"
 
 int

          
@@ 11,7 13,12 @@ linit(Lexer *l, int fd, State state)
 	if((l->c=chancreate(sizeof(Tok), 2)) == nil)
 		return Leof;
 
-	if(Binit(l->bp, fd, OREAD) == Beof) {
+	if((l->bp = malloc(sizeof(Biobuf))) == nil){
+		chanfree(l->c);
+		return Loverflow;
+	}
+	if(Binit(l->bp, fd, OREAD) == Beof){
+		free(l->bp);
 		chanfree(l->c);
 		return Leof;
 	}

          
@@ 28,6 35,7 @@ lterm(Lexer *l)
 {
 	chanfree(l->c);
 	Bterm(l->bp);
+	free(l->bp);
 }
 
 long

          
@@ 78,16 86,36 @@ laccept(Lexer *l, Rune *s)
 
 	if((c = lnext(l)) < 0) return 0;
 
-	if(runestrchr(s, c) != 0) return 1;
+	if(runestrchr(s, c) != nil) return 1;
 
 	lbackup(l);
 	return 0;
 }
 
-void
+long
 lacceptrun(Lexer *l, Rune *s)
 {
-	while(laccept(l, s));
+	long c;
+	int p, done;
+
+	p = l->pos;
+	while((c = lnext(l)) >= 0 && !(done = !runestrchr(s, c)));
+	if(done) lbackup(l);
+
+	return done ? l->pos-p : Loverflow;
+}
+
+long
+lacceptuntil(Lexer *l, Rune *s)
+{
+	long c;
+	int p, done;
+
+	p = l->pos;
+	while((c = lnext(l)) >= 0 && !(done = !!runestrchr(s, c)));
+	if(done) lbackup(l);
+
+	return done ? l->pos-p : Loverflow;
 }
 
 void

          
@@ 97,7 125,8 @@ lemit(Lexer *l, int typ)
 
 	t.typ = typ;
 	runestrncpy(t.str, l->b, l->pos-l->start);
-	t.str[Ltoksize] = 0;
+	t.str[l->pos-l->start] = L'\0';
+	lignore(l);
 
 	send(l->c, &t);
 }

          
@@ 107,6 136,31 @@ lnexttok(Lexer *l)
 {
 	Tok t;
 
-	while(!nbrecv(l->c, &t)) l->state = l->state.next(l);
+	while(!nbrecv(l->c, &t)) l->state = l->state.fun(l, l->state.aux);
+
 	return t;	
 }
+
+long
+llen(Lexer *l)
+{
+	long len;
+
+	len = l->pos - l->start;
+	return len >= 0 && len <  Ltoksize ? len : Loverflow;
+}
+
+Rune*
+lestr(Lexer *l){
+	Rune *s;
+	long len;
+
+	len = llen(l);
+	if(len < 0) return nil;
+	if((s=malloc((len+1)*sizeof(Rune))) == nil) return nil;
+
+	runestrncpy(s, l->b, len);
+	s[len] = L'\0';
+
+	return s;
+}
  No newline at end of file