]> granicus.if.org Git - re2c/log
re2c
7 years agoCompact tag versions before doing liveness analysis.
Ulya Trofimovich [Mon, 23 Jan 2017 16:38:04 +0000 (16:38 +0000)]
Compact tag versions before doing liveness analysis.

After TDFA construction tag versions occupy non-contiguous range
of natural numbers: there may be "holes" between versions. This wastes
space (and time) spent on liveness analysis and subsequent optimization
passes. This commit adds a simple pass that renumbers versions so that
they occupy contiguios range.

7 years agoUpdated parser for bison-3.0.4.
Ulya Trofimovich [Mon, 23 Jan 2017 16:19:24 +0000 (16:19 +0000)]
Updated parser for bison-3.0.4.

7 years agoAdded option '--no-lookahead'.
Ulya Trofimovich [Wed, 18 Jan 2017 13:58:48 +0000 (13:58 +0000)]
Added option '--no-lookahead'.

This option makes re2c generate TDFA that does not exploit lookahead
symbol when making tagged transitions.

7 years agoAdded option '--eager-skip'.
Ulya Trofimovich [Tue, 17 Jan 2017 12:36:43 +0000 (12:36 +0000)]
Added option '--eager-skip'.

This option allows to control program points in which cursor is
advanced to the next input character. Normally it happens after moving
to the next state (lazyly). With '--eager-skip' cursor is advanced
before moving to the next state (eagerly).

Eager mode presents some difficulties compared to the lazy mode:
transitions may disagree on whether advance cursor or not. Some of
them may go to accepting states and should not advance cursor, while
others go normal states and should advance cursor. In such cases
advancement must happen on a per-transition basis. If all transitions
agree on the matter, advancement can be hoisted out and performed on
a per-state basis, just as it happens in lazy mode.

7 years agoReuse mode: always reparse rules and restore options of the rules block.
Ulya Trofimovich [Sun, 8 Jan 2017 10:34:04 +0000 (10:34 +0000)]
Reuse mode: always reparse rules and restore options of the rules block.

7 years agoConsistently use current block's options for code generation.
Ulya Trofimovich [Sun, 1 Jan 2017 18:47:06 +0000 (18:47 +0000)]
Consistently use current block's options for code generation.

8 years agoFold output expressions in a separate pass over the output.
Ulya Trofimovich [Fri, 30 Dec 2016 17:15:16 +0000 (17:15 +0000)]
Fold output expressions in a separate pass over the output.

re2c folds sequences of simple statements to complex tatements, e.g.:
    ++YYCURSOR;
    yych = *YYCURSOR;
is folded to:
    yych = *++YYCURSOR;

It is hard to fold expressions on the fly (at the time they are generated),
because one needs to forsee all the cases in advance. Some cases cannot be
anticipated (e.g. if substatements belong to different states, which is often
the case), so many statements remain unfolded.

This commit separates code generation and folding by delaying output of
skip, peek and backup primitives. Folding now is done in a separate pass
when all the substatements are already known.

8 years agoMemorize options for each block and use them for delayed code generation.
Ulya Trofimovich [Thu, 29 Dec 2016 17:07:52 +0000 (17:07 +0000)]
Memorize options for each block and use them for delayed code generation.

Option scope is very poorly defined in re2c. Most options should have block
scope, but some have global scope (because they belong to global directives
like '/*!types:re2c ... */', '/*!tags:re2c ... */' or other global things
like generation date, etc.).

For now, options are applied immediately as they are parsed.
Code generation just reads the most recent state of options, so the output
depends not only on the location in source code when the option is defined,
but also on program points in which re2c generates and outputs code.
Generation and output may happen at different times, so they may use
different options. This is all very bad.

This commit is the first attempt to introduce scoped options: re2c now
makes a snapshot of options at the end of each block and uses this snapshot
for delayed code generation for this block (unless the given option must be
global).

This is not perfect (immediate code generation still uses 'seen-so-far'
options instead of block options), but at least it allows to delay code
generation for non-global things without loosing options.

8 years agoDelay code generation for condition dispatch.
Ulya Trofimovich [Tue, 27 Dec 2016 21:04:09 +0000 (21:04 +0000)]
Delay code generation for condition dispatch.

8 years agoFinally separated parsing, consistensy checking and after-parse transformation.
Ulya Trofimovich [Tue, 27 Dec 2016 17:00:09 +0000 (17:00 +0000)]
Finally separated parsing, consistensy checking and after-parse transformation.

Updates in test results are caused by the changed order of condition
compilation, see commit b7bbbf471778b487d0717c85a5e586b595e495ec.
Changes do not affect code generation, only the error messages.

8 years agoEmit conditions ordered by their number rather than lexicograhically.
Ulya Trofimovich [Sun, 25 Dec 2016 18:13:43 +0000 (18:13 +0000)]
Emit conditions ordered by their number rather than lexicograhically.

