Vcsn  2.5.dev
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  if (c == '"')
86  return read_quotes(is);
87  if (isspace(c) || is_comment(is, c))
88  break;
89  res += c;
90  }
91  boost::algorithm::trim_right(res);
92  return string_t{res};
93  }
94 
96  string_t read_entry(std::istream& is)
97  {
98  auto res = std::string{};
99  int c;
100  skip_space(is);
101  while ((c = is.get()) != EOF)
102  {
103  if (isspace(c) && res.empty())
104  continue;
105  if (is_comment(is, c))
106  break;
107  res += c;
108  }
109  return string_t{res};
110  }
111 
112  const static string_t pre = string_t{"$pre"};
113  const static string_t post = string_t{"$post"};
114 
115  vcsn::automaton_editor* make_editor(std::string ctx)
116  {
117  auto c = vcsn::dyn::make_context(ctx.empty() ? "lal_char, b" : ctx);
119  res->set_separator(',');
120  res->add_pre(pre);
121  res->add_post(post);
122  return res;
123  }
124  }
125 
126  automaton
127  read_daut(std::istream& is, const location& l)
128  {
129  // FIXME: Making `loc` const was not a good idea.
130  location loc = l;
131  auto first = true;
132  auto edit = std::shared_ptr<vcsn::automaton_editor>{};
133 
134  // Line: Source [->] Dest [Entry]
135  auto line = std::string{};
136  while (is.good())
137  {
138  loc.step();
139  loc.lines(1);
140  std::getline(is, line, '\n');
141  auto locline = location{loc.begin, loc.begin + line.size()};
142  // Trim here to handle line full of blanks.
143  boost::algorithm::trim_right(line);
144  if (line.empty() || boost::starts_with(line, "//"))
145  continue;
146 
147  if (first)
148  {
149  auto ctx = read_context(line);
150  try
151  {
152  edit.reset(make_editor(ctx));
153  }
154  catch (const std::runtime_error& e)
155  {
156  raise(locline, ": ", e, vcsn::detail::caret(is, locline));
157  }
158  first = false;
159  if (!ctx.empty())
160  continue;
161  }
162 
163  std::istringstream ss{line};
164  auto s = read_state(ss);
165  auto d = read_state(ss);
166  if (d.get().empty()) // Declaring a state with no transitions
167  {
168  edit->add_state(s);
169  continue;
170  }
171  if (d == "->")
172  d = read_state(ss);
173  VCSN_REQUIRE(!d.get().empty(),
174  locline, ": ",
175  "invalid daut file: expected destination after: ", s,
176  vcsn::detail::caret(is, locline));
177  auto entry = read_entry(ss);
178  try
179  {
180  edit->add_entry(s == "$" ? pre : s,
181  d == "$" ? post : d, entry);
182  }
183  catch (const std::runtime_error& e)
184  {
185  raise(locline, ": ", e, vcsn::detail::caret(is, locline));
186  }
187  }
188 
189  if (!edit)
190  edit.reset(make_editor(""));
191  return edit->result();
192  }
193  }
194 }
Abstract a location.
Definition: location.hh:47
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
symbol string_t
Definition: parse.hh:66
char get_char(std::istream &i)
Read a single char, with possible -escape support.
Definition: stream.cc:77
void step()
Reset initial location to final location.
Definition: location.hh:88
automaton read_daut(std::istream &is, const location &l)
Definition: daut.cc:127
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:102
return res
Definition: multiply.hh:399
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 Builder (the design pattern) for automata.
automaton_editor * make_automaton_editor(const context &ctx)
Build an automaton editor from its context.
context make_context(const std::string &name)
Build a context from its name.
Definition: others.cc:96
Definition: a-star.hh:8
boost::flyweight< std::string, boost::flyweights::no_tracking, boost::flyweights::intermodule_holder > symbol
An internalized string.
Definition: symbol.hh:21
void skip_space(std::istream &is)
Ignore spaces.
Definition: stream.cc:203