From 0c7a1b590fa164ef52a464c11452f1413ea860a9 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 26 Sep 2019 20:41:55 -0400
Subject: [PATCH 60/63] FIXME: WIP on adding sm DSL

---
 gcc/analyzer/Make-plugin.in    |    1 +
 gcc/analyzer/checker-script.cc | 1680 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1681 insertions(+)
 create mode 100644 gcc/analyzer/checker-script.cc

diff --git a/gcc/analyzer/Make-plugin.in b/gcc/analyzer/Make-plugin.in
index 7ee6ac0..26bf930 100644
--- a/gcc/analyzer/Make-plugin.in
+++ b/gcc/analyzer/Make-plugin.in
@@ -59,6 +59,7 @@ analyzer_OBJS = \
 	analyzer/analyzer-pass.o \
 	analyzer/analyzer-plugin.o \
 	analyzer/analyzer-selftests.o \
+	analyzer/checker-script.o \
 	analyzer/constraint-manager.o \
 	analyzer/digraph.o \
 	analyzer/graphviz.o \
diff --git a/gcc/analyzer/checker-script.cc b/gcc/analyzer/checker-script.cc
new file mode 100644
index 0000000..7876fac
--- /dev/null
+++ b/gcc/analyzer/checker-script.cc
@@ -0,0 +1,1680 @@
+/*
+  FIXME: compare with:
+  ~/coding/gcc-python/gcc-python/sm-more-checkers/sm/parser.py
+  ~/coding/gcc-python/gcc-python/sm-more-checkers/docs/sm.rst
+  Changes:
+    "=>" became "->"
+    "$id$" became "@id".  */
+
+/* FIXME.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "toplev.h"
+#include "cpplib.h"
+#include "diagnostic-core.h"
+#include "pretty-print.h"
+#include "selftest.h"
+
+#if 0
+namespace checker
+{
+class state_machine;
+
+class state_t;
+class state_name_t;
+
+class state_t
+{
+public:
+  state_t (int idx) : m_idx (idx) {}
+
+  const char *get_name (const state_machine *sm) const;
+
+  void print (pretty_printer *pp, const state_machine *sm) const;
+
+  //private:
+  int m_idx;
+};
+
+/* Either a list of states, or a wildcard.
+   States might be seen after a wildcard, so keep the "wildcard-ness"
+   around.  */
+
+class state_list_t
+{
+public:
+  state_list_t () : m_states (), m_wildcard (false) {}
+
+  void print (pretty_printer *pp, const state_machine *sm) const;
+
+  void add (state_name_t state_name);
+
+  /* Iterating.  */
+  unsigned get_length (const state_machine *sm) const;
+  state_t get_state_at (int idx) const;
+
+  auto_vec<state_t> m_states;
+  bool m_wildcard;
+};
+
+class type
+{
+public:
+  type (const char *fixme) {}
+
+  void print (pretty_printer *pp) const;
+};
+
+class decl
+{
+public:
+  decl (const char *name, type *t, bool stateful);
+
+  void print (pretty_printer *pp) const;
+
+private:
+  char *m_name;
+  type *m_type;
+  bool m_stateful;
+};
+
+/* state_name:
+     id.id
+     | id
+     | id ".*"
+     | "*"  */
+
+class state_name_t
+{
+public:
+  static state_name_t from_state (state_t state)
+  {
+    return state_name_t (state);
+  }
+  static state_name_t from_wildcard ()
+  {
+    return state_name_t (state_t (WILDCARD));
+  }
+
+  void print (pretty_printer *pp, const state_machine *sm) const;
+
+  bool wildcard_p () const
+  {
+    return m_state_or_wildcard.m_idx == WILDCARD;
+  }
+  state_t get_state () const
+  {
+    gcc_assert (!wildcard_p ());
+    return m_state_or_wildcard;
+  }
+
+private:
+  static const int WILDCARD = -1;
+
+  state_name_t (state_t state_or_wildcard)
+  : m_state_or_wildcard (state_or_wildcard)
+  {}
+
+  state_t m_state_or_wildcard;
+};
+
+class pattern
+{
+public:
+  virtual void print (pretty_printer *pp) const = 0;
+};
+
+class cpattern : public pattern
+{
+public:
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  virtual void print_body (pretty_printer *pp) const;
+};
+
+class result_of_fncall : public cpattern
+{
+public:
+  result_of_fncall (const char *lhs, const char *fnname);
+
+private:
+  void print_body (pretty_printer *pp) const FINAL OVERRIDE;
+
+  char *m_lhs;
+  char *m_fnname;
+};
+
+class dereference : public cpattern
+{
+public:
+  dereference (const char *rhs);
+
+private:
+  void print_body (pretty_printer *pp) const FINAL OVERRIDE;
+
+  char *m_rhs;
+};
+
+class at_pattern : public pattern
+{
+public:
+  at_pattern (const char *id);
+  ~at_pattern ();
+
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  char *m_id;
+};
+
+class use_named_pattern : public pattern
+{
+public:
+  use_named_pattern (const char *id);
+  ~use_named_pattern ();
+
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+private:
+  char *m_id;
+};
+
+class pattern_list
+{
+public:
+  void print (pretty_printer *pp) const;
+
+  void add_pattern (pattern *p);
+
+private:
+  auto_vec <pattern *> m_patterns;
+};
+
+class named_pattern_list
+{
+public:
+  named_pattern_list (const char *name, pattern_list *pl);
+
+  void print (pretty_printer *pp) const;
+
+private:
+  char *m_name;
+  pattern_list *m_pattern_list;
+};
+
+class state_outcome;
+class boolean_outcome;
+
+class outcome
+{
+public:
+  virtual void print (pretty_printer *pp) const = 0;
+
+  state_machine *get_sm () const { return m_sm; }
+
+  virtual state_outcome *dyncast_state_outcome () { return NULL; }
+  virtual boolean_outcome *dyncast_boolean_outcome () { return NULL; }
+
+protected:
+  outcome (state_machine *sm) : m_sm (sm) {}
+
+private:
+  state_machine *m_sm;
+};
+
+class script_outcome : public outcome
+{
+public:
+  script_outcome (state_machine *sm) : outcome (sm) {}
+
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+};
+
+class state_outcome : public outcome
+{
+public:
+  state_outcome (state_machine *sm, state_t state);
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  state_outcome *dyncast_state_outcome () FINAL OVERRIDE { return this; }
+
+  //private:
+  state_t m_state;
+};
+
+class boolean_outcome : public outcome
+{
+public:
+  boolean_outcome (state_machine *sm, outcome *wrapped, bool guard);
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  boolean_outcome *dyncast_boolean_outcome () FINAL OVERRIDE { return this; }
+
+private:
+  outcome *m_wrapped;
+  bool m_guard;
+};
+
+class pattern_rule
+{
+public:
+  pattern_rule (pattern_list *pl) : m_pattern_list (pl) {}
+
+  void add_outcome (outcome *o);
+
+  void print (pretty_printer *pp) const;
+
+  //private:
+  pattern_list *m_pattern_list;
+  auto_vec <outcome *> m_outcomes;
+};
+
+class state_clause
+{
+public:
+  void print (pretty_printer *pp, const state_machine *sm) const;
+
+  void dump_dot_to_pp (pretty_printer *pp, const state_machine *sm) const;
+
+  void add_pattern_rule (pattern_rule *pr);
+
+  //private:
+  state_list_t *m_states;
+  auto_vec <pattern_rule *> m_prs;
+};
+
+class state_machine
+{
+public:
+  /* Parser methods.  */
+  state_machine (const char *id);
+  ~state_machine ();
+  void add_decl (const char *name, type *t, bool stateful);
+  void add_named_pattern_list (const char *name, pattern_list *p);
+  void add_state_clause (state_clause *sc);
+
+  void print (pretty_printer *pp) const;
+  void dump_dot_to_pp (pretty_printer *pp) const;
+
+  const char *get_state_name (state_t state) const;
+  state_t get_state_by_name (const char *name);
+  int get_num_states () const { return m_state_names.length (); }
+
+private:
+  char *m_id;
+  auto_vec <decl *> m_decls;
+  auto_vec <named_pattern_list *> m_named_pattern_lists;
+  auto_vec <state_clause *> m_scs;
+
+  auto_vec<char *> m_state_names;
+};
+
+class script
+{
+public:
+  script (const char *path);
+  ~script ();
+
+  void add_state_machine (state_machine *sm);
+
+  void dump (FILE *f) const;
+  void print (pretty_printer *pp) const;
+
+  void dump_to_dot (const char *filename) const;
+  void dump_dot_to_pp (pretty_printer *pp) const;
+
+private:
+  char *m_path;
+  auto_vec <state_machine *> m_sms;
+};
+
+} // namespace checker
+
+using namespace checker;
+
+/* class checker::script.  */
+
+// FIXME
+
+script::script (const char *path)
+: m_path (xstrdup (path))
+{
+}
+
+script::~script ()
+{
+  free (m_path);
+
+  unsigned i;
+  state_machine *sm;
+  FOR_EACH_VEC_ELT (m_sms, i, sm)
+    delete sm;
+}
+
+void
+script::add_state_machine (state_machine *sm)
+{
+  m_sms.safe_push (sm);
+}
+
+void
+script::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+void
+script::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "/* path: %s */\n", m_path);
+
+  unsigned i;
+  state_machine *sm;
+  FOR_EACH_VEC_ELT (m_sms, i, sm)
+    sm->print (pp);
+}
+
+void
+script::dump_to_dot (const char *filename) const
+{
+  FILE *fp = fopen (filename, "w");
+  pretty_printer pp;
+  pp.buffer->stream = fp;
+  dump_dot_to_pp (&pp);
+  pp_flush (&pp);
+  fclose (fp);
+}
+
+void
+script::dump_dot_to_pp (pretty_printer *pp) const
+{
+  pp_string (pp, "digraph \"");
+  pp_write_text_to_stream (pp);
+  pp_string (pp, m_path);
+  pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
+  pp_string (pp, "\" {\n");
+
+  unsigned i;
+  state_machine *sm;
+  FOR_EACH_VEC_ELT (m_sms, i, sm)
+    sm->dump_dot_to_pp (pp);
+
+  /* Terminate "digraph" */
+  pp_string (pp, "}");
+}
+
+/* FIXME.  */
+
+const char *
+state_t::get_name (const state_machine *sm) const
+{
+  return sm->get_state_name (m_idx);
+}
+
+/* FIXME.  */
+
+void
+state_t::print (pretty_printer *pp, const state_machine *sm) const
+{
+  pp_string (pp, get_name (sm));
+}
+
+/* FIXME.  */
+
+void
+state_list_t::print (pretty_printer *pp, const state_machine *sm) const
+{
+  if (m_wildcard)
+    {
+      pp_string (pp, "FIXME.*");
+    }
+  else
+    {
+      unsigned i;
+      state_t *s;
+      FOR_EACH_VEC_ELT (m_states, i, s)
+	{
+	  if (i > 0)
+	    pp_string (pp, ", ");
+          pp_string (pp, "FIXME.");
+	  s->print (pp, sm);
+	}
+    }
+}
+
+/* FIXME.  */
+
+void
+state_list_t::add (state_name_t state_name)
+{
+  if (state_name.wildcard_p ())
+    m_wildcard = true;
+  else
+    m_states.safe_push (state_name.get_state ());
+}
+
+/* FIXME.  */
+
+unsigned
+state_list_t::get_length (const state_machine *sm) const
+{
+  if (m_wildcard)
+    return sm->get_num_states ();
+  else
+    return m_states.length ();
+}
+
+/* FIXME.  */
+
+state_t
+state_list_t::get_state_at (int idx) const
+{
+  if (m_wildcard)
+    return state_t (idx);
+  else
+    return m_states[idx];
+}
+
+/* class checker::type.  */
+
+void
+type::print (pretty_printer *pp) const
+{
+  // FIXME
+}
+
+/* class checker::decl.  */
+
+decl::decl (const char *name, type *t, bool stateful)
+: m_name (xstrdup (name)), m_type (t), m_stateful (stateful)
+{
+}
+
+void
+decl::print (pretty_printer *pp) const
+{
+  pp_string (pp, "  ");
+  if (m_stateful)
+    pp_printf (pp, "stateful ");
+  pp_printf (pp, "decl ");
+  m_type->print (pp);
+  pp_printf (pp, "%s;\n", m_name);
+}
+
+/* class checker::pattern.  */
+
+/* class checker::cpattern.  */
+
+void
+cpattern::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "{{ ");
+  print_body (pp);
+  pp_printf (pp, " }}");
+}
+
+void
+cpattern::print_body (pretty_printer *pp) const
+{
+  pp_printf (pp, "FIXME");
+}
+
+/* class checker::result_of_fncall.  */
+
+result_of_fncall::result_of_fncall (const char *lhs, const char *fnname)
+: m_lhs (xstrdup (lhs)), m_fnname (xstrdup (fnname))
+{
+}
+
+void
+result_of_fncall::print_body (pretty_printer *pp) const
+{
+  pp_printf (pp, "%s = %s()", m_lhs, m_fnname);
+}
+
+/* class checker::dereference.  */
+
+dereference::dereference (const char *rhs)
+: m_rhs (xstrdup (rhs))
+{
+}
+
+void
+dereference::print_body (pretty_printer *pp) const
+{
+  pp_printf (pp, "*%s", m_rhs);
+}
+
+/* class checker::at_pattern.  */
+
+at_pattern::at_pattern (const char *id)
+: m_id (xstrdup (id))
+{
+}
+
+at_pattern::~at_pattern ()
+{
+  free (m_id);
+}
+
+void
+at_pattern::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "@%s", m_id);
+}
+
+/* class checker::use_named_pattern.  */
+use_named_pattern::use_named_pattern (const char *id)
+: m_id (xstrdup (id))
+{
+}
+
+use_named_pattern::~use_named_pattern ()
+{
+  free (m_id);
+}
+
+void
+use_named_pattern::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "%s", m_id);
+}
+
+/* class checker::pattern_list.  */
+void
+pattern_list::print (pretty_printer *pp) const
+{
+  unsigned i;
+  pattern *p;
+  FOR_EACH_VEC_ELT (m_patterns, i, p)
+    {
+      if (i > 0)
+	pp_string (pp, "| ");
+      p->print (pp);
+    }
+}
+
+void
+pattern_list::add_pattern (pattern *p)
+{
+  m_patterns.safe_push (p);
+}
+
+/* class checker::named_pattern_list.  */
+
+named_pattern_list::named_pattern_list (const char *name, pattern_list *pl)
+: m_name (xstrdup (name)),
+  m_pattern_list (pl)
+{
+}
+
+void
+named_pattern_list::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "  pat %s ", m_name);
+  m_pattern_list->print (pp);
+  pp_string (pp, ";\n");
+}
+
+/* class checker::outcome.  */
+
+/* class checker::script_outcome.  */
+
+void
+script_outcome::print (pretty_printer *pp) const
+{
+  pp_string (pp, "{{ FIXME }}");
+}
+
+/* class checker::state_outcome.  */
+
+state_outcome::state_outcome (state_machine *sm, state_t state)
+: outcome (sm), m_state (state)
+{
+}
+
+void
+state_outcome::print (pretty_printer *pp) const
+{
+  m_state.print (pp, get_sm ());
+}
+
+/* class checker::boolean_outcome.  */
+
+boolean_outcome::boolean_outcome (state_machine *sm, outcome *wrapped, bool guard)
+: outcome (sm), m_wrapped (wrapped), m_guard (guard)
+{
+}
+
+void
+boolean_outcome::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "%s=", m_guard ? "true" : "false");
+  m_wrapped->print (pp);
+}
+
+/* class checker::pattern_rule.  */
+
+void
+pattern_rule::add_outcome (outcome *o)
+{
+  m_outcomes.safe_push (o);
+}
+
+void
+pattern_rule::print (pretty_printer *pp) const
+{
+  m_pattern_list->print (pp);
+  pp_printf (pp, " -> ");
+
+  unsigned i;
+  outcome *o;
+  FOR_EACH_VEC_ELT (m_outcomes, i, o)
+    {
+      if (i > 0)
+	pp_string (pp, ", ");
+      o->print (pp);
+    }
+}
+
+/* class checker::state_clause.  */
+
+/* statelist ":" patternrulelist ";".  */
+
+void
+state_clause::print (pretty_printer *pp, const state_machine *sm) const
+{
+  pp_string (pp, "  ");
+  m_states->print (pp, sm);
+  pp_string (pp, ":\n");
+
+  unsigned i;
+  pattern_rule *pr;
+  FOR_EACH_VEC_ELT (m_prs, i, pr)
+    {
+      if (i > 0)
+	pp_string (pp, "    | ");
+      else
+	pp_string (pp, "      ");
+      pr->print (pp);
+      pp_string (pp, "\n");
+    }
+
+  pp_string (pp, "    ;\n");
+}
+
+/* FIXME.  */
+void
+state_clause::dump_dot_to_pp (pretty_printer *pp, const state_machine *sm) const
+{
+  /* Add edges.  */
+  for (unsigned i = 0; i < m_states->get_length (sm); i++)
+    {
+      state_t state = m_states->get_state_at (i);
+
+      unsigned j;
+      pattern_rule *pr;
+      FOR_EACH_VEC_ELT (m_prs, j, pr)
+	{
+	  unsigned k;
+	  outcome *o;
+	  FOR_EACH_VEC_ELT (pr->m_outcomes, k, o)
+	    {
+	      if (state_outcome *so = o->dyncast_state_outcome ())
+		{
+		  // FIXME: annotate the edge
+		  // maybe put the patterns as child nodes within the
+		  // original state?
+		  pp_printf (pp, "state_%s -> state_%s;\n",
+			     state.get_name (sm),
+			     so->m_state.get_name (sm));
+		}
+	    }
+	}
+    }
+}
+
+void
+state_clause::add_pattern_rule (pattern_rule *pr)
+{
+  m_prs.safe_push (pr);
+}
+
+/* class checker::state_machine.  */
+
+// FIXME
+
+state_machine::state_machine (const char *id)
+: m_id (xstrdup (id))
+{
+}
+
+// FIXME
+
+state_machine::~state_machine ()
+{
+  free (m_id);
+}
+
+void
+state_machine::add_decl (const char *name, type *t, bool stateful)
+{
+  decl *d = new decl (name, t, stateful);
+  m_decls.safe_push (d);
+}
+
+void
+state_machine::add_named_pattern_list (const char *name, pattern_list *pl)
+{
+  m_named_pattern_lists.safe_push (new named_pattern_list (name, pl));
+}
+
+void
+state_machine::add_state_clause (state_clause *sc)
+{
+  m_scs.safe_push (sc);
+}
+
+void
+state_machine::print (pretty_printer *pp) const
+{
+  pp_printf (pp, "sm %s {\n", m_id);
+
+  unsigned i;
+
+  decl *d;
+  FOR_EACH_VEC_ELT (m_decls, i, d)
+    d->print (pp);
+
+  named_pattern_list *npl;
+  FOR_EACH_VEC_ELT (m_named_pattern_lists, i, npl)
+    npl->print (pp);
+
+  state_clause *sc;
+  FOR_EACH_VEC_ELT (m_scs, i, sc)
+    sc->print (pp, this);
+
+  pp_string (pp, "}\n");
+}
+
+void
+state_machine::dump_dot_to_pp (pretty_printer *pp) const
+{
+  pp_printf (pp, "subgraph \"state_machine_%s\" {", m_id);
+  pp_printf (pp,
+	     ("style=\"dashed\";"
+	      " color=\"black\";"
+	      " label=\"%s\";\n"),
+	     m_id);
+
+  /* Add nodes for states.  */
+  {
+    unsigned i;
+    char *name;
+    FOR_EACH_VEC_ELT (m_state_names, i, name)
+      {
+	pp_printf (pp, "state_%s", name);
+	pp_printf (pp, " [shape=%s,style=filled,fillcolor=%s,label=\"",
+		   "record", "lightgrey");
+	pp_write_text_to_stream (pp);
+	pp_string (pp, name);
+	pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
+	pp_string (pp, "\"];\n\n");
+      }
+  }
+
+  /* Add edges for state clauses.  */
+  {
+    unsigned i;
+    state_clause *sc;
+    FOR_EACH_VEC_ELT (m_scs, i, sc)
+      sc->dump_dot_to_pp (pp, this);
+  }
+
+  /* Terminate per-bb "subgraph" */
+  pp_string (pp, "}\n");
+}
+
+/* FIXME.  */
+
+const char *
+state_machine::get_state_name (state_t state) const
+{
+  gcc_assert (state.m_idx >= 0);
+  gcc_assert ((unsigned)state.m_idx < m_state_names.length ());
+  return m_state_names[state.m_idx];
+}
+
+/* FIXME.  */
+
+state_t
+state_machine::get_state_by_name (const char *name)
+{
+  unsigned i;
+  char *existing_name;
+  FOR_EACH_VEC_ELT (m_state_names, i, existing_name)
+    {
+      if (0 == strcmp (name, existing_name))
+	return state_t (i);
+    }
+
+  /* Not found: add a new state.  */
+  m_state_names.safe_push (xstrdup (name));
+  return m_state_names.length () - 1;
+}
+
+static void
+#if GCC_VERSION >= 4001
+  ATTRIBUTE_GCC_DIAG (2,3)
+#endif
+fatal_at (const cpp_token *tk, const char *msg, ...)
+{
+  va_list ap;
+  va_start (ap, msg);
+  emit_diagnostic_valist (DK_FATAL, tk->src_loc, 0, msg, &ap);
+  va_end (ap);
+}
+
+namespace checker
+{
+
+class parser
+{
+public:
+  parser ();
+  ~parser ();
+
+  script *parse (const char *path);
+
+private:
+  script *parse_toplevel (const char *path);
+  state_machine *parse_sm (script *s);
+  void parse_sm_clauses (state_machine *sm);
+  void parse_sm_clause (state_machine *sm);
+  void parse_decl (state_machine *sm, bool stateful);
+  void parse_pat (state_machine *sm);
+  void parse_statelist_smclause (state_machine *sm);
+  state_list_t *parse_statelist (state_machine *sm);
+  state_name_t parse_statename (state_machine *sm);
+  void parse_patternrulelist (state_machine *sm, state_clause *sc);
+  pattern_rule *parse_patternrule (state_machine *sm);
+  pattern_list *parse_pattern_list (state_machine *sm);
+  pattern *parse_pattern (state_machine *sm);
+  pattern *parse_cpattern (state_machine *sm);
+  void parse_outcomes (state_machine *sm, pattern_rule *pr);
+  outcome *parse_outcome (state_machine *sm);
+  outcome *parse_boolean_outcome (state_machine *sm, bool guard);
+
+  const cpp_token *next ();
+  const cpp_token *peek (unsigned = 1);
+  const cpp_token *peek_ident (const char * = NULL, unsigned = 1);
+  const cpp_token *expect (enum cpp_ttype);
+  const char *get_ident (location_t *outloc = NULL);
+  const cpp_token *eat_ident (const char *);
+
+  cpp_reader *r;
+};
+
+} // namespace checker
+
+/* class checker::parser.  */
+
+/* FIXME.  */
+
+parser::parser ()
+{
+  r = cpp_create_reader (CLK_GNUCXX, /*c_dialect_cxx () ? CLK_GNUCXX: CLK_GNUC89 */
+			 ident_hash, line_table);
+
+  cpp_options *cpp_opts = cpp_get_options (r);
+  cpp_opts->warn_dollars = 0;
+
+#if 0
+  // FIXME: wire up to c-common and reuse c_cpp_diagnostic
+  cpp_callbacks *cb = cpp_get_callbacks (r);
+  cb->diagnostic = c_cpp_diagnostic;
+#endif
+}
+
+/* FIXME.  */
+
+parser::~parser ()
+{
+  // TODO: m_lexer
+}
+
+/* FIXME.  */
+
+script *
+parser::parse (const char *path)
+{
+  /* FIXME: should we reuse c-lex.c? */
+
+  if (!cpp_read_main_file (r, path))
+    return NULL;
+
+  return parse_toplevel (path);
+}
+
+#if 0
+/* FIXME: maybe build a tree of these during parsing, then optimize it down
+   to something else.  This way we can emit good error messages (as we have
+   source locations for everything.  */
+
+enum kind {
+  NODE_SCRIPT,
+  NODE_SM
+};
+
+/* FIXME.  */
+
+class ast_node
+{
+public:
+  ast_node (enum kind kind, location_t loc)
+  : m_children (), m_kind (kind), m_loc (loc) {}
+
+  void add_child (ast_node *child) { m_children.safe_push (child); }
+
+  auto_vec <ast_node *> m_children;
+  enum kind m_kind;
+  location_t m_loc;
+};
+#endif
+
+/* One or more "sm":
+     checker:
+       sm
+       | sm checker  */
+
+script *
+parser::parse_toplevel (const char *path)
+{
+  script *s = new script (path);
+
+#if 0
+  ast_node *node_s = new ast_node (NODE_SCRIPT, UNKNOWN_LOCATION); // FIXME
+#endif
+
+  const cpp_token *token = next ();
+  while (token->type != CPP_EOF)
+    {
+      _cpp_backup_tokens (r, 1);
+      state_machine *sm = parse_sm (s);
+      if (sm)
+	s->add_state_machine (sm);
+      token = next ();
+    }
+  /* TODO: warn about unreachable states.  */
+
+#if 0
+  delete node_s;
+#endif
+
+  return s;
+}
+
+/* Parse an "sm".
+
+   sm:
+     "sm" id "{" smclauses "}"  */
+
+state_machine *
+parser::parse_sm (script *s)
+{
+  if (!peek_ident ("sm"))
+    {
+      fatal_error (peek ()->src_loc, "expected %qs", "sm");
+      return NULL;
+    }
+
+  eat_ident ("sm");
+  const char *id = get_ident ();
+  expect (CPP_OPEN_BRACE);
+
+  state_machine *sm = new state_machine (id);
+
+  parse_sm_clauses (sm);
+
+  expect (CPP_CLOSE_BRACE);
+
+  return sm;
+}
+
+/* Parse smclauses: one or more smclause.
+
+   smclauses:
+     smclause
+     | smclauses smclause  */
+
+void
+parser::parse_sm_clauses (state_machine *sm)
+{
+  const cpp_token *token = peek ();
+  while (token->type != CPP_CLOSE_BRACE)
+    {
+      parse_sm_clause (sm);
+      token = peek ();
+    }
+}
+
+/* Parse smclause.
+
+   smclause:
+     "stateful"? "decl" declkind id ";"
+     | "pat" id pattern ";"
+     | statelist ":" patternrulelist ";"  */
+
+void
+parser::parse_sm_clause (state_machine *sm)
+{
+  const cpp_token *id = peek_ident ();
+  if (!id)
+    fatal_at (peek (),
+	      "expected %<stateful%>, %<decl%>, %<pat%>, or identifier");
+  //inform (input_location, "got id: %qs", id);
+
+  /* Handle decls.  */
+  if (peek_ident ("stateful"))
+    {
+      eat_ident ("stateful");
+      parse_decl (sm, true);
+      return;
+    }
+  if (peek_ident ("decl"))
+    {
+      parse_decl (sm, false);
+      return;
+    }
+  if (peek_ident ("pat"))
+    {
+      parse_pat (sm);
+      return;
+    }
+
+  parse_statelist_smclause (sm);
+}
+
+// FIXME
+/* Any "stateful" has already been consumed.  */
+
+void
+parser::parse_decl (state_machine *sm, bool stateful)
+{
+  eat_ident ("decl");
+
+  // TODO: parse declkind;
+  const char *id_type = get_ident ();
+  type *t = new type (id_type);
+
+  const char *id = get_ident ();
+
+  expect (CPP_SEMICOLON);
+
+  sm->add_decl (id, t, stateful);
+}
+
+void
+parser::parse_pat (state_machine *sm)
+{
+  eat_ident ("pat");
+
+  const char *id = get_ident ();
+
+  pattern_list *pl = parse_pattern_list (sm);
+
+  expect (CPP_SEMICOLON);
+
+  sm->add_named_pattern_list (id, pl);
+}
+
+// FIXME
+/*
+    smclause:
+      statelist ":" patternrulelist ";"  */
+
+void
+parser::parse_statelist_smclause (state_machine *sm)
+{
+  state_clause *sc = new state_clause ();
+
+  sc->m_states = parse_statelist (sm/*, sc*/);
+  expect (CPP_COLON);
+  parse_patternrulelist (sm, sc);
+  expect (CPP_SEMICOLON);
+
+  sm->add_state_clause (sc);
+}
+
+/* Parse one or more comma-separated statename.
+
+   statelist:
+     statename
+     | statename "," statelist  */
+
+state_list_t *
+parser::parse_statelist (state_machine *sm)
+{
+  state_list_t *result = new state_list_t ();
+  while (1)
+    {
+      state_name_t sn = parse_statename (sm);
+      result->add (sn);
+      if (peek ()->type != CPP_COMMA)
+	break;
+      expect (CPP_COMMA);
+    }
+  return result;
+}
+
+/* FIXME:
+   statename:
+     id.id
+     | id
+     | id ".*"
+     | "*"  */
+
+state_name_t
+parser::parse_statename (state_machine *sm)
+{
+  location_t loc;
+  const char *id = get_ident (&loc);
+  inform (loc, "got id: %qs", id);
+
+  // TODO: look up the decl
+
+  const cpp_token *tok = peek ();
+  if (tok->type == CPP_DOT_STAR)
+    {
+      location_t loc2 = tok->src_loc;
+      next ();
+      return state_name_t::from_wildcard ();
+    }
+  if (tok->type == CPP_DOT)
+    {
+      next ();
+      location_t loc2;
+      const char *id2 = get_ident (&loc2);
+      inform (loc2, "got id2: %qs", id2);
+      return state_name_t::from_state (sm->get_state_by_name (id2));
+    }
+
+  // FIXME
+  return state_name_t::from_state (sm->get_state_by_name ("FIXME"));
+}
+
+/* Parse one or more "|"-separated patternrule.
+
+   patternrulelist:
+     patternrule
+     | patternrule "|" patternrulelist  */
+
+void
+parser::parse_patternrulelist (state_machine *sm, state_clause *sc)
+{
+  while (1)
+    {
+      pattern_rule *pr = parse_patternrule (sm);
+      if (pr)
+	sc->add_pattern_rule (pr);
+      if (peek ()->type != CPP_OR)
+	break;
+      expect (CPP_OR);
+    }
+}
+
+/* FIXME.
+   patternrule:
+     pattern-list "->" outcomes  */
+
+pattern_rule *
+parser::parse_patternrule (state_machine *sm)
+{
+  pattern_list *pl = parse_pattern_list (sm);
+  pattern_rule *pr = new pattern_rule (pl);
+  expect (CPP_DEREF);
+  parse_outcomes (sm, pr);
+  return pr;
+}
+
+/* pattern-list:
+      pattern
+      pattern-list "|" pattern  */
+
+pattern_list *
+parser::parse_pattern_list (state_machine *sm)
+{
+  pattern_list *pl = new pattern_list ();
+
+  while (1)
+    {
+      pattern *p = parse_pattern (sm);
+      if (p)
+	pl->add_pattern (p);
+      if (peek ()->type != CPP_OR)
+	break;
+      expect (CPP_OR);
+    }
+
+  return pl;
+}
+
+/* FIXME:
+    pattern:
+      "{" cpattern "}"
+      | id
+      | "@" id  */
+
+pattern *
+parser::parse_pattern (state_machine *sm)
+{
+  const cpp_token *tok = peek ();
+  if (tok->type == CPP_OPEN_BRACE)
+    {
+      expect (CPP_OPEN_BRACE);
+      pattern *p = parse_cpattern (sm);
+      expect (CPP_CLOSE_BRACE);
+      return p;
+    }
+  if (tok->type == CPP_ATSIGN)
+    {
+      expect (CPP_ATSIGN);
+      const char *id = get_ident ();
+      return new at_pattern (id);
+    }
+  const char *id = get_ident ();
+  return new use_named_pattern (id);
+}
+
+/* FIXME.
+    cpattern
+    : assignment-cpattern
+    | addressof-cpattern
+    | return-cpattern
+    | result-of-fncall-cpattern
+    | comparison-cpattern
+
+   assignment-cpattern:
+     id "=" literal
+     | id "=" literal
+     | id "=" id
+   e.g. "q = 0"
+
+   addressof-cpattern:
+     id "=" "&" id
+   e.g. "&var"
+
+   return-cpattern:
+     "return" id
+     | "return" literal
+     | "return"
+   e.g. "return var"
+
+   result-of-fncall-cpattern:
+     id "=" id "(" fncall_args ")"
+   e.g. "ptr = malloc()".
+
+   dereference-cpattern:
+     "*" id
+   e.g. "*ptr"
+
+   comparison-cpattern:
+     id comparison literal
+     id comparison id
+   e.g. "ptr == 0".  */
+
+/*
+def p_fncall_arg(p):
+    '''
+    fncall_arg : ID
+               | LITERAL_STRING
+               | LITERAL_NUMBER
+    '''
+    p[0] = p[1]
+
+def p_nonempty_fncall_args(p):
+    '''
+    nonempty_fncall_args : fncall_arg
+                         | fncall_args COMMA fncall_arg
+    '''
+    if len(p) == 2:
+        p[0] = [p[1]]
+    else:
+        p[0] = p[1] + [p[3]]
+
+def p_fncall_args_from_nonempty(p):
+    '''
+    fncall_args : nonempty_fncall_args
+    '''
+    p[0] = p[1]
+
+def p_fncall_args_from_empty(p):
+    '''
+    fncall_args : empty
+    '''
+    p[0] = []
+
+def p_cpattern_arg_of_fn_call(p):
+    'cpattern : ID LPAREN fncall_args RPAREN'
+    # e.g. "free(ptr)"
+    p[0] = ArgsOfFnCall(fnname=p[1], args=p[3])
+
+def p_cpattern_arraylookup(p):
+    'cpattern : ID LSQUARE ID RSQUARE'
+    # e.g. "arr[x]"
+    p[0] = ArrayLookup(array=p[1], index=p[3])
+
+def p_cpattern_usage(p):
+    'cpattern : ID'
+*/
+
+pattern *
+parser::parse_cpattern (state_machine *sm)
+{
+  const cpp_token *tok1 = peek ();
+  const cpp_token *tok2 = peek (2);
+  if (tok1->type == CPP_NAME && tok2 && tok2->type == CPP_EQ)
+    {
+      const char *lhs = get_ident ();
+      expect (CPP_EQ);
+      /* id "=" ...  */
+      const cpp_token *rhs = peek ();
+      inform (rhs->src_loc, "FIXME");
+      if (rhs->type == CPP_NAME)
+	{
+	  const char *rhs_name = get_ident ();
+	  if (peek ()->type == CPP_OPEN_PAREN)
+	    {
+	      /* result-of-fncall-cpattern.  */
+
+	      /* FIXME: for now, consume until close paren.  */
+	      while (1)
+		{
+		  const cpp_token *tok = peek ();
+		  if (tok->type == CPP_CLOSE_PAREN || tok->type == CPP_EOF)
+		    break;
+		  next ();
+		}
+	      next ();
+	      return new result_of_fncall (lhs, rhs_name);
+	    }
+	  else
+	    inform (rhs->src_loc, "assignment");
+	}
+    }
+  if (tok1->type == CPP_MULT)
+    {
+      /* dereference-cpattern.  */
+      expect (CPP_MULT);
+      const char *rhs = get_ident ();
+      return new dereference (rhs);
+    }
+
+  /* FIXME: for now, consume until close brace.  */
+  while (1)
+    {
+      const cpp_token *tok = peek ();
+      if (tok->type == CPP_CLOSE_BRACE || tok->type == CPP_EOF)
+	break;
+      next ();
+    }
+
+  // FIXME:
+  return new cpattern ();
+}
+
+/* FIXME.
+   outcomes:
+     outcome
+     | outcome "," outcomes */
+
+void
+parser::parse_outcomes (state_machine *sm, pattern_rule *pr)
+{
+  while (1)
+    {
+      outcome *o = parse_outcome (sm);
+      if (o)
+	pr->add_outcome (o);
+      if (peek ()->type != CPP_COMMA)
+	break;
+      expect (CPP_COMMA);
+    }
+}
+
+/* FIXME.
+
+   outcome:
+     statename     e.g. "ptr.unknown"
+     | "true" "=" outcome    e.g. "true=ptr.null"
+     | "false" "=" outcome
+     | PYTHON  FIXME
+       e.g. "{ error('use of possibly-NULL pointer %s', ptr) }"  */
+
+outcome *
+parser::parse_outcome (state_machine *sm)
+{
+  const cpp_token *tok = peek_ident ();
+
+  if (peek_ident ("true"))
+    return parse_boolean_outcome (sm, true);
+  else if (peek_ident ("false"))
+    return parse_boolean_outcome (sm, false);
+  else if (peek ()->type == CPP_OPEN_BRACE)
+    {
+      expect (CPP_OPEN_BRACE);
+      expect (CPP_OPEN_BRACE);
+
+      /* FIXME: for now, consume until close brace.  */
+      while (1)
+	{
+	  const cpp_token *tok = peek ();
+	  if (tok->type == CPP_CLOSE_BRACE || tok->type == CPP_EOF)
+	    break;
+	  next ();
+	}
+
+      expect (CPP_CLOSE_BRACE);
+      expect (CPP_CLOSE_BRACE);
+      return new script_outcome (sm);
+    }
+
+  // FIXME:
+  state_name_t sn = parse_statename (sm);
+
+#if 0
+  if (sn.wildcard_p ())
+    {
+      error ("cannot transition to a wildcard state");
+      return NULL;
+    }
+#endif
+  return new state_outcome (sm, sn.get_state ());
+}
+
+/* Parse a guarded outcome:
+
+   outcome:
+     "true" "=" outcome
+     | "false" "=" outcome  */
+
+outcome *
+parser::parse_boolean_outcome (state_machine *sm, bool guard)
+{
+  next ();
+  expect (CPP_EQ);
+  return new boolean_outcome (sm, parse_outcome (sm), guard);
+}
+
+/* Lexing helpers.  */
+
+/* Read the next non-whitespace token from R.  */
+
+const cpp_token *
+parser::next ()
+{
+  const cpp_token *token;
+  do
+    {
+      token = cpp_get_token (r);
+    }
+  while (token->type == CPP_PADDING);
+  return token;
+}
+
+/* Peek at the next non-whitespace token from R.  */
+
+const cpp_token *
+parser::peek (unsigned num)
+{
+  const cpp_token *token;
+  unsigned i = 0;
+  do
+    {
+      token = cpp_peek_token (r, i++);
+    }
+  while (token->type == CPP_PADDING
+	 || (--num > 0));
+  /* If we peek at EOF this is a fatal error as it leaves the
+     cpp_reader in unusable state.  Assume we really wanted a
+     token and thus this EOF is unexpected.  */
+  if (token->type == CPP_EOF)
+    ; // fatal_at (token, "unexpected end of file");
+  return token;
+}
+
+/* Peek at the next identifier token (or return NULL if the next
+   token is not an identifier or equal to ID if supplied).  */
+
+const cpp_token *
+parser::peek_ident (const char *id, unsigned num)
+{
+  const cpp_token *token = peek (num);
+  if (token->type != CPP_NAME)
+    return 0;
+
+  if (id == 0)
+    return token;
+
+  const char *t = (const char *) CPP_HASHNODE (token->val.node.node)->ident.str;
+  if (strcmp (id, t) == 0)
+    return token;
+
+  return 0;
+}
+
+/* Read the next token from R and assert it is of type TK.  */
+
+const cpp_token *
+parser::expect (enum cpp_ttype tk)
+{
+  const cpp_token *token = next ();
+  if (token->type != tk)
+    fatal_at (token, "expected %s, got %s",
+	      cpp_type2name (tk, 0), cpp_type2name (token->type, 0));
+
+  return token;
+}
+
+/* Read the next token from R and assert it is of type CPP_NAME and
+   return its value.  */
+
+const char *
+parser::get_ident (location_t *outloc)
+{
+  const cpp_token *token = expect (CPP_NAME);
+  if (outloc)
+    *outloc = token->src_loc;
+  return (const char *)CPP_HASHNODE (token->val.node.node)->ident.str;
+}
+
+/* Eat an identifier token with value S from R.  */
+
+const cpp_token *
+parser::eat_ident (const char *s)
+{
+  const cpp_token *token = peek ();
+  const char *t = get_ident ();
+  if (strcmp (s, t) != 0)
+    fatal_at (token, "expected '%s' got '%s'\n", s, t);
+  return token;
+}
+
+static script *
+parse_script (const char *path)
+{
+  parser p;
+  return p.parse (path);
+}
+
+void
+debug (script *s)
+{
+  s->dump (stderr);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests for checker scripts.  */
+
+/* FIXME.  */
+
+static void
+test_parsing_malloc_checker ()
+{
+  // FIXME:
+  script *s = parse_script ("../../src/checkers/malloc_checker.sm");
+  debug (s);
+  s->dump_to_dot ("malloc_checker.dot");
+}
+
+/* FIXME.  */
+
+static void
+test_parsing_taint ()
+{
+  // FIXME:
+  script *s = parse_script ("../../src/checkers/taint.sm");
+  debug (s);
+  s->dump_to_dot ("taint.dot");
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+analyzer_checker_script_cc_tests ()
+{
+  test_parsing_malloc_checker ();
+  test_parsing_taint ();
+}
+
+} // namespace selftest
+
+#endif
+
+#endif /* CHECKING_P */
-- 
1.8.5.3