Each condition has a unique name and unique number. Numbers are assigned
to conditions in the order they appear in source file. This order must
be preserved since some users rely on it (user code makes implicit
assumptions about condition order, see '-Wcondition-order').

However, once the order is preserved, the automata code can be emitted
in any order (user code can only address conditions by their start label,
and it can be anywhere in the output file).

This commit changes the order of automata: now they are emitted according
to their numbers, not names.

Test results have changed in cases when lexicographical order does not
coinside with number order. The changes touch some important files and
are not easy to verify. However, rearranging conditions in the generated
code back in lexicographical order makes the differences trivial (labels,
'YYDEBUG' parameter, 'YYFILL' labels, 'YYSETSTATE' parameter --- in other
words, everything that depends on state numbering). It is easy to see
that the structure of the code is not changed.

8 years agoReuse 'Code' data struct for setup code.
Ulya Trofimovich [Sat, 24 Dec 2016 12:00:59 +0000 (12:00 +0000)]
Reuse 'Code' data struct for setup code.

8 years agoReduced one argument to function as it is a part of another argument.
Ulya Trofimovich [Sat, 24 Dec 2016 11:20:48 +0000 (11:20 +0000)]
Reduced one argument to function as it is a part of another argument.

8 years agoParser: started gathering all error messages in one place.
Ulya Trofimovich [Sat, 24 Dec 2016 10:40:08 +0000 (10:40 +0000)]
Parser: started gathering all error messages in one place.

8 years agoKeep startup code '<>' separately from other rules.
Ulya Trofimovich [Fri, 23 Dec 2016 16:53:31 +0000 (16:53 +0000)]
Keep startup code '<>' separately from other rules.

8 years agoKeep '*' rules separately from other rules.
Ulya Trofimovich [Fri, 23 Dec 2016 16:10:50 +0000 (16:10 +0000)]
Keep '*' rules separately from other rules.

8 years agoSetup code for rules belongs in DFA scope, not the entire block scope.
Ulya Trofimovich [Fri, 23 Dec 2016 15:55:01 +0000 (15:55 +0000)]
Setup code for rules belongs in DFA scope, not the entire block scope.

8 years agoRestructured parser grammar to explicitly list different rule types.
Ulya Trofimovich [Fri, 23 Dec 2016 15:01:28 +0000 (15:01 +0000)]
Restructured parser grammar to explicitly list different rule types.

8 years agoParser grammar: separate production for code in conditional rules.
Ulya Trofimovich [Thu, 22 Dec 2016 22:11:08 +0000 (22:11 +0000)]
Parser grammar: separate production for code in conditional rules.

8 years agoPass context as parameter to 'yyparse' instead of a bunch of static variables.
Ulya Trofimovich [Thu, 22 Dec 2016 17:36:15 +0000 (17:36 +0000)]
Pass context as parameter to 'yyparse' instead of a bunch of static variables.

As a side effect of this commit a long-standing bug with setup rules
has been fixed: re2c forgot to clean up the list of setup rules together
with other data, which resulted in false errors about duplicate setup code.

8 years agoRemoved unused typedef.
Ulya Trofimovich [Thu, 22 Dec 2016 16:56:15 +0000 (16:56 +0000)]
Removed unused typedef.

8 years agoFixed memleaks (found by valgrind).
Ulya Trofimovich [Thu, 22 Dec 2016 16:53:41 +0000 (16:53 +0000)]
Fixed memleaks (found by valgrind).

8 years agoHandle all types of rules in one place (in parser).
Ulya Trofimovich [Thu, 22 Dec 2016 14:40:32 +0000 (14:40 +0000)]
Handle all types of rules in one place (in parser).

8 years agoSimplified handling of user-defined code in parser.
Ulya Trofimovich [Thu, 22 Dec 2016 13:58:05 +0000 (13:58 +0000)]
Simplified handling of user-defined code in parser.

8 years agoInlined small function with only one user.
Ulya Trofimovich [Thu, 22 Dec 2016 12:04:59 +0000 (12:04 +0000)]
Inlined small function with only one user.

8 years agoSimplified line tracking in lexer.
Ulya Trofimovich [Thu, 22 Dec 2016 11:58:48 +0000 (11:58 +0000)]
Simplified line tracking in lexer.

8 years agoSeparated compilation pass from code generation pass.
Ulya Trofimovich [Thu, 22 Dec 2016 09:54:13 +0000 (09:54 +0000)]
Separated compilation pass from code generation pass.

They might be totally disconnected in case of '-r' option and it's
more convenient to handle them separately.

8 years agoParse rules in a uniform way regardless of the '-c' option.
Ulya Trofimovich [Wed, 21 Dec 2016 23:14:17 +0000 (23:14 +0000)]
Parse rules in a uniform way regardless of the '-c' option.

