src/ir/regexp/encoding/utf16/utf16.cc \
src/ir/regexp/encoding/utf16/utf16_range.cc \
src/ir/regexp/fixed_length.cc \
+ src/ir/regexp/nullable.cc \
src/ir/regexp/regexp.cc \
src/ir/compile.cc \
src/ir/rule_rank.cc \
src/ir/skeleton/control_flow.cc \
src/ir/skeleton/generate_code.cc \
src/ir/skeleton/generate_data.cc \
- src/ir/skeleton/match_empty.cc \
src/ir/skeleton/maxlen.cc \
src/ir/skeleton/skeleton.cc \
- src/ir/skeleton/unreachable.cc \
+ src/ir/skeleton/unreachable_nullable.cc \
src/ir/skeleton/way.cc \
src/main.cc \
src/parse/code.cc \
head->action.set_initial (initial_label, head->action.type == Action::SAVE);
skeleton->warn_undefined_control_flow ();
- skeleton->warn_unreachable_rules ();
- skeleton->warn_match_empty ();
+ skeleton->warn_unreachable_nullable_rules ();
if (opts->target == opt_t::SKELETON)
{
// skeleton must be constructed after DFA construction
// but prior to any other DFA transformations
Skeleton *skeleton = new Skeleton(dfa, cs, spec.rules, name, cond, line);
+ spec.re->nullable_rules(skeleton->nullable_rules);
minimization(dfa);
--- /dev/null
+#include "src/ir/regexp/regexp.h"
+#include "src/ir/regexp/regexp_alt.h"
+#include "src/ir/regexp/regexp_cat.h"
+#include "src/ir/regexp/regexp_close.h"
+#include "src/ir/regexp/regexp_match.h"
+#include "src/ir/regexp/regexp_null.h"
+#include "src/ir/regexp/regexp_rule.h"
+
+namespace re2c
+{
+
+bool AltOp::nullable() const
+{
+ return exp1->nullable()
+ || exp2->nullable();
+}
+
+bool CatOp::nullable() const
+{
+ return exp1->nullable()
+ && exp2->nullable();
+}
+
+bool CloseOp::nullable() const
+{
+ return true;
+}
+
+bool MatchOp::nullable() const
+{
+ return false;
+}
+
+bool NullOp::nullable() const
+{
+ return true;
+}
+
+bool RuleOp::nullable() const
+{
+ return exp->nullable();
+}
+
+void RegExp::nullable_rules(std::set<rule_rank_t>&) const {}
+
+void AltOp::nullable_rules(std::set<rule_rank_t> &rs) const
+{
+ exp1->nullable_rules(rs);
+ exp2->nullable_rules(rs);
+}
+
+void RuleOp::nullable_rules(std::set<rule_rank_t> &rs) const
+{
+ if (exp->nullable())
+ {
+ rs.insert(rank);
+ }
+}
+
+} // end namespace re2c
+
#include <set>
#include <vector>
+#include "src/ir/rule_rank.h"
#include "src/util/free_list.h"
#include "src/util/forbid_copy.h"
virtual void split (std::set<uint32_t> &) = 0;
virtual uint32_t calc_size() const = 0;
virtual uint32_t fixedLength ();
+ virtual bool nullable() const = 0;
+ virtual void nullable_rules(std::set<rule_rank_t>&) const;
virtual nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n) = 0;
virtual void display (std::ostream &) const = 0;
friend std::ostream & operator << (std::ostream & o, const RegExp & re);
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
uint32_t fixedLength ();
+ bool nullable() const;
+ void nullable_rules(std::set<rule_rank_t>&) const;
nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n);
void display (std::ostream & o) const;
friend RegExp * mkAlt (RegExp *, RegExp *);
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
uint32_t fixedLength ();
+ bool nullable() const;
nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n);
void display (std::ostream & o) const;
{}
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
+ bool nullable() const;
nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n);
void display (std::ostream & o) const;
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
uint32_t fixedLength ();
+ bool nullable() const;
nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n);
void display (std::ostream & o) const;
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
uint32_t fixedLength ();
+ bool nullable() const;
nfa_state_t *compile(nfa_t &nfa, nfa_state_t *n);
void display (std::ostream & o) const;
};
#include <string>
#include "src/ir/regexp/regexp.h"
-#include "src/ir/rule_rank.h"
#include "src/parse/code.h"
namespace re2c
, code (c)
, newcond (cond ? *cond : "")
{}
+ bool nullable() const;
+ void nullable_rules(std::set<rule_rank_t>&) const;
void display (std::ostream & o) const;
void split (std::set<uint32_t> &);
uint32_t calc_size() const;
+++ /dev/null
-#include <map>
-#include <set>
-
-#include "src/conf/warn.h"
-#include "src/globals.h"
-#include "src/ir/rule_rank.h"
-#include "src/ir/skeleton/path.h"
-#include "src/ir/skeleton/skeleton.h"
-#include "src/parse/rules.h"
-
-namespace re2c
-{
-
-void Skeleton::warn_match_empty ()
-{
- Node & head = nodes[0];
-
- head.calc_reachable ();
- const std::set<rule_t> & reach = head.reachable;
-
- // warn about rules that match empty string
- if (!head.rule.rank.is_none ())
- {
- bool reachable = head.end ();
- for (std::set<rule_t>::const_iterator i = reach.begin ();
- !reachable && i != reach.end (); ++i)
- {
- reachable |= i->rank.is_none ();
- }
- if (reachable)
- {
- warn.match_empty_string (rules[head.rule.rank].line);
- }
- }
-
- // warn about rules that match empty string with nonempty trailing context
- if (head.ctx)
- {
- for (std::set<rule_t>::const_iterator i = reach.begin (); i != reach.end (); ++i)
- {
- if (i->restorectx)
- {
- warn.match_empty_string (rules[i->rank].line);
- }
- }
- }
-}
-
-} // namespace re2c
, nodes (new Node [nodes_count + 1]) // +1 for default state
, sizeof_key (4)
, rules (rs)
+ , nullable_rules ()
{
const size_t nc = cs.size() - 1;
uint32_t dist;
// rules reachable from this node (including absent rule)
- std::set<rule_t> reachable;
+ std::set<rule_rank_t> reachable;
// path to end node (for constructing path cover)
path_t * suffix;
Node * nodes;
size_t sizeof_key;
rules_t rules;
+ std::set<rule_rank_t> nullable_rules;
Skeleton
( const dfa_t &dfa
);
~Skeleton ();
void warn_undefined_control_flow ();
- void warn_unreachable_rules ();
- void warn_match_empty ();
+ void warn_unreachable_nullable_rules ();
void emit_data (const char * fname);
static void emit_prolog (OutputFile & o);
void emit_start
}
else if (end ())
{
- reachable.insert (rule);
+ reachable.insert (rule.rank);
}
else if (loop < 2)
{
}
}
-void Skeleton::warn_unreachable_rules ()
+void Skeleton::warn_unreachable_nullable_rules ()
{
- nodes->calc_reachable ();
+ // calculate reachable rules
+ nodes->calc_reachable();
for (uint32_t i = 0; i < nodes_count; ++i)
{
const rule_rank_t r1 = nodes[i].rule.rank;
- const std::set<rule_t> & rs = nodes[i].reachable;
- for (std::set<rule_t>::const_iterator j = rs.begin (); j != rs.end (); ++j)
+ const std::set<rule_rank_t> & rs = nodes[i].reachable;
+ for (std::set<rule_rank_t>::const_iterator j = rs.begin(); j != rs.end(); ++j)
{
- const rule_rank_t r2 = j->rank;
- if (r1 == r2 || r2.is_none ())
+ const rule_rank_t r2 = *j;
+ if (r1 == r2 || r2.is_none())
{
rules[r1].reachable = true;
}
else
{
- rules[r1].shadow.insert (r2);
+ rules[r1].shadow.insert(r2);
}
}
}
// - infinite rules that consume infinitely many characters and fail on YYFILL, e.g. '[^]*'
// - rules that contain never-matching link, e.g. '[]' with option '--empty-class match-none'
// default rule '*' should not be reported
- for (rules_t::const_iterator i = rules.begin (); i != rules.end (); ++i)
+ for (rules_t::const_iterator i = rules.begin(); i != rules.end(); ++i)
{
const rule_rank_t r = i->first;
- if (!r.is_none () && !r.is_def () && !rules[r].reachable)
+ if (!r.is_none() && !r.is_def() && !rules[r].reachable)
{
- warn.unreachable_rule (cond, i->second, rules);
+ warn.unreachable_rule(cond, i->second, rules);
+ }
+ }
+
+ // warn about nullable rules:
+ // - rules that match empty string
+ // - rules that match empty strins with nonempty trailing context
+ // false positives on partially shadowed (yet reachable) rules, e.g.:
+ // [^]?
+ for (std::set<rule_rank_t>::const_iterator i = nullable_rules.begin();
+ i != nullable_rules.end(); ++i)
+ {
+ const rule_info_t &ri = rules[*i];
+ if (ri.reachable)
+ {
+ warn.match_empty_string(ri.line);
}
}
}
re2c: warning: line 105: rule matches empty string [-Wmatch-empty-string]
re2c: warning: line 119: rule matches empty string [-Wmatch-empty-string]
re2c: warning: line 133: rule matches empty string [-Wmatch-empty-string]
+re2c: warning: line 134: rule matches empty string [-Wmatch-empty-string]
re2c: warning: line 149: rule matches empty string [-Wmatch-empty-string]
re2c: warning: line 158: rule matches empty string [-Wmatch-empty-string]
re2c: warning: line 170: rule matches empty string [-Wmatch-empty-string]