master xplshn/aruu / cmd / dev / ninqu / parse.c
  1#include "ninqu.h"
  2
  3#include <ctype.h>
  4#include <limits.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7#include <string.h>
  8
  9struct StrList files_read;
 10
 11/* second child must be an atom, more children may follow */
 12const char *
 13first_atom(struct SNode *node, const char *msg)
 14{
 15  if (node->nkids < 2 || node->kids[1]->kind != S_ATOM)
 16    eprintf("manifest: %s\n", msg);
 17  return node->kids[1]->atom;
 18}
 19
 20/* second child must be the only other child, and an atom */
 21const char *
 22sole_atom(struct SNode *node, const char *msg)
 23{
 24  if (node->nkids != 2 || node->kids[1]->kind != S_ATOM)
 25    eprintf("manifest: %s\n", msg);
 26  return node->kids[1]->atom;
 27}
 28
 29/* a dep operand is a bare atom, a (rule NAME) stored as @NAME, or
 30 * an (after X...) whose operands are order-only */
 31static void
 32parse_dep_item(struct SNode *item, struct StrList *hard, struct StrList *ord, int allow_after)
 33{
 34  const char *h;
 35
 36  if (item->kind == S_ATOM) {
 37    sl_push(hard, item->atom);
 38    return;
 39  }
 40  h = s_head(item);
 41  if (!h)
 42    eprintf("manifest: empty () in dep\n");
 43  if (strcmp(h, "rule") == 0) {
 44    char buf[256];
 45    snprintf(buf, sizeof buf, "@%s", sole_atom(item, "(rule NAME) takes one name"));
 46    sl_push(hard, buf);
 47    return;
 48  }
 49  if (strcmp(h, "after") == 0) {
 50    int i;
 51    if (!allow_after)
 52      eprintf("manifest: (after ...) not allowed here\n");
 53    for (i = 1; i < item->nkids; i++)
 54      parse_dep_item(item->kids[i], ord, ord, 0);
 55    return;
 56  }
 57  eprintf("manifest: unknown dep operand '%s'\n", h);
 58}
 59
 60static void
 61parse_dep_clause(struct SNode *clause, struct StrList *hard, struct StrList *ord)
 62{
 63  int i;
 64  for (i = 1; i < clause->nkids; i++)
 65    parse_dep_item(clause->kids[i], hard, ord, 1);
 66}
 67
 68void
 69parse_exec_clause(struct SNode *clause, struct Cmd *cmd)
 70{
 71  int i;
 72  memset(cmd, 0, sizeof *cmd);
 73  for (i = 1; i < clause->nkids; i++) {
 74    if (clause->kids[i]->kind != S_ATOM)
 75      eprintf("manifest: (exec ...) operands must be atoms\n");
 76    sl_push(&cmd->argv, clause->kids[i]->atom);
 77  }
 78  if (cmd->argv.n == 0)
 79    eprintf("manifest: (exec ...) needs one operand\n");
 80}
 81
 82/* desugar to (exec sh -c STR). sh is the posix shell, overridable
 83 * by writing (exec ...) directly */
 84void
 85parse_shell_clause(struct SNode *clause, struct Cmd *cmd)
 86{
 87  memset(cmd, 0, sizeof *cmd);
 88  sl_push(&cmd->argv, "sh");
 89  sl_push(&cmd->argv, "-c");
 90  sl_push(&cmd->argv, sole_atom(clause, "(shell \"...\") takes one string"));
 91}
 92
 93static void
 94parse_pipe_stage(struct SNode *stage, struct StrList *argv)
 95{
 96  const char *h = s_head(stage);
 97  int         j;
 98
 99  if (!h)
100    eprintf("manifest: (pipe ...) stage must be a form\n");
101  if (strcmp(h, "exec") == 0) {
102    for (j = 1; j < stage->nkids; j++) {
103      if (stage->kids[j]->kind != S_ATOM)
104        eprintf("manifest: (exec ...) operands must be atoms\n");
105      sl_push(argv, stage->kids[j]->atom);
106    }
107  } else if (strcmp(h, "shell") == 0) {
108    sl_push(argv, "sh");
109    sl_push(argv, "-c");
110    sl_push(argv, sole_atom(stage, "(shell \"...\") takes one string"));
111  } else {
112    eprintf("manifest: (pipe) stage must be exec or shell, got '%s'\n", h);
113  }
114  if (argv->n == 0)
115    eprintf("manifest: (pipe) stage cannot be empty\n");
116}
117
118void
119parse_pipe_clause(struct SNode *clause, struct Cmd *cmd)
120{
121  int i, j;
122  memset(cmd, 0, sizeof *cmd);
123  cmd->is_pipe = 1;
124  for (i = 1; i < clause->nkids; i++) {
125    struct StrList  argv = {0};
126    struct StrList *slot;
127
128    parse_pipe_stage(clause->kids[i], &argv);
129
130    slot = emalloc(sizeof *slot);
131    memset(slot, 0, sizeof *slot);
132    for (j = 0; j < argv.n; j++)
133      sl_push(slot, argv.v[j]);
134    if (cmd->nstages == 0)
135      cmd->stages = emalloc(8 * sizeof *cmd->stages);
136    else if (cmd->nstages >= 8)
137      cmd->stages = erealloc(cmd->stages, (size_t)cmd->nstages * 2 * sizeof *cmd->stages);
138    cmd->stages[cmd->nstages++] = slot;
139  }
140  if (cmd->nstages < 2)
141    eprintf("manifest: (pipe ...) needs two stages\n");
142  for (i = 0; i < cmd->nstages; i++) {
143    char *joined = sl_join(cmd->stages[i]);
144    if (i > 0)
145      sl_push(&cmd->argv, "|");
146    sl_push(&cmd->argv, joined);
147    free(joined);
148  }
149}
150
151static void apply_rule_clause(struct Rule *r, const char *name, struct SNode *clause);
152
153static void
154process_rule_body(struct Rule *r, const char *name, struct SNode *form, int start)
155{
156  int i;
157  for (i = start; i < form->nkids; i++)
158    apply_rule_clause(r, name, form->kids[i]);
159}
160
161/* (if EXPR clause...) splices its clauses in when EXPR is true.
162 * reuses the gate grammar so there is one condition language.
163 * a $(...) in the condition is rejected: splicing per glob match
164 * would mean deferring cmd/dep construction, which no rule needs.
165 * use (gate ...) on a separate rule for a per-instance condition */
166static void
167apply_if_clause(struct Rule *r, const char *name, struct SNode *clause)
168{
169  struct Gate *cond;
170
171  if (clause->nkids < 2)
172    eprintf("manifest: rule %s: (if EXPR clause...) needs a condition\n", name);
173  cond = gate_parse(clause->kids[1]);
174  if (gate_has_dyn(cond))
175    eprintf(
176        "manifest: rule %s: (if ...) condition cannot reference $(...), "
177        "only (gate ...) on the whole rule supports a per-instance condition\n",
178        name
179    );
180  if (gate_eval(cond))
181    process_rule_body(r, name, clause, 2);
182  gate_free(cond);
183}
184
185static void
186apply_rule_clause(struct Rule *r, const char *name, struct SNode *clause)
187{
188  const char *h = s_head(clause);
189  if (!h)
190    eprintf("manifest: rule %s: empty () clause\n", name);
191
192  if (strcmp(h, "if") == 0) {
193    apply_if_clause(r, name, clause);
194  } else if (strcmp(h, "glob") == 0) {
195    int j;
196    for (j = 1; j < clause->nkids; j++) {
197      if (clause->kids[j]->kind != S_ATOM)
198        eprintf("manifest: (glob ...) operands must be atoms\n");
199      sl_push(&r->globs, clause->kids[j]->atom);
200    }
201  } else if (strcmp(h, "skip") == 0) {
202    int j;
203    for (j = 1; j < clause->nkids; j++) {
204      if (clause->kids[j]->kind != S_ATOM)
205        eprintf("manifest: (skip ...) operands must be atoms\n");
206      sl_push(&r->skip, clause->kids[j]->atom);
207    }
208  } else if (strcmp(h, "mark") == 0) {
209    strlcpy(
210        r->require_marker,
211        sole_atom(clause, "(mark STR) takes one string"),
212        sizeof r->require_marker
213    );
214  } else if (strcmp(h, "req") == 0) {
215    strlcpy(
216        r->require_file, sole_atom(clause, "(req PATH) takes one path"), sizeof r->require_file
217    );
218  } else if (strcmp(h, "gate") == 0) {
219    if (clause->nkids != 2)
220      eprintf("manifest: (gate EXPR) takes one expression\n");
221    r->gate = gate_parse(clause->kids[1]);
222  } else if (strcmp(h, "dep") == 0) {
223    parse_dep_clause(clause, &r->in, &r->extra_deps);
224  } else if (strcmp(h, "after") == 0) {
225    parse_dep_clause(clause, &r->extra_deps, &r->extra_deps);
226  } else if (strcmp(h, "out") == 0) {
227    strlcpy(r->out, sole_atom(clause, "(out TEMPLATE) takes one template"), sizeof r->out);
228  } else if (strcmp(h, "exec") == 0) {
229    parse_exec_clause(clause, &r->cmd);
230  } else if (strcmp(h, "shell") == 0) {
231    parse_shell_clause(clause, &r->cmd);
232  } else if (strcmp(h, "pipe") == 0) {
233    parse_pipe_clause(clause, &r->cmd);
234  } else if (strcmp(h, "dir") == 0) {
235    strlcpy(r->workdir, sole_atom(clause, "(dir PATH) takes one path"), sizeof r->workdir);
236  } else if (strcmp(h, "phony") == 0) {
237    r->phony = 1;
238  } else if (strcmp(h, "redirect") == 0) {
239    r->redirect = 1;
240  } else if (strcmp(h, "produces") == 0) {
241    strlcpy(r->produces, sole_atom(clause, "(produces NAME) takes one name"), sizeof r->produces);
242  } else if (strcmp(h, "description") == 0) {
243    strlcpy(
244        r->description,
245        sole_atom(clause, "(description STR) takes one string"),
246        sizeof r->description
247    );
248  } else if (strcmp(h, "group") == 0) {
249    /* display-only: shows up in (query rules) output, no effect on
250     * resolution. (member ...) below is the enforcing one. the two
251     * are separate because several rules used (group ...) this way
252     * before namespacing existed */
253    strlcpy(r->group_name, sole_atom(clause, "(group NAME) takes one name"), sizeof r->group_name);
254  } else if (strcmp(h, "member") == 0) {
255    /* (member GROUP [ALIAS]) hides the bare name from the command
256     * line. ALIAS defaults to the rule own name */
257    if (clause->nkids < 2 || clause->nkids > 3 || clause->kids[1]->kind != S_ATOM)
258      eprintf("manifest: rule %s: (member GROUP [ALIAS]) needs a group name\n", name);
259    strlcpy(r->member_of, clause->kids[1]->atom, sizeof r->member_of);
260    if (clause->nkids == 3) {
261      if (clause->kids[2]->kind != S_ATOM)
262        eprintf("manifest: rule %s: (member GROUP ALIAS) alias must be an atom\n", name);
263      strlcpy(r->member_alias, clause->kids[2]->atom, sizeof r->member_alias);
264    }
265  } else if (strcmp(h, "feature") == 0) {
266    sl_push(&r->features, sole_atom(clause, "(feature NAME) takes one name"));
267  } else {
268    eprintf("manifest: rule %s: unknown clause '%s'\n", name, h);
269  }
270}
271
272static void
273parse_rule(struct SNode *form)
274{
275  struct Rule *r;
276  const char  *name;
277
278  name = first_atom(form, "(rule NAME ...) needs a name");
279  r    = rule_new(name);
280  process_rule_body(r, name, form, 2);
281}
282
283static void
284parse_group(struct SNode *form)
285{
286  struct Group *g;
287  int           i;
288  const char   *name;
289
290  name = first_atom(form, "(group NAME ...) needs a name");
291  g    = group_new(name);
292
293  for (i = 2; i < form->nkids; i++) {
294    struct SNode *ref = form->kids[i];
295    const char   *h;
296
297    if (ref->kind == S_ATOM) {
298      sl_push(&g->refs, ref->atom);
299      continue;
300    }
301    h = s_head(ref);
302    if (!h)
303      eprintf("manifest: group %s: empty () ref\n", name);
304    if ((strcmp(h, "rule") == 0 || strcmp(h, "group") == 0) && ref->nkids == 2
305        && ref->kids[1]->kind == S_ATOM) {
306      sl_push(&g->refs, ref->kids[1]->atom);
307      continue;
308    }
309    eprintf("manifest: group %s: unknown ref '%s'\n", name, h);
310  }
311}
312
313/* stores K/V verbatim, unexpanded. expansion happens in meta_apply
314 * at target-resolution time, so a value picks up whatever its vars
315 * hold then, not what they held at load time */
316static void
317parse_meta(struct SNode *form)
318{
319  struct Meta *m;
320  int          i;
321  const char  *name;
322
323  name = first_atom(form, "(meta NAME ...) needs a name");
324  m    = meta_new(name);
325
326  for (i = 2; i < form->nkids; i++) {
327    struct SNode *clause = form->kids[i];
328    const char   *h      = s_head(clause);
329    if (!h || strcmp(h, "set") != 0)
330      eprintf("manifest: meta %s: only (set NAME VAL) clauses are allowed\n", name);
331    if (clause->nkids != 3 || clause->kids[1]->kind != S_ATOM || clause->kids[2]->kind != S_ATOM)
332      eprintf("manifest: meta %s: (set NAME VAL) needs a name and an atom value\n", name);
333    sl_push(&m->keys, clause->kids[1]->atom);
334    sl_push(&m->vals, clause->kids[2]->atom);
335  }
336}
337
338void
339meta_apply(struct Meta *m)
340{
341  int i;
342  for (i = 0; i < m->keys.n; i++) {
343    char *exp = kv_expand(m->vals.v[i]);
344    kv_set_manifest(m->keys.v[i], exp);
345    free(exp);
346  }
347}
348
349static void
350parse_set(struct SNode *form)
351{
352  char buf[8192];
353
354  first_atom(form, "(set NAME VAL) needs a name");
355  if (form->nkids != 3)
356    eprintf("manifest: (set NAME VAL) takes one value\n");
357
358  if (form->kids[2]->kind == S_LIST) {
359    const char *h = s_head(form->kids[2]);
360    if (h && strcmp(h, "capture") == 0) {
361      struct SNode *cap = form->kids[2];
362      struct Cmd    cmd;
363      char         *out;
364
365      if (cap->nkids != 2)
366        eprintf("manifest: (capture (exec ...)) takes one form\n");
367      h = s_head(cap->kids[1]);
368      if (!h)
369        eprintf("manifest: capture operand must be a form\n");
370      if (strcmp(h, "exec") == 0)
371        parse_exec_clause(cap->kids[1], &cmd);
372      else if (strcmp(h, "shell") == 0)
373        parse_shell_clause(cap->kids[1], &cmd);
374      else
375        eprintf("manifest: capture operand must be exec or shell\n");
376      out = capture_argv(&cmd.argv);
377      strlcpy(buf, out, sizeof buf);
378      free(out);
379      kv_set_manifest(form->kids[1]->atom, buf);
380      return;
381    }
382    eprintf("manifest: (set NAME VAL): value must be atom or (capture ...)\n");
383  }
384  kv_set_manifest(form->kids[1]->atom, form->kids[2]->atom);
385}
386
387/* (list) and (append) keep a space-joined string in the kv store.
388 * a $(NAME) alone in a command splits back into argv entries */
389static void
390parse_list_or_append(struct SNode *form, int append)
391{
392  const char *name;
393  char        buf[8192];
394  char        msg[64];
395  size_t      len = 0;
396  int         i;
397  char       *prev;
398
399  snprintf(msg, sizeof msg, "(%s NAME tok...) needs a name", form->kids[0]->atom);
400  name   = first_atom(form, msg);
401  buf[0] = '\0';
402
403  if (append) {
404    prev = kv_get(name);
405    if (prev)
406      len = strlcpy(buf, prev, sizeof buf);
407  }
408  for (i = 2; i < form->nkids; i++) {
409    struct SNode *tok = form->kids[i];
410    char         *exp;
411    if (tok->kind != S_ATOM)
412      eprintf("manifest: (%s NAME tok...): tokens must be atoms\n", form->kids[0]->atom);
413    if (len > 0 && len + 1 < sizeof buf)
414      buf[len++] = ' ';
415    exp = kv_expand(tok->atom);
416    len += strlcpy(buf + len, exp, sizeof buf - len);
417    free(exp);
418  }
419  kv_set_manifest(name, buf);
420}
421
422static void
423parse_map(struct SNode *form)
424{
425  struct Map *m;
426  int         i;
427  const char *name;
428
429  name = first_atom(form, "(map NAME (k v)...) needs a name");
430  m    = map_find(name);
431  if (!m)
432    m = map_new(name);
433
434  for (i = 2; i < form->nkids; i++) {
435    struct SNode *pair = form->kids[i];
436    if (!s_is_list(pair) || pair->nkids != 2 || pair->kids[0]->kind != S_ATOM
437        || pair->kids[1]->kind != S_ATOM)
438      eprintf("manifest: (map %s ...) entries must be (key value)\n", name);
439    kv_set_raw(&m->entries, pair->kids[0]->atom, pair->kids[1]->atom);
440  }
441}
442
443static void
444parse_import(struct SNode *form)
445{
446  char        rp[PATH_MAX];
447  const char *path;
448
449  path = sole_atom(form, "(import PATH) takes one path");
450
451  if (realpath(path, rp) && sl_has(&files_read, rp))
452    return;
453
454  materialize_if_missing(path);
455  if (!file_exists(path))
456    eprintf("manifest: (import %s): missing and no rule produces it\n", path);
457  load_file(path);
458}
459
460static void
461parse_set_file(struct SNode *form)
462{
463  const char *path;
464  FILE       *fp;
465  char        line[8192];
466  char        rp[PATH_MAX];
467
468  path = sole_atom(form, "(set-file PATH) takes one path");
469
470  materialize_if_missing(path);
471  if (!file_exists(path))
472    eprintf("manifest: (set-file %s): missing and no rule produces it\n", path);
473
474  fp = fopen(path, "r");
475  if (!fp)
476    eprintf("manifest: (set-file %s):", path);
477  if (realpath(path, rp))
478    sl_push(&files_read, rp);
479
480  while (fgets(line, sizeof line, fp)) {
481    char *s = line, *eq, *end;
482    while (isspace((unsigned char)*s))
483      s++;
484    if (*s == '\0' || *s == '#')
485      continue;
486    eq = strchr(s, '=');
487    if (!eq)
488      continue;
489    *eq = '\0';
490    end = eq - 1;
491    while (end > s && isspace((unsigned char)*end))
492      *end-- = '\0';
493    eq++;
494    while (isspace((unsigned char)*eq))
495      eq++;
496    end = eq + strlen(eq);
497    while (end > eq && (isspace((unsigned char)end[-1]) || end[-1] == '\n'))
498      *--end = '\0';
499    kv_set_manifest(s, eq);
500  }
501  fclose(fp);
502}
503
504/* (template NAME (PARAM...) FORM...): the body is cloned so it stays
505 * valid regardless of when or how many times (use ...) instantiates
506 * it */
507static void
508parse_template(struct SNode *form)
509{
510  struct Template *t;
511  struct SNode    *params;
512  const char      *name;
513  int              i;
514
515  name = first_atom(form, "(template NAME (PARAM...) FORM...) needs a name");
516  if (form->nkids < 3 || form->kids[2]->kind != S_LIST)
517    eprintf("manifest: template %s: needs a (PARAM...) list\n", name);
518  params = form->kids[2];
519
520  t = template_new(name);
521  for (i = 0; i < params->nkids; i++) {
522    if (params->kids[i]->kind != S_ATOM)
523      eprintf("manifest: template %s: param names must be atoms\n", name);
524    sl_push(&t->params, params->kids[i]->atom);
525  }
526  t->body = snode_list(form->line);
527  for (i = 3; i < form->nkids; i++)
528    snode_push(t->body, snode_clone(form->kids[i]));
529}
530
531/* clone the template body, substitute each $(PARAM), then dispatch
532 * every substituted form as a normal top-level form */
533static void
534parse_use(struct SNode *form)
535{
536  struct Template *t;
537  struct SNode    *clone;
538  const char      *name;
539  char           **vals;
540  int              i, nargs;
541
542  name = first_atom(form, "(use NAME arg...) needs a name");
543  t    = template_find(name);
544  if (!t)
545    eprintf("manifest: (use %s ...): no such template\n", name);
546
547  nargs = form->nkids - 2;
548  if (nargs != t->params.n)
549    eprintf("manifest: (use %s ...): takes %d argument(s), got %d\n", name, t->params.n, nargs);
550
551  vals = emalloc((size_t)(nargs ? nargs : 1) * sizeof *vals);
552  for (i = 0; i < nargs; i++) {
553    if (form->kids[i + 2]->kind != S_ATOM)
554      eprintf("manifest: (use %s ...): arguments must be atoms\n", name);
555    vals[i] = form->kids[i + 2]->atom;
556  }
557
558  clone = snode_clone(t->body);
559  snode_subst(clone, &t->params, vals);
560  for (i = 0; i < clone->nkids; i++)
561    dispatch_top_form(clone->kids[i]);
562  snode_free(clone);
563  free(vals);
564}
565
566void
567dispatch_top_form(struct SNode *form)
568{
569  const char *h = s_head(form);
570  if (!h)
571    eprintf("manifest: top-level form has no head\n");
572  if (strcmp(h, "set") == 0)
573    parse_set(form);
574  else if (strcmp(h, "list") == 0)
575    parse_list_or_append(form, 0);
576  else if (strcmp(h, "append") == 0)
577    parse_list_or_append(form, 1);
578  else if (strcmp(h, "map") == 0)
579    parse_map(form);
580  else if (strcmp(h, "rule") == 0)
581    parse_rule(form);
582  else if (strcmp(h, "group") == 0)
583    parse_group(form);
584  else if (strcmp(h, "meta") == 0)
585    parse_meta(form);
586  else if (strcmp(h, "import") == 0)
587    parse_import(form);
588  else if (strcmp(h, "set-file") == 0)
589    parse_set_file(form);
590  else if (strcmp(h, "template") == 0)
591    parse_template(form);
592  else if (strcmp(h, "use") == 0)
593    parse_use(form);
594  else
595    eprintf("manifest: unknown top-level form '%s'\n", h);
596}
597
598void
599load_manifest(struct SNode *root)
600{
601  int i;
602  for (i = 0; i < root->nkids; i++)
603    dispatch_top_form(root->kids[i]);
604}
605
606void
607load_file(const char *path)
608{
609  FILE         *fp;
610  char         *buf;
611  size_t        cap, len;
612  char          rp[PATH_MAX];
613  struct SLex   lx;
614  struct SNode *root;
615
616  if (realpath(path, rp)) {
617    if (sl_has(&files_read, rp))
618      return;
619    sl_push(&files_read, rp);
620  }
621
622  fp = fopen(path, "r");
623  if (!fp)
624    eprintf("cannot open manifest %s:", path);
625
626  cap = 65536;
627  len = 0;
628  buf = emalloc(cap);
629  for (;;) {
630    size_t n;
631    if (len + 65536 > cap) {
632      cap *= 2;
633      buf = erealloc(buf, cap);
634    }
635    n = fread(buf + len, 1, cap - len - 1, fp);
636    len += n;
637    if (n == 0)
638      break;
639  }
640  buf[len] = '\0';
641  fclose(fp);
642
643  lx.src  = buf;
644  lx.pos  = 0;
645  lx.line = 1;
646  root    = snode_parse(&lx);
647  free(buf);
648  load_manifest(root);
649  snode_free(root);
650}