8 years agoSimplified parsing of startup code '<>'.
Ulya Trofimovich [Wed, 21 Dec 2016 22:31:34 +0000 (22:31 +0000)]
Simplified parsing of startup code '<>'.

8 years agoDon't use special token type for '<!' and '<>'.
Ulya Trofimovich [Wed, 21 Dec 2016 18:44:02 +0000 (18:44 +0000)]
Don't use special token type for '<!' and '<>'.

Parse them as character sequences.

8 years agoFurther simplified default rule parsing.
Ulya Trofimovich [Wed, 21 Dec 2016 17:21:31 +0000 (17:21 +0000)]
Further simplified default rule parsing.

8 years agoSimplified handling of default rule in parser.
Ulya Trofimovich [Wed, 21 Dec 2016 17:12:09 +0000 (17:12 +0000)]
Simplified handling of default rule in parser.

8 years agoDon't loose condition if it consists of default rule only.
Ulya Trofimovich [Wed, 21 Dec 2016 11:43:22 +0000 (11:43 +0000)]
Don't loose condition if it consists of default rule only.

8 years agoSimplified '*' condition handling in parser.
Ulya Trofimovich [Tue, 20 Dec 2016 22:45:23 +0000 (22:45 +0000)]
Simplified '*' condition handling in parser.

8 years agoMoved variable from global scope to function scope.
Ulya Trofimovich [Tue, 20 Dec 2016 09:39:52 +0000 (09:39 +0000)]
Moved variable from global scope to function scope.

8 years agoMake 'Scanner' parameter to 'yyparse' instead of global variable.
Ulya Trofimovich [Mon, 19 Dec 2016 09:59:15 +0000 (09:59 +0000)]
Make 'Scanner' parameter to 'yyparse' instead of global variable.

8 years agoRemoved empty header 'globals.h' and all includes of it.
Ulya Trofimovich [Mon, 19 Dec 2016 08:45:46 +0000 (08:45 +0000)]
Removed empty header 'globals.h' and all includes of it.

8 years agoRemoved variable from global scope.
Ulya Trofimovich [Mon, 19 Dec 2016 08:42:14 +0000 (08:42 +0000)]
Removed variable from global scope.

8 years agoRemoved variable from global scope.
Ulya Trofimovich [Mon, 19 Dec 2016 08:37:46 +0000 (08:37 +0000)]
Removed variable from global scope.

8 years agoRemoved variable from global scope, simplified bitmaps.
Ulya Trofimovich [Mon, 19 Dec 2016 00:06:19 +0000 (00:06 +0000)]
Removed variable from global scope, simplified bitmaps.

8 years agoRemoved variable from global scope.
Ulya Trofimovich [Sun, 18 Dec 2016 16:37:05 +0000 (16:37 +0000)]
Removed variable from global scope.

8 years agoRemoved variable from global scope.
Ulya Trofimovich [Sun, 18 Dec 2016 14:56:10 +0000 (14:56 +0000)]
Removed variable from global scope.

8 years agoRemoved warnings from global scope.
Ulya Trofimovich [Sun, 18 Dec 2016 12:27:19 +0000 (12:27 +0000)]
Removed warnings from global scope.

8 years agoRemoved options from global scope.
Ulya Trofimovich [Sun, 18 Dec 2016 12:02:05 +0000 (12:02 +0000)]
Removed options from global scope.

8 years agoSimplified code generation for initial state.
Ulya Trofimovich [Wed, 14 Dec 2016 16:25:54 +0000 (16:25 +0000)]
Simplified code generation for initial state.

8 years agoSimplified generation of code for buffer refilling.
Ulya Trofimovich [Wed, 14 Dec 2016 15:16:01 +0000 (15:16 +0000)]
Simplified generation of code for buffer refilling.

8 years agoDon't delay reading next character until dispatch.
Ulya Trofimovich [Wed, 14 Dec 2016 14:21:28 +0000 (14:21 +0000)]
Don't delay reading next character until dispatch.

