mg(1) list evaluation

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

mg(1) list evaluation

Mark Lumsden-3
This diff introduces lists to the startup file of mg. With this diff
applied you could have something similar to:

(define y(list b.txt c.txt))
(define z(list d.txt e.txt))
(find-file a.txt y f.txt z)

in your .mg file, and when you opened mg, there would be 7 buffers opened
(a.txt, b.txt, c.txt, d.txt, e.txt, and f.txt, including the *scratch*
buffer)

When I got about 3/4 of the way through this diff I realised I was
probably not doing it the best way. I should have a list of 'key' words
and multi-line parsing and lists within lists etc... I think I need to go
back and take those ideas and apply them bit by bit to this diff. However,
as a way forward does anyone object to this diff going in? My plan is to
hopefully have some kind of user definable functions in mg, but functions
(usually) need conditionals and variables, so I am trying to add in the
basics beforehand. I'm using this for regress tests within mg, but
arguably there could be other uses, depending on how much was developed.

Like with the previous multi arg diff, if your .mg file has no '('
characters on the first line, you won't (or shouldn't) see any functional
difference in mg. Any comments/objections?

Mark

Index: bell.c
===================================================================
RCS file: /cvs/src/usr.bin/mg/bell.c,v
retrieving revision 1.4
diff -u -p -u -p -r1.4 bell.c
--- bell.c 3 Jan 2016 19:37:08 -0000 1.4
+++ bell.c 15 Jul 2019 20:12:00 -0000
@@ -26,6 +26,23 @@ bellinit(void)
  dovisiblebell = 0;
  }

+
+int
+dobeep_msgs(const char *msg, const char *s)
+{
+ ewprintf("%s %s", msg, s);
+ dobeep();
+ return (FALSE);
+}
+
+int
+dobeep_msg(const char *msg)
+{
+ ewprintf("%s", msg);
+ dobeep();
+ return (FALSE);
+}
+
  void
  dobeep(void)
  {
Index: def.h
===================================================================
RCS file: /cvs/src/usr.bin/mg/def.h,v
retrieving revision 1.162
diff -u -p -u -p -r1.162 def.h
--- def.h 3 Jul 2019 18:11:07 -0000 1.162
+++ def.h 15 Jul 2019 20:12:00 -0000
@@ -711,6 +711,8 @@ int compile(int, int);
  void bellinit(void);
  int toggleaudiblebell(int, int);
  int togglevisiblebell(int, int);
+int dobeep_msgs(const char *, const char *);
+int dobeep_msg(const char *);
  void dobeep(void);

  /*
Index: extend.c
===================================================================
RCS file: /cvs/src/usr.bin/mg/extend.c,v
retrieving revision 1.67
diff -u -p -u -p -r1.67 extend.c
--- extend.c 11 Jul 2019 18:20:18 -0000 1.67
+++ extend.c 15 Jul 2019 20:12:00 -0000
@@ -29,6 +29,17 @@ static int dobind(KEYMAP *, const char
  static char *skipwhite(char *);
  static char *parsetoken(char *);
  static int bindkey(KEYMAP **, const char *, KCHAR *, int);
+static int clearvars(void);
+
+struct varentry {
+ SLIST_ENTRY(varentry) entry;
+ char *name;
+ char *vals;
+ int count;
+};
+SLIST_HEAD(slisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead);
+
+#define BUFSIZE 128 /* Size of line contents - could be larger? */

  /*
   * Insert a string, mainly for use from macros (created by selfinsert).
@@ -37,7 +48,7 @@ static int bindkey(KEYMAP **, const cha
  int
  insert(int f, int n)
  {
- char buf[128], *bufp, *cp;
+ char buf[BUFSIZE], *bufp, *cp;
  int count, c;

  if (inmacro) {
@@ -595,7 +606,7 @@ extend(int f, int n)
  int
  evalexpr(int f, int n)
  {
- char exbuf[128], *bufp;
+ char exbuf[BUFSIZE], *bufp;

  if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
     EFNEW | EFCR)) == NULL)
@@ -616,18 +627,21 @@ evalbuffer(int f, int n)
  struct line *lp;
  struct buffer *bp = curbp;
  int s;
- static char excbuf[128];
+ static char excbuf[BUFSIZE];

  for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
- if (llength(lp) >= 128)
+ if (llength(lp) >= BUFSIZE)
  return (FALSE);
  (void)strncpy(excbuf, ltext(lp), llength(lp));

  /* make sure it's terminated */
  excbuf[llength(lp)] = '\0';
- if ((s = excline(excbuf)) != TRUE)
+ if ((s = excline(excbuf)) != TRUE) {
+ (void) clearvars();
  return (s);
+ }
  }
