Vcsn  2.8
Be Rational
daut.cc
Go to the documentation of this file.
1 #include <fstream>
2 #include <string>
3 
4 #include <boost/algorithm/string/predicate.hpp>
5 #include <boost/algorithm/string/trim.hpp>
6 
7 #include <lib/vcsn/algos/fwd.hh>
8 #include <lib/vcsn/rat/caret.hh>
10 #include <vcsn/dyn/algos.hh>
11 #include <vcsn/dyn/automaton.hh>
12 #include <vcsn/misc/location.hh>
13 #include <vcsn/misc/regex.hh>
14 #include <vcsn/misc/stream.hh>
15 #include <vcsn/misc/symbol.hh>
16 
17 namespace vcsn
18 {
19  namespace dyn
20  {
21  namespace
22  {
23  using string_t = symbol;
24 
26  std::string read_context(std::string& line)
27  {
28  // A context definition possibly followed by comments.
29  static auto re = std::regex("^[ \t]*(?:vcsn_)?(?:context|ctx)[ \t]*="
30  "[ \t]*(\"?)(.*?)\\1[ \t]*(?://.*)?$");
31  // To handle escaped characters.
32  static auto sub = std::regex("\\\\\\\\");
33  std::smatch m;
34  if (std::regex_match(line, m, re))
35  {
36  std::string res = m[2];
37  if (m[1] == "\"")
38  res = std::regex_replace(res, sub, "\\");
39  return res;
40  }
41  else
42  return "";
43  }
44 
46  string_t read_quotes(std::istream& is)
47  {
48  auto res = std::string{};
49  int c;
50  while ((c = is.peek()) != EOF && c != '"')
51  res += get_char(is);
52  if (c != '"')
53  raise("invalid daut file: missing '\"' after '", res, "'");
54  is.ignore(); // Eat the final '"'
55  return string_t{res};
56  }
57 
60  bool is_comment(std::istream& is, char c)
61  {
62  if (c == '/' && is.peek() == '/')
63  {
64  is.unget(); // Put back the first '/'
65  return true;
66  }
67  else
68  return c == '#';
69  }
70 
72  string_t read_state(std::istream& is)
73  {
74  auto res = std::string{};
75  int c;
76  skip_space(is);
77  while ((c = is.get()) != EOF)
78  {
79  if (c == '\\')
80  {
81  if ((c = is.get()) != EOF)
82  res += c;
83  continue;
84  }
85  else if (c == '"')
86  return read_quotes(is);
87  // `->` is a keyword: "a->b" stands for "a -> b".
88  else if (c == '-' && is.peek() == '>')
89  {
90  if (res.empty())
91  {
92  is.ignore();
93  res = "->";
94  }
95  else
96  is.unget();
97  break;
98  }
99  else if (std::isalnum(c)
100  || c == '_' || c == '-' || c == '.' || c == '$')
101  res += c;
102  else
103  {
104  is.unget();
105  break;
106  }
107  }
108  boost::algorithm::trim_right(res);
109  return string_t{res};
110  }
111 
113  string_t read_entry(std::istream& is)
114  {
115  auto res = std::string{};
116  int c;
117  skip_space(is);
118  while ((c = is.get()) != EOF)
119  {
120  if (isspace(c) && res.empty())
121  continue;
122  if (is_comment(is, c))
123  break;
124  res += c;
125  }
126  return string_t{res};
127  }
128 
129  const static string_t pre = string_t{"$pre"};
130  const static string_t post = string_t{"$post"};
131 
132  vcsn::automaton_editor* make_editor(std::string ctx)
133  {
134  auto c = vcsn::dyn::make_context(ctx.empty() ? "lal_char, b" : ctx);
136  res->set_separator(',');
137  res->add_pre(pre);
138  res->add_post(post);
139  return res;
140  }
141  }
142 
143  automaton
144  read_daut(std::istream& is, const location& l)
145  {
146  // FIXME: Making `loc` const was not a good idea.
147  location loc = l;
148  auto first = true;
149  auto edit = std::shared_ptr<vcsn::automaton_editor>{};
150 
151  // Line: Source [->] Dest [Entry]
152  auto line = std::string{};
153  while (is.good())
154  {
155  loc.step();
156  loc.lines(1);
157  std::getline(is, line, '\n');
158  auto locline = location{loc.begin, loc.begin + line.size()};
159  // Trim here to handle line full of blanks.
160  boost::algorithm::trim_right(line);
161  if (line.empty() || boost::starts_with(line, "//"))
162  continue;
163 
164  if (first)
165  {
166  auto ctx = read_context(line);
167  try
168  {
169  edit.reset(make_editor(ctx));
170  }
171  catch (const std::runtime_error& e)
172  {
173  raise(locline, ": ", e, vcsn::detail::caret(is, locline));
174  }
175  first = false;
176  if (!ctx.empty())
177  continue;
178  }
179 
180  std::istringstream ss{line};
181  auto s = read_state(ss);
182  auto d = read_state(ss);
183  if (d.get().empty()) // Declaring a state with no transitions
184  {
185  edit->add_state(s);
186  continue;
187  }
188  if (d == "->")
189  d = read_state(ss);
190  VCSN_REQUIRE(!d.get().empty(),
191  locline, ": ",
192  "invalid daut file: expected destination after: ", s,
193  vcsn::detail::caret(is, locline));
194  auto entry = read_entry(ss);
195  try
196  {
197  edit->add_entry(s == "$" ? pre : s,
198  d == "$" ? post : d, entry);
199  }
200  catch (const std::runtime_error& e)
201  {
202  raise(locline, ": ", e, vcsn::detail::caret(is, locline));
203  }
204  }
205 
206  if (!edit)
207  edit.reset(make_editor(""));
208  return edit->result();
209  }
210  }
211 }
char get_char(std::istream &i)
Read a single char, with possible -escape support.
Definition: stream.cc:92
context make_context(const std::string &name)
Build a context from its name.
Definition: others.cc:96
void skip_space(std::istream &is)
Ignore spaces.
Definition: stream.cc:222
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:21
symbol string_t
Definition: parse.hh:66
std::string caret(std::istream &is, const rat::location &loc)
Repeat a line with an error, underlining the error with carets.
Definition: caret.cc:47
Abstract a location.
Definition: location.hh:47
Abstract Builder (the design pattern) for automata.
Definition: a-star.hh:8
void step()
Reset initial location to final location.
Definition: location.hh:88
automaton read_daut(std::istream &is, const location &l)
Definition: daut.cc:144
position begin
Beginning of the located region.
Definition: location.hh:109
void lines(int count=1)
Extend the current location to the COUNT next lines.
Definition: location.hh:100
automaton_editor * make_automaton_editor(const context &ctx)
Build an automaton editor from its context.
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:98
return res
Definition: multiply.hh:399