Instead of sometimes generating code like this:

    ++YYCURSOR;
    switch ((yych = *YYCURSOR)) {

re2c now always generates

    yych = *++YYCURSOR;
    switch (yych) {

There's not much difference: it's hard to say which would be better
optimized by C/C++ compiler. Most likely they would be optimized
equally well; practical experiments with GCC -O2 resulted in bit-to-bit
identical binaries.

However, dropping the first form allows to cut off a lot of tricky code
in re2c itself (a whole layer of code spread across multiple files was
tracking whether next input character has been already read or not).

Changes to the test results are multiple, but trivial; they have been
verified with skeleton.

8 years agoDon't peek next input character if all transitions go to final state.
Ulya Trofimovich [Wed, 14 Dec 2016 13:35:21 +0000 (13:35 +0000)]
Don't peek next input character if all transitions go to final state.

A similar heuristic already existed, but it was imprecise and captured
only those cases when final state happened to be next to the one in
question.

A lot of tests were updated; however changes are trivial and I verified
them with skeleton.

8 years agoRenamed option '--dfa-mapping <bijective | injective>' to '--non-bijective-mapping'.
Ulya Trofimovich [Mon, 12 Dec 2016 13:44:00 +0000 (13:44 +0000)]
Renamed option '--dfa-mapping <bijective | injective>' to '--non-bijective-mapping'.

8 years agoSearch for the best candidate in case of non-bijective mapping.
Ulya Trofimovich [Mon, 12 Dec 2016 13:16:56 +0000 (13:16 +0000)]
Search for the best candidate in case of non-bijective mapping.

For bijective mappings there is always at most one mapping candidate,
for non-bijective mappings there may be many candidates.

8 years agoRespect injective mappings when optimizing save(X), copy(Y,X) to save(Y).
Ulya Trofimovich [Fri, 9 Dec 2016 12:15:56 +0000 (12:15 +0000)]
Respect injective mappings when optimizing save(X), copy(Y,X) to save(Y).

If mapping is injective, there may be multiple copy commands per one
save command: save(X), copy(Y,X), copy(Z,X). In this case save command
must be replicated for each copy command: save(Y), save(Z).

8 years agoNow checking for tag priority violation respects injective mappings.
Ulya Trofimovich [Thu, 8 Dec 2016 17:34:12 +0000 (17:34 +0000)]
Now checking for tag priority violation respects injective mappings.

8 years agoFixed atrocious typo that unconditionally enabled injective mapping.
Ulya Trofimovich [Thu, 8 Dec 2016 13:31:28 +0000 (13:31 +0000)]
Fixed atrocious typo that unconditionally enabled injective mapping.

However, injectivity never showed up because every injective mapping
(as of now) is erroneously considered to violate tag priority.

8 years agoAdded test: exponential blowup if bottom is treated as special version.
Ulya Trofimovich [Wed, 7 Dec 2016 17:09:14 +0000 (17:09 +0000)]
Added test: exponential blowup if bottom is treated as special version.

One tempting way to track default value of tags is to forbid mapping
bottom (default) to non-bottom versions. This way we don't need to
explicitely save bottom to variable; bottom value is encoded in state
(once a tag becomes bottom, it will never become non-bottom again).

However, this may exponentially blow up the number of states.

8 years agoCleaned up determinization.
Ulya Trofimovich [Wed, 7 Dec 2016 15:08:58 +0000 (15:08 +0000)]
Cleaned up determinization.

Moved creation of new DFA states and finalizers to a separate routine,
together with building tag commands.

8 years agoUntwined determinization and '-Wnondeterministic-tags' analysis.
Ulya Trofimovich [Tue, 6 Dec 2016 13:35:07 +0000 (13:35 +0000)]
Untwined determinization and '-Wnondeterministic-tags' analysis.

Now '-Wnondeterministic-tags' analysis is done right after determinization,
while we still have all the kernels available. Instead of comparing
transition tags (which are available in closure items, but not in kernels)
we now compare tag versions: maximum number of distinct versions per tag is
the desired degree of nondeterminism. Kernel items that belong to another
rule do not count (all tags that don't belong to a particular rule should
have initial versions in all items belonging to this rule).

Added test with an example of pathological regexp: counted repetition
cannot be folded efficiently in DFA, and combined with nondeterministic
tags it results in arbitrary long chains of reordering commands.
Length of command choin grows linearly with the number of repetitions
(as DFA itself does).

8 years agoDebug '--dump-adfa' option: drop 'YYFILL' in move states, it's fictive.
Ulya Trofimovich [Mon, 5 Dec 2016 22:19:37 +0000 (22:19 +0000)]
Debug '--dump-adfa' option: drop 'YYFILL' in move states, it's fictive.

8 years agoAdded debug option '--dump-dfa-raw'.
Ulya Trofimovich [Mon, 5 Dec 2016 17:32:41 +0000 (17:32 +0000)]
Added debug option '--dump-dfa-raw'.

It dumps DFA at the very early stage of construction, expanding underlying
state kernels with NFA substates and correspondings sets of tag versions.

8 years agoAdded option '--dfa-mapping <bijective | injective>'.
Ulya Trofimovich [Sun, 4 Dec 2016 14:38:53 +0000 (14:38 +0000)]
Added option '--dfa-mapping <bijective | injective>'.

This option controls how tag versions in DFA states are mapped to each
other during determinization. By default mapping is 'bijective', however
if it is set to 'injective' the new state must be less versatile than
the old one.

8 years agoDebug '--dump-adfa' option: correctly display range lower bound.
Ulya Trofimovich [Sun, 4 Dec 2016 12:16:19 +0000 (12:16 +0000)]
Debug '--dump-adfa' option: correctly display range lower bound.

8 years agoDrop hoisted tags in 'base' states duplicated by tunneling.
Ulya Trofimovich [Sun, 4 Dec 2016 12:00:31 +0000 (12:00 +0000)]
Drop hoisted tags in 'base' states duplicated by tunneling.

Tunneling optimization finds certain states and splits them into
two parts: First part consumes a character and unconditionally moves
to the second part. Second part does not consume characters, but
contains a switch on character value.

Second part is a 'base switch', which may be utilized by many other
states. So if the first part has hoisted tags, they should be dropped
in the second part (the aforementioned many other states may have hoisted
tags of there own or no hoisted tags at all).

Found by slyfox's fuzzer. :)

Yet unable to construct a test case that reveals the error: the original
test case found by fuzzer is not breaking anymore after recent commit
44045203c211531cc3e3a47f0b5eb2e5d993cc15 "Properly remove useless final states.".

8 years agoFixed 'uniq' on commands (algorithm that removes duplicates).
Ulya Trofimovich [Sat, 3 Dec 2016 00:07:17 +0000 (00:07 +0000)]
Fixed 'uniq' on commands (algorithm that removes duplicates).

The previous version erroneously skipped a coomand after removal,
so it would fail in case of three identical commands in a row.

8 years agoDon't loose liveness of tags that occur on both sides of 'copy' commands.
Ulya Trofimovich [Fri, 2 Dec 2016 23:33:51 +0000 (23:33 +0000)]
Don't loose liveness of tags that occur on both sides of 'copy' commands.

When processing chain of 'copy' commands like 'x = y; y = z;'
one should first mark all left hand sides ('x' and 'y') as dead,
then mark all right hand sides as alive ('y' and 'z'). This way
'y' will end up alive, as it should be.

Found by slyfox's fuzzer. :)

8 years agoProperly remove useless final states.
Ulya Trofimovich [Fri, 2 Dec 2016 19:06:42 +0000 (19:06 +0000)]
Properly remove useless final states.

Before this commit the algorithm considered final state useless only
if its rule is unreachable in this state. However, this leaves out
cases when final state is shadowed by other final states of the same
rule (longer alternatives of the same rule always match). In such
cases the rule is reachable because some (maybe all) outgoing accepting
paths match the same rule.

Now the algorithm considers final state useless if all outgoing paths
are accepting (there's no default transitions and default rule is
unreachable in all successors).

8 years agoRetry tunnel optimization if the first attempt is unsuccessful.
Ulya Trofimovich [Fri, 2 Dec 2016 13:37:59 +0000 (13:37 +0000)]
Retry tunnel optimization if the first attempt is unsuccessful.

This is probably a decades-old typo (misplaced 'break' statement).
Surely the author intended to try all successors, not just the first one.

8 years agoAdded debug option '--dump-adfa'.
Ulya Trofimovich [Thu, 1 Dec 2016 15:21:48 +0000 (15:21 +0000)]
Added debug option '--dump-adfa'.

8 years agoTopologically sort 'copy' commands.
Ulya Trofimovich [Wed, 30 Nov 2016 23:02:25 +0000 (23:02 +0000)]
Topologically sort 'copy' commands.

The order in which copy commands are executed is important:
'x = y; y = z;' is not the same as 'y = z; x = y;' (the latter
overwrites 'y' before its precious value is copied to 'x').
To avoid overwrites, commands should be topologically sorted.
This is always possible because there's no cyclic dependencies
by construction.

Found by slyfox's fuzzer. :)

8 years agoAdded debug options '--dump-dfa-det', '--dump-dfa-tagopt' and '--dump-dfa-min'.
Ulya Trofimovich [Wed, 30 Nov 2016 16:48:45 +0000 (16:48 +0000)]
Added debug options '--dump-dfa-det', '--dump-dfa-tagopt' and '--dump-dfa-min'.

8 years agoSkeleton: apply 'copy' commands before 'save' commands.
Ulya Trofimovich [Wed, 30 Nov 2016 12:37:38 +0000 (12:37 +0000)]
Skeleton: apply 'copy' commands before 'save' commands.

This is how the generated TDFA does and how it should be done.

Found by slyfox's fuzzer. :)

8 years agoAllow nondeterministic tags.
Ulya Trofimovich [Wed, 30 Nov 2016 11:21:27 +0000 (11:21 +0000)]
Allow nondeterministic tags.

This commit makes some important changes:

    - Each DFA state under construction keeps a set of tag versions
      (which version of each tag is actual for each NFA sub-state).

    - Tag versions now have priorities which allow to disambuguate
      between two competing configurations when building tagged epsilon-
      closure. We try to maximaze value of each tag, thus versions
      that correspond to greater tag value have greater priority.

    - Comparison of DFA states (when trying to find an existing state
      equal to the new one) has become more complex. We try to find
      a similar enough (not necessarily identical) state: one that is
      equal to the new state up to reordering of tag versions. Mapping
      should be either bijective or injective (new state must be less
      or equally versatile then the old one), default is bijective.
      Mapping should not violate tag priorities: relative order of
      versions must hold (absolute values of priorities don't have to
      coincide).

    - On each update tag gets new version: if tag is updated to bottom,
      it gets negative version; otherwise it gets positive version.

    - Different tags that updated by the same transition still get
      different versions. This may seem to be a waste: we know exactly
      that input position is equal for all tags on the same transition,
      even more, for all tags on all outgoung transitions from the same
      state, so we can alocate exactly one version for all of them.
      However, allocating one version for different tags creates states
      with a very specific tag configurations: these states don't map
      to existing states and the automaton gets bloated (sometimes
      exponentially). Furthermore, allocating independent versions is
      good for optimizations.

    - Second pass of optimizations: it only makes difference for one
      test case (usually the first pass does a good job), but it's cheap
      (all the necessary infrastructure is in place) and powerful.

    - The resulting automaton is similar to Laurikari's TDFA, except
      for one important difference: re2c's automaton makes use of
      lookahead tags, while Laurikari's does not.

8 years agoAdded test: tags in trailing context.
Ulya Trofimovich [Mon, 28 Nov 2016 15:48:09 +0000 (15:48 +0000)]
Added test: tags in trailing context.

8 years agoKeep fixed and variable tags separately.
Ulya Trofimovich [Mon, 28 Nov 2016 15:32:46 +0000 (15:32 +0000)]
Keep fixed and variable tags separately.

This commit reverts commit de3f9e70a45c42fcb848a347ece3a727b8fb983e:
    Keep fixed and variable tags together in one array.
Optimizations of variable tags get more complicated and fixed tags
should not get in the way.

This commit also drops check for tags in trailing context: there's
nothing special about them; no technical reason to forbid them.

8 years agoMerged computation of fixed / variable tags into NFA construction.
Ulya Trofimovich [Sat, 26 Nov 2016 20:16:11 +0000 (20:16 +0000)]
Merged computation of fixed / variable tags into NFA construction.

NFA construction is already complex because of insertion of default tags,
now it gets even worse. However, keeping two separate passes requires
maintaining exactly the same order of traversal, which is fragile.

8 years agoRemoved redundant computation.
Ulya Trofimovich [Sat, 26 Nov 2016 18:09:06 +0000 (18:09 +0000)]
Removed redundant computation.

Tags used in the given sub-regexp are already tracked by tag index;
boolean array is redundant.

8 years agoUse different datatypes for closures and kernels.
Ulya Trofimovich [Sat, 19 Nov 2016 23:01:44 +0000 (23:01 +0000)]
Use different datatypes for closures and kernels.

This is a preliminary step for tracking tag versions in closures / kernels.

For now closures and kernels store the same information, but it will
diverge when we start tracking tag versions: closure items will need
some extra-data that is needed during closure construction, but shouldn't
be in kernel.

Kernel representation should also allow efficient comparison for identitiy
or compatibility (for mapping).

8 years agoSkeleton: fixed comparison of transition tags during construction.
Ulya Trofimovich [Fri, 18 Nov 2016 16:37:40 +0000 (16:37 +0000)]
Skeleton: fixed comparison of transition tags during construction.

At the time of skeleton construction DFA has just been build and
all tags in it are just raw pointers to lists of commands. These
pointers are unique for each transition (tags are not shared between
transitions). This means, comparing tags for different transitions
will always result in 'not equal', except if both transitions have
no tags (pointers are NULLs).

Found by slyfox's fuzzer. ;)

8 years agoSkeleton: generate proper keys for tags.
Ulya Trofimovich [Fri, 18 Nov 2016 16:29:09 +0000 (16:29 +0000)]
Skeleton: generate proper keys for tags.

Instead of calculating all necessary keys, then attributing keys
of the first path to all paths, write proper keys for each path.

Found by slyfox's fuzzer. :)

8 years agoTrack uninitialized tags and set them to default value.
Ulya Trofimovich [Fri, 18 Nov 2016 15:54:40 +0000 (15:54 +0000)]
Track uninitialized tags and set them to default value.

Uninitialized tags come from alternative and iteration:
    ... @t ... | ...
    (... @t ...)*

Tags under alternative preserve their initial value (given to them
before entering DFA) -- they don't have to be explicitely set to
default value (but it is convenient, otherwise we'd have to initialize
all tags every time before entering DFA).

Tags enclosed in iteration might be assigned positional values on
some iterarions, but not on the last one -- their default value must
be restored explicitely before each iteration.

This commit adds specail kind of 'save' commands that set the given
tag to default value (instead of current input position). We insert
these 'save default' commands for tags in alternatives: all tags that
are present on left alternative get default values at the end of right
alternative.

Later on (during DFA construction and optimizations) we treat default
value just like normal positional value: save it to a variable, copy
values between variables, etc. This way DFA gets additional commands,
but the number of states does not grow much.

Another way would be to treat default value as a special bottom value:
instead of saving it to variable, keep a dedicated bottom tag version
and forbid mixing bottom version with other versions. This way default
values would be encoded in the structure of DFA, so there would be no
need to explicitely save them. DFA would gain no additional default
commands, but new states would appear.

Test changes have been verified with '--skeleton'.
Test 'tags/interference.i--tags.re' have been completely rewritten --
the old version no longer captures what it should (see comment in the
test). The old vesion has been moved to 'tags/fallback6.i--tags.re'.

8 years agoDon't loose 'yyaccept' when fallback and initial states coincide.
Ulya Trofimovich [Tue, 15 Nov 2016 17:09:53 +0000 (17:09 +0000)]
Don't loose 'yyaccept' when fallback and initial states coincide.

Before code generation each DFA state is assigned a specific action.
Actions are mutually exclussive except for one: inital state may
coincide with fallback state. The bug was in overriding fallback
action with initial action (we lost 'yyaccept' which cause the wrong
match).

The bug was found with '--skeleton'.

8 years agoReduce all final versions of the given tag to one common version.
Ulya Trofimovich [Mon, 14 Nov 2016 17:15:30 +0000 (17:15 +0000)]
Reduce all final versions of the given tag to one common version.

Tag may have different final versions in different accepting states
(currently either fallback version of the main version). Now we
reduce all final versions to one (this takes one additional 'copy'
command for normal tags and none for tags that have fallback copy).

8 years agoSkeleton tests: count re2c crashes as fatal (not expected) errors.
Ulya Trofimovich [Sat, 12 Nov 2016 21:47:11 +0000 (21:47 +0000)]
Skeleton tests: count re2c crashes as fatal (not expected) errors.

8 years agoSkeleton: code cleanup: split large function into subroutines.
Ulya Trofimovich [Sat, 12 Nov 2016 21:33:13 +0000 (21:33 +0000)]
Skeleton: code cleanup: split large function into subroutines.

8 years agoSkeleton: check tags.
Ulya Trofimovich [Sat, 12 Nov 2016 15:24:57 +0000 (15:24 +0000)]
Skeleton: check tags.

Calculate final tag values during string generation and put them into
'.keys' file together with the length of the consumed / matched input
and the matching rule number. Each pack of keys only gets those tags
that belong to the matching rule (no tags for default paths or paths
corresponding to untagged rules). Tags do not include trailing context,
as it is already included into the length of matching input.

8 years agoEmulate tag calculation when generating paths for skeleton.
Ulya Trofimovich [Thu, 10 Nov 2016 15:44:02 +0000 (15:44 +0000)]
Emulate tag calculation when generating paths for skeleton.

8 years agoLimited skeleton lifespan to the time of DFA construction.
Ulya Trofimovich [Thu, 10 Nov 2016 11:24:35 +0000 (11:24 +0000)]
Limited skeleton lifespan to the time of DFA construction.

There's no need to pull the whole skeleton up to code generation phase:
skeleton itself is used only for string generation and tracing undefined
control flow, both things are done after DFA construction and before any
optimizations.

By the time code generation starts skeleton is obsolete: its structure
is no longer in sync with DFA and it shouldn't be used.
Code generation only needs some attributes (key size and default rule
index), which can be explicitely stored in DFA.

8 years agoMoved '-Wundefined-control-flow' analysis to the point of skeleton generation.
Ulya Trofimovich [Wed, 9 Nov 2016 23:05:00 +0000 (23:05 +0000)]
Moved '-Wundefined-control-flow' analysis to the point of skeleton generation.

8 years agoSkeleton: share data with DFA, generate strings before optimizing DFA.
Ulya Trofimovich [Wed, 9 Nov 2016 22:21:13 +0000 (22:21 +0000)]
Skeleton: share data with DFA, generate strings before optimizing DFA.

Skeleton must use unoptimized DFA right after determinization, otherwise
it won't be able to track optimization errors. Therefore skeleton must
either use a copy of DFA, or use the original before any optimizations.
Copy-pasting the whole DFA is a waste (especially when it comes to tag
commands, which are stored in linked lists), so we take the second approach.

However, this means that skeleton '.input' files are generated every time
DFA is constructed, not every time DFA is used for code generation.
This explains changes in tests: in reuse mode, we sometimes construct
DFA and never use it for code generation.

8 years agoSkeleton: don't keep expanded range representation in nodes.
Ulya Trofimovich [Sun, 6 Nov 2016 15:11:31 +0000 (15:11 +0000)]
Skeleton: don't keep expanded range representation in nodes.

For the most part character range is represented as a pair of bounds.
When generating data for skeleton programs we need to pick some
256 distinct characters from each range; only these characters will
participate in data generation.

Before this commit range expansion happened at the time of skeleton
construction. Now it is delayed until the time of dumping skeleton
paths to file. This means that expansion of particular range is done
multiple times (every time the corresponding skeleton multiarc is
dumped to file). However, data generation only happens with '--skeleton'
option, while skeleton construction is unavoidable.

8 years agoAdded test and comment about tag backups in fallback states.
Ulya Trofimovich [Fri, 4 Nov 2016 15:01:47 +0000 (15:01 +0000)]
Added test and comment about tag backups in fallback states.

8 years agoMoved tag optimizing functions to 'cfg_t' struct namespace.
Ulya Trofimovich [Wed, 2 Nov 2016 18:27:27 +0000 (18:27 +0000)]
Moved tag optimizing functions to 'cfg_t' struct namespace.

8 years agoMoved tag optimization stuff to subdirectory.
Ulya Trofimovich [Wed, 2 Nov 2016 17:53:33 +0000 (17:53 +0000)]
Moved tag optimization stuff to subdirectory.

8 years agoSlab allocator for tag commands.
Ulya Trofimovich [Wed, 2 Nov 2016 17:20:27 +0000 (17:20 +0000)]
Slab allocator for tag commands.

8 years agoDisallow accidental change of tag commands that share representation.
Ulya Trofimovich [Wed, 2 Nov 2016 15:31:07 +0000 (15:31 +0000)]
Disallow accidental change of tag commands that share representation.

After inserting commands into common index some of them may share
representations in memory. Initially commands are stored in mutable
lists, which is very convenient for tag optimizations. However,
leaving this representation after indexing is dangerous, because
accidental modification of one command may affect other commands.

This commit hides mutable lists behind indices in hash table.
Table gives read-only access to the lists.

Table index is 32-bit, which is smaller than the previous 'index'
type (a pair of pointers). So in theory command comparison has
become a little faster.

8 years agoPre-calculate initial liveness of final tag versions used in rules.
Ulya Trofimovich [Mon, 31 Oct 2016 17:47:48 +0000 (17:47 +0000)]
Pre-calculate initial liveness of final tag versions used in rules.

8 years agoRestricted 'Tagpool' lifetime to DFA construction phase.
Ulya Trofimovich [Mon, 31 Oct 2016 15:59:57 +0000 (15:59 +0000)]
Restricted 'Tagpool' lifetime to DFA construction phase.

8 years agoBuild control flow graph of DFA before tag optimizations.
Ulya Trofimovich [Mon, 31 Oct 2016 14:45:45 +0000 (14:45 +0000)]
Build control flow graph of DFA before tag optimizations.

DFA may be very large, but have only few tagged transitions.
In such cases doing tag optimizations on the whole DFA is a waste:
space aloocations and (respectively) processing time is proportional
to the number of all DFA transitions.

Instead, we spend a little effort to create CFG. It has one basic
block per each tagged transition or tagged final state, plus root
basic block.

8 years agoUpdate struct field instead of passing return value.
Ulya Trofimovich [Mon, 31 Oct 2016 13:44:43 +0000 (13:44 +0000)]
Update struct field instead of passing return value.

8 years agoAdded comment.
Ulya Trofimovich [Thu, 27 Oct 2016 14:34:56 +0000 (15:34 +0100)]
Added comment.

8 years agoConstified comparison predicate in lookup table.
Ulya Trofimovich [Thu, 27 Oct 2016 13:56:06 +0000 (14:56 +0100)]
Constified comparison predicate in lookup table.

8 years agoUse linked lists instead of arrays for 'save tag' commands.
Ulya Trofimovich [Thu, 27 Oct 2016 13:29:28 +0000 (14:29 +0100)]
Use linked lists instead of arrays for 'save tag' commands.

Fixed-length arrays are widely used during DFA construction, where
we need to keep all versions of all tags in each NFA sub-state of the
DFA state under construction. These arrays are deduplicated, indexed
and stored in a hash table.

However, after determinization we deal with small (probably empty)
sets of tag versions that are updated on a given transition.
Corresponding arrays are sparse. We don't care about storage space
(arrays are deduplicated anyway), but traversing sparse arrays is
wasteful and the code is somewhat bloated.

We already used linked lists for 'copy' commands; now we also use
them for 'save' commands.