Commit 8458ab36 authored by Akim Demaille's avatar Akim Demaille
Browse files

is-valid: make the difference between invalid, and cannot-tell

Currently to check if an expression is invalid, we compute its
constant-term and catch errors: indeed `1*` will fail.

Unfortunately constant-term can also fail because of operators
it does not support, such as @ and {\}.  We must tell the difference.

See issue #60.

* vcsn/algos/constant-term.hh: Report errors for compose too.
* vcsn/algos/is-valid-expression.hh: Improve error messages.

* tests/bin/test.py: Improve error messages.
* tests/python/constant-term.py:
* tests/python/is-valid.py: Prefer q to r.
Check more cases.
parent cc862a80
......@@ -135,9 +135,10 @@ def XFAIL(fun, exp=None):
if exp is None or exp in str(e):
PASS()
else:
FAIL('does not include the expected error message')
FAIL('Unexpected error message')
rst_file("Expected error", exp)
rst_file("Effective error", str(e))
rst_diff(exp, str(e))
else:
FAIL('did not raise an exception', str(fun))
......
......@@ -3,14 +3,14 @@
import vcsn
from test import *
# Use a context with expression weights to check the order of
# products.
ctx = vcsn.context("lal_char(abc), seriesset<lal_char(xyz), q>")
# check WEIGHT RAT-EXP
# --------------------
# Check that the constant-term of RAT-EXP is WEIGHT, and check that
# this is indeed the evaluation of the empty word on derived-term(RAT-EXP).
#
# Use a context with expression weights to check the order of products.
# check WEIGHT EXP
# ----------------
# Check that the constant-term of EXP is WEIGHT, and check that this
# is indeed the evaluation of the empty word on derived-term(EXP).
def check(weight, exp, algo='expansion'):
w = ctx.weight(weight)
re = ctx.expression(exp)
......@@ -61,7 +61,8 @@ check('zyx', '(<xyz>\e){T}')
# ldiv
e = ctx.expression('<x>a{\}<x>a')
XFAIL(lambda: e.constant_term())
XFAIL(lambda: e.constant_term(),
"constant_term: ldiv is not supported")
# tuple.
#
......@@ -73,3 +74,8 @@ check('0', ' (<2>a)|(<3>x)', 'derivation')
check('0', ' (<2>a)|(<3>\e)', 'derivation')
check('0', '(<2>\e)|(<3>x)', 'derivation')
check('6', '(<2>\e)|(<3>\e)', 'derivation')
# compose
e = ctx.expression('\e|a @ a|\e')
XFAIL(lambda: e.constant_term(),
"constant_term: compose is not supported")
......@@ -3,7 +3,8 @@
import vcsn
from test import *
ctx = vcsn.context('lal_char(abc), r')
ctx = vcsn.context('lan_char(abc), q')
expr = ctx.expression
# check(OUTPUT, RAT_EXP)
# ----------------------
......@@ -11,17 +12,28 @@ ctx = vcsn.context('lal_char(abc), r')
# Also check is-valid on its Thompson.
#
# Use a context with expression weights to check the order of products.
def check (exp, re):
r = ctx.expression(re)
def check(exp, re):
r = expr(re)
CHECK_EQ(exp, r.is_valid())
# Check that we are consistent with the validity of the Thompson
# of RAT_EXP.
a = ctx.expression(re).thompson()
a = r.thompson()
CHECK_EQ(exp, a.is_valid())
check(True, 'a*')
check(False, '(<42>b*)*')
check(True, '<64>a+<4>b+(<0.6>b*)*')
check(False, '(<0.6>a*+<0.4>b*)*')
check(True, '(<0.6>a*+<0.3>b*)*<42>')
check(True, '(<0.6>a*.<0.4>b*)*')
check(True, '<64>a+<4>b+(<6/10>b*)*')
check(False, '(<6/10>a*+<4/10>b*)*')
check(True, '(<6/10>a*+<3/10>b*)*<42>')
check(True, '(<6/10>a*.<4/10>b*)*')
# Arguably, we should be able to answer here, since there are no star
# that depends on knowing these constant-terms. The day we pass
# instead of failing, just add a star...
e = expr('a{\}a')
XFAIL(lambda: e.is_valid(),
'is_valid: ldiv is not supported')
e = vcsn.context('lat<lan, lan>, q').expression('a|\e @ \e|a')
XFAIL(lambda: e.is_valid(),
'is_valid: compose is not supported')
......@@ -97,10 +97,16 @@ namespace vcsn
VCSN_RAT_VISIT(prod, v) { visit_product(v); }
VCSN_RAT_VISIT(shuffle, v) { visit_product(v); }
/// Cannot compute the constant-term easily here: c(<x>a{\}<y>a)
/// = x{\}y, yet both operands have a null constant-term.
/// Cannot compute the constant-term easily here:
/// c(<x>a{\}<y>a) = x{\}y, yet both operands have a null
/// constant-term.
VCSN_RAT_UNSUPPORTED(ldiv);
/// Cannot compute the constant-term easily here:
/// c(<x>\e|x @ <y>x|\e) = xy, yet both operands have a null
/// constant-term.
VCSN_RAT_UNSUPPORTED(compose);
VCSN_RAT_VISIT(transposition, v)
{
res_ = ws_.transpose(constant_term(v.sub()));
......@@ -181,7 +187,6 @@ namespace vcsn
weightset_t ws_;
weight_t res_;
};
} // rat::
/// The constant term of \a e.
......@@ -189,8 +194,8 @@ namespace vcsn
weight_t_of<ExpSet>
constant_term(const ExpSet& rs, const typename ExpSet::value_t& e)
{
auto constant_term = rat::constant_term_visitor<ExpSet>{rs};
return constant_term(e);
auto c = rat::constant_term_visitor<ExpSet>{rs};
return c(e);
}
namespace dyn
......
#pragma once
#include <cstring> // strstr
#include <stdexcept>
#include <vcsn/algos/constant-term.hh>
......@@ -18,11 +19,21 @@ namespace vcsn
{
try
{
// We check validity by checking whether constant_term succeeds:
// `1*` will raise.
constant_term(rs, e);
return true;
}
catch (const std::runtime_error&)
catch (const std::runtime_error& e)
{
// Some operators prevent the computation of the constant-term,
// and raise an exception. Forward that failure.
if (std::strstr(e.what(), "is not supported"))
raise("is_valid", strchr(e.what(), ':'));
// Make sure this is really the failure we are looking for.
if (std::strstr(e.what(), "star: invalid value"))
std::cerr << "warning: is_valid: unexpected error: "
<< e.what() << '\n';
return false;
}
}
......
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