+ (void) clearvars();
  return (TRUE);
  }

@@ -657,7 +671,7 @@ load(const char *fname)
  {
  int s = TRUE, line, ret;
  int nbytes = 0;
- char excbuf[128], fncpy[NFILEN];
+ char excbuf[BUFSIZE], fncpy[NFILEN];
  FILE    *ffp;

  if ((fname = adjustname(fname, TRUE)) == NULL)
@@ -693,17 +707,39 @@ load(const char *fname)
  }

  /*
- * Line has a '(' as the first non-white char.
+ * Is an item a value or a variable?
+ */
+static int
+isvar(char **argp, char **tmpbuf, int sizof)
+{
+ struct varentry *v1 = NULL;
+
+ if (SLIST_EMPTY(&varhead))
+ return (FALSE);
+
+ SLIST_FOREACH(v1, &varhead, entry) {
+ if (strcmp(*argp, v1->name) == 0) {
+ (void)(strlcpy(*tmpbuf, v1->vals, sizof) >= sizof);
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*
+ * Pass a list of arguments to a function.
   */
  static int
  multiarg(char *funstr)
  {
  regex_t  regex_buff;
  PF funcp;
- char excbuf[128];
- char *cmdp, *argp, *fendp, *endp, *p, *s = " ";
+ char excbuf[BUFSIZE], argbuf[BUFSIZE], *contbuf, tmpbuf[BUFSIZE];
+ char *cmdp, *argp, *fendp, *endp, *p, *t, *s = " ";
  int singlecmd = 0, spc, numparams, numspc;
+ int inlist, foundlst = 0, last, sizof;

+ contbuf = NULL;
  endp = strrchr(funstr, ')');
  if (endp == NULL) {
  ewprintf("No closing parenthesis found");
@@ -714,24 +750,18 @@ multiarg(char *funstr)
  *p = '\0';
  /* we now know that string starts with '(' and ends with ')' */
  if (regcomp(&regex_buff, "^[(][\t ]*[)]$", REG_EXTENDED)) {
- dobeep();
- ewprintf("Could not compile regex");
  regfree(&regex_buff);
- return(FALSE);
+ return (dobeep_msg("Could not compile regex"));
  }
  if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
- dobeep();
- ewprintf("No command found");
  regfree(&regex_buff);
- return(FALSE);
+ return (dobeep_msg("No command found"));
  }
  /* currently there are no mg commands that don't have a letter */
  if (regcomp(&regex_buff, "^[(][\t ]*[A-Za-z-]+[\t ]*[)]$",
     REG_EXTENDED)) {
- dobeep();
- ewprintf("Could not compile regex");
  regfree(&regex_buff);
- return(FALSE);
+ return (dobeep_msg("Could not compile regex"));
  }
  if (!regexec(&regex_buff, funstr, 0, NULL, 0))
  singlecmd = 1;
@@ -754,53 +784,82 @@ multiarg(char *funstr)

  *fendp = '\0';
  /*
- * If no extant mg command found, line could be a (define of some kind.
- * Since no defines exist at the moment, just return.
+ * If no extant mg command found, just return.
  */
- if ((funcp = name_function(cmdp)) == NULL) {
- dobeep();
- ewprintf("Unknown command: %s", cmdp);
- return (FALSE);
- }
+ if ((funcp = name_function(cmdp)) == NULL)
+ return (dobeep_msgs("Unknown command: ", cmdp));
+
  numparams = numparams_function(funcp);
- if (numparams == 0) {
- dobeep();
- ewprintf("Command takes no arguments: %s", cmdp);
- return (FALSE);
- }
+ if (numparams == 0)
+ return (dobeep_msgs("Command takes no arguments: ", cmdp));

  /* now find the first argument */
- p = fendp + 1;
- argp = skipwhite(p);
+ p = fendp + 1;
+ p = skipwhite(p);
+ if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
+ return (dobeep_msg("strlcpy error"));
+ argp = argbuf;
  numspc = spc = 1; /* initially fake a space so we find first argument */
+ inlist = last = 0;

  for (p = argp; *p != '\0'; p++) {
+ if (foundlst) {
+ foundlst = 0;
+ p--; /* otherwise 1st arg is missed from list. */
+ }
  if (*p == ' ' || *p == '\t' || *p == ')') {
  if (spc == 1)
  continue;
  if (spc == 0 && (numspc % numparams == 0)) {
+ if (*p == ')')
+ last = 1;
+ else
+ last = 0;
  *p = '\0'; /* terminate arg string */
+ endp = p + 1;
  excbuf[0] = '\0';
+ /* Is arg a var? */
+ if (!inlist) {
+ if ((contbuf = strndup(endp, BUFSIZE))
+    == NULL)
+ return(FALSE);
+ sizof = sizeof(tmpbuf);
+ t = tmpbuf;
+ if (isvar(&argp, &t, sizof)) {
+ *p = ' ';
+ (void)(strlcpy(argbuf, tmpbuf,
+    sizof) >= sizof);
+ p = argp = argbuf;
+ spc = 1;
+ foundlst = inlist = 1;
+ continue;
+ }
+ }
  if (strlcpy(excbuf, cmdp, sizeof(excbuf))
-     >= sizeof(excbuf)) {
- dobeep();
- ewprintf("strlcpy error");
- return (FALSE);
- }
+     >= sizeof(excbuf))
+ return (dobeep_msg("strlcpy error"));
  if (strlcat(excbuf, s, sizeof(excbuf))
-    >= sizeof(excbuf)) {
- dobeep();
- ewprintf("strlcpy error");
- return (FALSE);
- }
+    >= sizeof(excbuf))
+ return (dobeep_msg("strlcat error"));
  if (strlcat(excbuf, argp, sizeof(excbuf))
-    >= sizeof(excbuf)) {
- dobeep();
- ewprintf("strlcpy error");
- return (FALSE);
- }
+    >= sizeof(excbuf))
+ return (dobeep_msg("strlcat error"));
+
  excline(excbuf);
- *p = ' '; /* so 'for' loop can continue */
+ *p = ' '; /* so 'for' loop can continue */
+ if (last) {
+ if (contbuf != NULL) {
+ (void)strlcpy(argbuf, contbuf,
+    sizeof(argbuf));
+ contbuf = NULL;
+ p = argp = argbuf;
+ foundlst = 1;
+ inlist = 0;
+ continue;
+ }
+ spc = 1;
+ inlist = 0;
+ }
  }
  numspc++;
  spc = 1;
@@ -815,6 +874,165 @@ multiarg(char *funstr)
  return (TRUE);
  }

+
+/*
+ * The (define string _must_ adhere to the regex in foundparen.
+ * This is not the correct way to do parsing but it does highlight
+ * the issues.
+ */
+static int
+foundlist(char *defstr)
+{
+ struct varentry *vt, *v1 = NULL;
+ const char e[1] = "e", t[1] = "t";
+ char *p, *vnamep, *vendp = NULL, *valp;
+ int spc;
+
+
+ p = defstr + 1;         /* move past first '(' char.    */
+ p = skipwhite(p);     /* find first char of 'define'. */
+ p = strstr(p, e); /* find first 'e' in 'define'. */
+ p = strstr(++p, e); /* find second 'e' in 'define'. */
+ p++; /* move past second 'e'. */
+ vnamep = skipwhite(p);  /* find first char of var name. */
+ vendp = vnamep;
+
+ /* now find the end of the list name */
+ while (1) {
+ ++vendp;
+ if (*vendp == '(' || *vendp == ' ' || *vendp == '\t')
+ break;
+ }
+ *vendp = '\0';
+ /*
+ * Check list name is not an existing function.
+ * Although could this be allowed? Shouldn't context dictate?
+ */
+ if (name_function(vnamep) != NULL)
+ return(dobeep_msgs("Variable/function name clash:", vnamep));
+
+ p = ++vendp;
+ p = strstr(p, t); /* find 't' in 'list'. */
+ valp = skipwhite(++p); /* find first value */
+ /*
+ * Now we have the name of the list starting at 'vnamep',
+ * and the first value is at 'valp', record the details
+ * in a linked list. But first remove variable, if existing already.
+ */
+ if (!SLIST_EMPTY(&varhead)) {
+ SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
+ if (strcmp(vnamep, v1->name) == 0)
+ SLIST_REMOVE(&varhead, v1, varentry, entry);
+ }
+ }
+ if ((v1 = malloc(sizeof(struct varentry))) == NULL)
+ return (ABORT);
+ SLIST_INSERT_HEAD(&varhead, v1, entry);
+ if ((v1->name = strndup(vnamep, BUFSIZE)) == NULL)
+ return(dobeep_msg("strndup error"));
+ v1->count = 0;
+ vendp = NULL;
+
+ /* initially fake a space so we find first value */
+ spc = 1;
+ /* now loop through values in list value string while counting them */
+ for (p = valp; *p != '\0'; p++) {
+ if (*p == ' ' || *p == '\t') {
+ if (spc == 0)
+ vendp = p;
+ spc = 1;
+ } else if (*p == ')') {
+ vendp = ++p; /* currently need ')' */
+ break;
+ } else {
+ if (spc == 1)
+ v1->count++;
+ spc = 0;
+ }
+ }
+ *vendp = '\0';
+ if ((v1->vals = strndup(valp, BUFSIZE)) == NULL)
+ return(dobeep_msg("strndup error"));
+
+ return (TRUE);
+}
+
+/*
+ * to do
+ */
+static int
+foundvar(char *funstr)
+{
+ ewprintf("to do");
+ return (TRUE);
+}
+
+/*
+ * Finished with evaluation, so clean up any vars.
+ */
+static int
+clearvars(void)
+{
+ struct varentry *v1 = NULL;
+
+ while (!SLIST_EMPTY(&varhead)) {
+ v1 = SLIST_FIRST(&varhead);
+ SLIST_REMOVE_HEAD(&varhead, entry);
+ free(v1->vals);
+ free(v1->name);
+ free(v1);
+ }
+ return (FALSE);
+}
+
+/*
+ * Line has a '(' as the first non-white char.
+ * Do some very basic parsing of line with '(' as the first character.
+ * Multi-line not supported at the moment, To do.
+ */
+static int
+foundparen(char *funstr)
+{
+ regex_t  regex_buff;
+ char *regs;
+
+ /* Does the line have a list 'define' like: */
+ /* (define alist(list 1 2 3 4)) */
+ regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[(][\t ]*list[\t ]+"\
+ "[^\t ]+.*[)][\t ]*[)]";
+ if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+ regfree(&regex_buff);
+ return(dobeep_msg("Could not compile regex"));
+ }
+ if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+ regfree(&regex_buff);
+ return(foundlist(funstr));
+ }
+ /* Does the line have a single variable 'define' like: */
+ /* (define i 0) */
+ regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[^\t (]+[\t ]*[)]";
+ if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+ regfree(&regex_buff);
+ return(dobeep_msg("Could not compile regex"));
+ }
+ if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+ regfree(&regex_buff);
+ return(foundvar(funstr));
+ }
+ /* Does the line have an unrecognised 'define' */
+ regs = "^[(][\t ]*define[\t ]+";
+ if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
+ regfree(&regex_buff);
+ return(dobeep_msg("Could not compile regex"));
+ }
+ if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
+ regfree(&regex_buff);
+ return(dobeep_msg("Invalid use of define"));
+ }
+ regfree(&regex_buff);
+ return(multiarg(funstr));
+}
+
  /*
   * excline - run a line from a load file or eval-expression.
   */
@@ -837,18 +1055,16 @@ excline(char *line)

  lp = NULL;

- if (macrodef || inmacro) {
- dobeep();
- ewprintf("Not now!");
- return (FALSE);
- }
+ if (macrodef || inmacro)
+ return(dobeep_msg("Not now!"));
+
  f = 0;
  n = 1;
  funcp = skipwhite(line);
  if (*funcp == '\0')
  return (TRUE); /* No error on blank lines */
  if (*funcp == '(')
- return (multiarg(funcp));
+ return (foundparen(funcp));
  line = parsetoken(funcp);
  if (*line != '\0') {
  *line++ = '\0';
@@ -867,11 +1083,9 @@ excline(char *line)
  return (FALSE);
  n = (int)nl;
  }
- if ((fp = name_function(funcp)) == NULL) {
- dobeep();
- ewprintf("Unknown function: %s", funcp);
- return (FALSE);
- }
+ if ((fp = name_function(funcp)) == NULL)
+ return (dobeep_msgs("Unknown function: ", funcp));
+
  if (fp == bindtokey || fp == unbindtokey) {
  bind = BINDARG;
  curmap = fundamental_map;