Commit d2887d72 authored by Akim Demaille's avatar Akim Demaille
Browse files

dyn: introduce make_ function to facilitate the parsing from a string

* vcsn/dyn/algos.hh, vcsn/dyn/algos.hxx (make_automaton)
(make_expression, make_label, make_polynomial, make_weight): New.
* build-aux/bin/oodyn-gen: Use them.

* build-aux/bin/tools-gen: Skip these `make_` functions, they are
useless in tools, use -E, -A, etc. instead.
While at it, report the function we ignore.

* build-aux/bin/oodyn-gen, build-aux/bin/tools-gen: Make sure we won't
stick on stdin, as was the case for a precursor of this commit.
parent f4682824
......@@ -92,7 +92,7 @@ classes_prologue_header = {
'expression':
r'''
expression(const struct context& ctx, const std::string& r,
identities ids);
identities ids = {});
/// Convert \a this to \a ctx, using \a ids.
expression as(const struct context& ctx = {},
......@@ -125,21 +125,23 @@ automaton::automaton(const std::string& data, const std::string& format,
const std::string& filename, bool strip)
: val_{nullptr}
{
auto is = make_istream(data, filename);
try
{
val_ = vcsn::dyn::read_automaton(*is, format, strip);
vcsn::require(is->peek() == EOF, "unexpected trailing characters: ", *is);
}
catch (const std::runtime_error& e)
// It is possible to have an empty data to mean an empty automaton.
// Check the filename instead.
if (filename.empty())
val_ = vcsn::dyn::make_automaton(data, format, strip);
else
{
if (data.empty())
vcsn::raise(e, " while reading automaton: ", filename);
else if (std::count(data.begin(), data.end(), '\n') < 30)
vcsn::raise(e, " while reading automaton: ", data);
else
vcsn::raise(e, " while reading automaton");
}
auto is = vcsn::open_input_file(filename);
try
{
val_ = vcsn::dyn::read_automaton(*is, format, strip);
vcsn::require(is->peek() == EOF, "unexpected trailing characters: ", *is);
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading automaton: ", filename);
}
}
}
''',
......@@ -148,8 +150,8 @@ automaton::automaton(const std::string& data, const std::string& format,
context::context()
{}
context::context(const std::string& ctx)
: context(vcsn::dyn::make_context(ctx))
context::context(const std::string& s)
: context(vcsn::dyn::make_context(s))
{}
context::operator bool() const
......@@ -180,20 +182,10 @@ automaton context::trie(const std::string& data,
'expression':
r'''
expression::expression(const struct context& ctx, const std::string& r,
expression::expression(const struct context& ctx, const std::string& e,
identities ids)
{
std::istringstream is{r};
try
{
val_ = vcsn::dyn::read_expression(ctx.val_, ids, is);
vcsn::require(is.peek() == EOF, "unexpected trailing characters: ", is);
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading expression: ", r);
}
}
: val_{make_expression(ctx.val_, e, ids)}
{}
expression expression::as(const struct context& ctx,
identities ids) const
......@@ -206,52 +198,22 @@ expression expression::as(const struct context& ctx,
r'''
label::label(const context& ctx, const std::string& s)
{
std::istringstream is{s};
try
{
val_ = vcsn::dyn::read_label(ctx.val_, is);
vcsn::require(is.peek() == EOF, "unexpected trailing characters: ", is);
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading label: ", s);
}
}
: val_{make_label(ctx.val_, s)}
{}
''',
'polynomial':
r'''
polynomial::polynomial(const struct context& ctx, const std::string& s)
{
std::istringstream is{s};
try
{
val_ = vcsn::dyn::read_polynomial(ctx.val_, is);
vcsn::require(is.peek() == EOF, "unexpected trailing characters: ", is);
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading polynomial: ", s);
}
}
: val_{make_polynomial(ctx.val_, s)}
{}
''',
'weight':
r'''
weight::weight(const context& ctx, const std::string& s)
{
std::istringstream is(s);
try
{
val_ = vcsn::dyn::read_weight(ctx.val_, is);
vcsn::require(is.peek() == EOF, "unexpected trailing characters: ", is);
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading weight: ", s);
}
}
: val_{make_weight(ctx.val_, s)}
{}
''',
}
......
......@@ -71,7 +71,7 @@ def fun_def_template(fun):
def ignore(fun):
'''Report that we don't process this case.'''
print('warning: ignoring: {fun}({formals}) -> {result}'
print('warning: ignoring: {dynfun}({formals}) -> {result}'
.format_map(fun), file=sys.stderr)
# These types are not supported by tools yet, meaning that
......@@ -123,6 +123,7 @@ def clean(res):
res = ugly_types.get(res, res)
return res
def gen_doc_sig(fun, formals, gen_defaults=True):
'''Generate a function signature for use
in help and error messages.'''
......@@ -164,7 +165,7 @@ def format_function_doc(fun, formals):
doc = fun['doc']
res = fun['doc']['desc']
# Indent all lines but the first
# Indent all lines but the first.
res = [res[0]] + [' ' + l for l in res[1:]]
def is_supported(name):
......@@ -215,6 +216,10 @@ def format_function_doc(fun, formals):
def process_function(fun):
'''Process a function in `dyn/algo.hh`.'''
if fun['dynfun'].startswith('make_'):
ignore(fun)
return
formals = fun['formals']
fun['command'] = fun['dynfun'].replace('_', '-')
......@@ -262,7 +267,8 @@ def gen_function_decl(fun, formals):
for i, formal in enumerate(formals):
i = i - removed_args
if formal['class'] in unsupported_types:
return ""
ignore(fun)
return ''
# We don't match on the context, and don't require it
# as an argument in tools.
......@@ -272,7 +278,8 @@ def gen_function_decl(fun, formals):
removed_args += 1
continue
else:
return ""
ignore(fun)
return ''
# dyn::word isn't really a type, it's just
# an indication for us to use a word context.
......
......@@ -8,4 +8,4 @@ set -e
## ------ ##
cd "$DIR_BUILD"
make -j${NBPROC:-2} check
make -j${NBPROC:-2} check </dev/null
......@@ -7,7 +7,7 @@ cd "$DIR_BUILD"
distcheck()
{
if make -j${NBPROC:-2} vcsn-distcheck "$@"; then
if make -j${NBPROC:-2} vcsn-distcheck "$@" </dev/null; then
:
else
sta=$?
......
%% Cell type:markdown id: tags:
# Tools
Vcsn comes with a set of programs that you use to manipulate automata, expressions, etc.
It is much less powerful than writing Python programs, however it comes handy occasionally to process a batch of files, or for a quick experiment.
## Synopsis
vcsn COMMAND [OPTION...] ARG...
Run `vcsn tools --help` for details on the interface:
%% Cell type:code id: tags:
``` python
!vcsn tools --help
```
%% Output
usage: vcsn COMMAND [OPTIONS...] [ARGS...]
General Options:
-h, --help display this help message and exit successfully
-c, --commands display the supported commands and exit successfully
Available COMMANDs:
accessible add ambiguous-word are-equal are-equivalent
are-isomorphic cat cerny coaccessible codeterminize cominimize
compare complement complete component compose concatenate condense
configuration conjugate conjunction constant-term context-of copy
costandard cotrie de-bruijn delay-automaton derivation derived-term
determinize difference divkbaseb eliminate-state evaluate expand
expression-one expression-zero factor focus has-bounded-lag
has-lightening-cycle has-twins-property identities-of inductive
infiltrate insplit is-accessible is-ambiguous is-coaccessible
is-codeterministic is-complete is-costandard is-cycle-ambiguous
is-deterministic is-empty is-eps-acyclic is-functional is-letterized
is-normalized is-out-sorted is-partial-identity is-proper
is-realtime is-standard is-synchronized is-synchronized-by
is-synchronizing is-trim is-useless is-valid join ladybird ldivide
less-than letterize levenshtein lgcd lift lightest
lightest-automaton lweight make-context make-word-context minimize
multiply normalize num-components num-tapes pair partial-identity
prefix project proper push-weights quotkbaseb random-automaton
lightest-automaton lweight minimize multiply normalize
num-components num-tapes pair partial-identity prefix project proper
push-weights quotkbaseb random-automaton
random-automaton-deterministic random-expression random-weight
rdivide realtime reduce rweight scc shortest shuffle sort split
standard star star-height star-normal-form strip subword suffix
synchronize synchronizing-word thompson to-automaton to-expansion
to-expression transpose transposition trie trim tuple type u
universal weight-one weight-series weight-zero zpc
Input/Output:
-C CONTEXT the context to use
'lal, b', 'law, q', 'lan(abc), zmin', etc.
-A input is an automaton
-B input is a boolean
-D input is an identity
-E input is a rational expression
-F input is a float
-L input is a label (or a word)
-N input is a number
-P input is a polynomial
-S input is a string
-W input is a weight
-e STRING input is STRING
-f FILE input is FILE
-I FORMAT input format (daut, dot, efsm, fado, text)
-O FORMAT output format
(daut, dot, efsm, grail, info, list, null, text, tikz, utf8)
-o FILE save output into FILE
-q discard any output
Input/Output Formats (for Automata, Expressions, Labels, Polynomials, Weights):
daut A Simplified Dot syntax for Automata
dot A GraphViz's Dot language
efsm A Extended FSM format for OpenFST: use efstcompile
fado A FAdo's format
grail A Grail's format
info AE facts about the result (size, etc.)
latex ELPW display as a LaTeX formula
list P display one monomial per line
null AELPW no output at all (e.g., for benchmarks)
text ELPW usual concrete syntax in ASCII
tikz A LaTeX source for TikZ
utf8 ELPW usual concrete syntax in UTF-8
Examples:
$ vcsn thompson -Ee '[ab]*a[ab]{3}' |
vcsn proper |
vcsn determinize |
vcsn evaluate -L 'abba'
$ vcsn thompson -Ee '[ab]*a[ab]{3}' |
vcsn proper -f - |
vcsn determinize -f - |
vcsn evaluate -f - -L 'abba'
$ vcsn derived-term -C 'lat<lan, lan>, q' -Ee 'a*|b*' |
vcsn shortest 10
%% Cell type:markdown id: tags:
You can also run `vcsn tools COMMAND -h` to get information about a particular command.
%% Cell type:code id: tags:
``` python
!vcsn tools evaluate -h
```
%% Output
usage: vcsn evaluate [OPTIONS...] [ARGS...]
Available versions:
AUT:automaton L:word -> weight
Evaluate L on AUT.
AUT:automaton P:polynomial -> weight
Evaluate P on AUT.
Try 'vcsn tools --help' for more information.
%% Cell type:code id: tags:
``` python
!vcsn tools multiply -h
```
%% Output
usage: vcsn multiply [OPTIONS...] [ARGS...]
Available versions:
AUT:automaton MIN:number [MAX=-2:number] [ALGO="auto":string] -> automaton
Repeated multiplication (concatenation) of an automaton with itself.
Parameters:
AUT the automaton.
MIN the minimum number. If -1, denotes 0.
MAX the maximum number.
If -1, denotes infinity, using star.
If -2, denotes the same value as min.
ALGO how to compute the result
- "standard" AUT is standard,
build a standard automaton.
- "general" no requirement on AUT,
but add spontaneous transitions.
- "auto" "standard" if AUT is standard,
"general" otherwise.
E:expression MIN:number [MAX=-2:number] -> expression
Repeated multiplication (concatenation) of an expression with itself.
Parameters:
E the expression.
MIN the minimum number. If -1, denotes 0.
MAX the maximum number.
If -1, denotes infinity, using star.
If -2, denotes the same value as min.
E:weight MIN:number [MAX=-2:number] -> weight
Repeated multiplication of a weight with itself.
Parameters:
E the weight.
MIN the minimum number. If -1, denotes 0.
MAX the maximum number.
If -1, denotes infinity, using star.
If -2, denotes the same value as min.
L:label NUM:number -> label
Repeated multiplication of a label with itself.
Parameters:
L the label.
NUM the exponent.
LHS:automaton RHS:automaton [ALGO="auto":string] -> automaton
Multiply (concatenate) two automata.
Parameters:
LHS an automaton.
RHS another one.
ALGO how to compute the result
- "standard" both LHS and RHS are standard,
build a standard automaton.
- "general" no requirement on LHS and and RHS,
but add spontaneous transitions.
- "auto" "standard" if both automata are standard,
"general" otherwise.
LHS:expression RHS:expression -> expression
Multiply (concatenate) two expressions.
LHS:label RHS:label -> label
Multiply (concatenate) two labels.
LHS:polynomial RHS:polynomial -> polynomial
Multiply two polynomials.
LHS:weight RHS:weight -> weight
Multiply two weights.
Try 'vcsn tools --help' for more information.
%% Cell type:markdown id: tags:
## Examples
To generate the standard automaton of $(a+b)^*c$ and save it in `abc.gv`:
%% Cell type:code id: tags:
``` python
!vcsn standard -Ee '[ab]*c' -o abc.gv
```
%% Cell type:code id: tags:
``` python
import vcsn
vcsn.automaton(filename='abc.gv')
```
%% Output
mutable_automaton<context<letterset<char_letters>, b>>
%% Cell type:markdown id: tags:
To generate a Thompson automaton, make it proper, determinize it and extract a rational expression from it:
%% Cell type:code id: tags:
``` python
!vcsn thompson -Ee '[ab]*c' | vcsn proper | vcsn determinize | vcsn to-expression
```
%% Output
c+aa*c+(b+aa*b)(b+aa*b)*(c+aa*c)
%% Cell type:markdown id: tags:
Likewise, but with an additional minimization step:
%% Cell type:code id: tags:
``` python
!vcsn thompson -Ee '[ab]*c' | vcsn proper | vcsn determinize | vcsn minimize | vcsn to-expression
```
%% Output
(a+b)*c
%% Cell type:markdown id: tags:
The Python equivalent of these runs are:
%% Cell type:code id: tags:
``` python
vcsn.B.expression('[ab]*c').thompson().proper().determinize().expression()
```
%% Output
$c + a \, {a}^{*} \, c + \left(b + a \, {a}^{*} \, b\right) \, \left(b + a \, {a}^{*} \, b\right)^{*} \, \left(c + a \, {a}^{*} \, c\right)$
c+aa*c+(b+aa*b)(b+aa*b)*(c+aa*c)
%% Cell type:code id: tags:
``` python
vcsn.B.expression('[ab]*c').thompson().proper().determinize().minimize().expression()
```
%% Output
$\left(a + b\right)^{*} \, c$
(a+b)*c
......
......@@ -18,8 +18,8 @@ dist_noinst_python += $(oodyn_gen)
# Files generated by oodyn_gen.
from_oodyn_gen = \
%D%/oodyn.hh %D%/oodyn.cc
CLEANFILES += %D%/bindings.stamp $(from_oodyn_gen)
%D%/bindings.stamp: $(oodyn_gen) vcsn/dyn/algos.json
CLEANFILES += %D%/oodyn.stamp $(from_oodyn_gen)
%D%/oodyn.stamp: $(oodyn_gen) vcsn/dyn/algos.json
$(AM_V_GEN)$(mkdir_p) $(@D)
$(AM_V_at)rm -f $@ $@.tmp
$(AM_V_at)echo '$@ rebuilt because of: $?' >$@.tmp
......@@ -33,7 +33,7 @@ CLEANFILES += %D%/bindings.stamp $(from_oodyn_gen)
done
$(AM_V_at)mv -f $@.tmp $@
$(from_oodyn_gen): %D%/bindings.stamp
$(from_oodyn_gen): %D%/oodyn.stamp
@if test ! -f $@; then \
rm -f $<; \
$(MAKE) $(AM_MAKEFLAGS) $<; \
......
......@@ -93,7 +93,7 @@ auto make_value_vector(const boost::python::list& list)
/// Convert this value to string.
template <typename Value>
std::string format(const Value& v,
const std::string& format = "text")
const std::string& format = "default")
{
std::ostringstream os;
vcsn::dyn::print(v.val_, os, format);
......
......@@ -152,14 +152,20 @@ def XFAIL(fun, exp=None):
try:
fun()
except RuntimeError as e:
# Error messages about files that we read will have $srcdir as
# prefix. Remove it. E.g., from
# while reading automaton: ../../tests/python/efsm.dir/bad_final_weight.efsm
# to
# while reading automaton: efsm.dir/bad_final_weight.efsm
eff = str(e).replace(medir + '/', '')
if (exp is None
or isinstance(exp, re._pattern_type) and re.match(exp, str(e))
or isinstance(exp, str) and exp in str(e)):
or isinstance(exp, re._pattern_type) and re.match(exp, eff)
or isinstance(exp, str) and exp in eff):
PASS()
else:
FAIL('Unexpected error message')
rst_file("Expected error", exp)
rst_file("Effective error", str(e))
rst_file("Effective error", eff)
rst_diff(exp, str(e))
else:
FAIL('did not raise an exception', str(fun))
......
......@@ -160,11 +160,15 @@ if have_ofst:
else:
SKIP('OpenFST is missing')
# Invalid final weight
XFAIL(lambda: meaut('bad_final_weight', 'efsm'), '''Zmin: unexpected d
# Invalid final weight.
XFAIL(lambda: meaut('bad_final_weight', 'efsm'),
'''Zmin: unexpected trailing characters: df
while reading: 7df
while setting final state: <7df>0''')
# Invalid transition weight
XFAIL(lambda: meaut('bad_weight', 'efsm'), '''Zmin: unexpected e
while setting final state: <7df>0
while reading automaton: bad_final_weight.efsm''')
# Invalid transition weight.
XFAIL(lambda: meaut('bad_weight', 'efsm'),
'''Zmin: unexpected trailing characters: er
while reading: 4er
while adding transition: (0, <4er>a, 1)''')
while adding transition: (0, <4er>a, 1)
while reading automaton: bad_weight.efsm''')
......@@ -559,11 +559,37 @@ namespace vcsn
unsigned num = 1,
const std::string& algo = "auto");
/// Read an automaton from a string.
/// \param data the input string.
/// \param format its format.
/// \param strip whether to return a stripped automaton,
/// or a named automaton.
automaton make_automaton(const std::string& data,
const std::string& format = "default",
bool strip = true);
/// Build an automaton editor from its context.
automaton_editor* make_automaton_editor(const context& ctx);
/// Build a context from its name.
context make_context(const std::string& name);
/// Build an automatonset from its context.
automaton_editor* make_automaton_editor(const context& ctx);
/// Read an expression from a stream.
///
/// \param ctx the context.
/// \param s the input string.
/// \param ids the identities to apply.
expression make_expression(const context& ctx,
const std::string& s, identities ids = {});
/// Build a label from a string.
label make_label(const context& ctx, const std::string& s);
/// Build a polynomial from a string.
polynomial make_polynomial(const context& ctx, const std::string& s);
/// Build a weight from a string.
weight make_weight(const context& ctx, const std::string& s);
/// The context for words.
context make_word_context(const context& ctx);
......
#include <sstream>
#include <vcsn/dyn/automaton.hh>
#include <vcsn/dyn/value.hh>
#include <vcsn/misc/yaml.hh>
namespace vcsn
......@@ -16,6 +20,11 @@ namespace vcsn
return compare(lhs, rhs) == 0;
}
inline
std::string configuration(const std::string& key)
{
return vcsn::configuration(key);
}
inline
bool less_than(const automaton& lhs, const automaton& rhs)
{
......@@ -29,9 +38,94 @@ namespace vcsn
}
inline
std::string configuration(const std::string& key)
automaton make_automaton(const std::string& data,
const std::string& format,
bool strip)
{
return vcsn::configuration(key);
std::istringstream is{data};
try
{
auto res = vcsn::dyn::read_automaton(is, format, strip);
vcsn::require(is.peek() == EOF,
"unexpected trailing characters: ", is);
return res;
}
catch (const std::runtime_error& e)
{
if (std::count(data.begin(), data.end(), '\n') < 30)
vcsn::raise(e, " while reading automaton: ", data);
else
vcsn::raise(e, " while reading automaton");
}
}
inline
expression make_expression(const context& ctx,
const std::string& s, identities ids)
{
std::istringstream is{s};
try
{
auto res = vcsn::dyn::read_expression(ctx, ids, is);
vcsn::require(is.peek() == EOF,
"unexpected trailing characters: ", is);
return res;
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading expression: ", s);
}
}
inline
label make_label(const context& ctx, const std::string& s)
{
std::istringstream is{s};
try
{
auto res = read_label(ctx, is);
vcsn::require(is.peek() == EOF,
"unexpected trailing characters: ", is);
return res;
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading label: ", s);
}
}
inline
polynomial make_polynomial(const context& ctx, const std::string& s)
{
std::istringstream is{s};
try
{
auto res = vcsn::dyn::read_polynomial(ctx, is);
vcsn::require(is.peek() == EOF,
"unexpected trailing characters: ", is);
return res;
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading polynomial: ", s);
}
}
inline
weight make_weight(const context& ctx, const std::string& s)
{
std::istringstream is(s);
try
{
auto res = vcsn::dyn::read_weight(ctx, is);
vcsn::require(is.peek() == EOF,
"unexpected trailing characters: ", is);
return res;
}
catch (const std::runtime_error& e)
{
vcsn::raise(e, " while reading weight: ", s);
}
}
}
}
......@@ -35,7 +35,7 @@ namespace vcsn LIBVCSN_API
{
const auto res = vs.conv(i, std::forward<Args>(args)...);
VCSN_REQUIRE(i.peek() == EOF,
vs, ": unexpected ", str_escape(i.peek()));
vs, ": unexpected trailing characters: ", i);
return res;
}
catch (const std::runtime_error& e)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment