Compare commits

...
25 Commits
Author SHA1 Message Date
Ulya Trofimovich 349a97c831 CMake: create output directory before building docs.
Previously trying to run rst2man.py with an output file in a nonexistent
directory resulted in the following build error:

  $ cmake --preset=linux-gcc-debug-ootree-skeleton-full
  $ cmake --build --preset=linux-gcc-debug-ootree-skeleton-full
  ...
  [  1%] Generating src/msg/help.cc
  Unable to open destination file for writing:
    OutputError: [Errno 2] No such file or directory:
      '[...]/linux-gcc-debug-ootree-skeleton-full/src/msg/help.cc.1'
2021-11-28 16:38:51 +00:00
Ulya Trofimovich e07052b566 CMakePresets.json: inherit "full" configuration before "fast".
Otherwise variable definitions from "fast" configuration take precedence
over the "full" configuration, resulting in disabling some important
configrations, such as Go language support.

For example, if "fast" is inherited before "full" we have the following
incorrect configrations that sets RE2C_BUILD_RE2GO:BOOL="FALSE":

  $ cmake -S .. --preset=linux-gcc-release-ootree-skeleton-full
  Preset CMake variables:

    CMAKE_BUILD_TYPE="Release"
    CMAKE_CXX_COMPILER="g++"
    CMAKE_C_COMPILER="gcc"
    CMAKE_INSTALL_PREFIX:PATH="[CUT]/re2c/install"
    RE2C_BUILD_LIBS:BOOL="TRUE"
    RE2C_BUILD_RE2GO:BOOL="FALSE"
    RE2C_FOR_BUILD="[CUT]/re2c/install/bin/re2c"
    RE2C_REBUILD_DOCS:BOOL="TRUE"
    RE2C_REBUILD_LEXERS:BOOL="TRUE"

But if "full" is inherited after "fast" we have the following (correct):

  $ cmake -S .. --preset=linux-gcc-release-ootree-skeleton-full
  Preset CMake variables:

    CMAKE_BUILD_TYPE="Release"
    CMAKE_CXX_COMPILER="g++"
    CMAKE_C_COMPILER="gcc"
    CMAKE_INSTALL_PREFIX:PATH="[CUT]/re2c/install-2"
    RE2C_BUILD_LIBS:BOOL="TRUE"
    RE2C_BUILD_RE2GO:BOOL="TRUE"
    RE2C_FOR_BUILD="[CUT]/re2c/install/bin/re2c"
    RE2C_REBUILD_DOCS:BOOL="TRUE"
    RE2C_REBUILD_LEXERS:BOOL="TRUE"
2021-11-28 16:38:51 +00:00
Ulya Trofimovich 8a4772cee8 Make --loop-switch work with conditions.
Merge all conditions in one loop/switch where DFA for each condition has
its own set of cases. Use `yystate` variable to switch between
conditions in the same way as between DFA states.

The => rules set user-defined condition variable with YYSETCONDITION.
The :=> rules set `yystate` and continue to the head of the loop/switch.
2021-11-27 00:05:53 +00:00
Ulya Trofimovich b031e27437 Respect define:YYGETSTATE:naked configuration in loop/switch mode. 2021-11-26 23:15:51 +00:00
Ulya Trofimovich c643db456c For skeleton and .dot targets merge header file into the output file.
Header file may contain blocks with regular expressions and rules. They
should be handled as well as the blocks in the output file.

This patch fixed a failing skeleton test.
2021-11-22 23:53:15 +00:00
Ulya Trofimovich dcbbd01055 Use initial state indices as condition numbers with --loop-switch.
Normally with the goto/label approach condition enumeration uses
implicit consecutive indices starting from zero. With the loop/switch
approach it is more convenient to represent conditions with their
initial DFA state index. This allows one to assign condition numbers to
the state variable `yystate` and get rid of the entry condition switch.
2021-11-21 21:41:26 +00:00
Ulya Trofimovich d2cf8a37e9 Factor out block list construction into a separate function. 2021-11-21 20:42:02 +00:00
Ulya Trofimovich c5179da5f8 Delay YYMAXFILL / YYMAXNMATCH computation until handling the directive.
The `maxfill:re2c` and `maxnmatch:re2c` directives may specify an
optional list of block names. If the list is specified, the directives
compute maximum value over the specified blocks, otherwise they compute
global maximum across all blocks in the program.

Previously the global maximum was prepared in advance even if the
directives were not used. This patch delays the computation of global
YYMAXFILL / YYMAXNMATCH until they are needed, as is already done in
the case when the directive specifies a list of blocks.
2021-11-21 17:57:20 +00:00
Ulya Trofimovich 4edb6ca2d9 Delay global tag list construction until handling the directive.
The `stags:re2c` and `mtags:re2c` directives may specify an optional
list of block names. If the list is specified, the directives gather
condition nems from the specified blocks, otherwise they use a global
set of tag names gathered across all blocks.

Previously the global set was prepared in advance even if the directives
were not used, but a set for specific block list was constructed only if
needed. This patch delays global set construction until it is needed and
unifies it with the case of a specific block list.
2021-11-21 10:36:35 +00:00
Ulya Trofimovich 8b93728f93 Do not autogenerate condition enum if there is an explicit types:re2c.
The rationale is, if the user specified an explicit `types:re2c`
directive, they probably do not want the autogenerated enum as well. If
for some reason they need multiple enum definitions (either in the
output file or in the header file), they can use multiple `types:re2c`
directives.
2021-11-21 10:04:13 +00:00
Ulya Trofimovich e551a4918b Search for named blocks both in the output file and in header.
This fixes a test for condition enum: previously re2c emitted an error
because one of the `types:re2c` directives listed block name for a block
defined in another file, and re2c failed to find it. Now it finds it.
2021-11-21 09:44:50 +00:00
Ulya Trofimovich 51f769133b Do not generate empty condition enum.
Some compilers or language standards allow it, but generally it's more
likely to indicate an error in user code.

Fix a few tests that generated empty condition enum by mistake.
2021-11-21 09:41:31 +00:00
Ulya Trofimovich df50c25fc3 Clean up old tests for condition enum and header.
Replace a bunch of simple and similar tests with a few more
comprehensive ones that cover recent re2c features like named blocks and
block lists in the `types:re2c` directive, in addition to older features
such as the --type-header option.

One of the new tests reveals an error in re2c: it fails to find
conditions in header. The error will be fixed in a follow-up commit.
2021-11-21 09:41:31 +00:00
Sergei Trofimovich ac672c9ce4 avoid using reserved identifiers (underscores)
clang with -Weverything detects these as:

    ./src/util/free_list.h:9:16:
      warning: identifier '_Ty' is reserved
      because it starts with '_' followed by a capital letter [-Wreserved-identifier]
    template<class _Ty>
                   ^
2021-11-17 22:52:30 +00:00
Sergei Trofimovich 59edd763bf fix struct/class declaration mismatch (-Wmismatched-tags)
Detected by clang's-Weverything:

    ./src/regexp/re.h:17:1: warning: struct 'Msg' was previously declared as a class;
      this is valid, but may result in linker errors under the Microsoft C++ ABI [-Wmismatched-tags]
    struct Msg;
    ^
2021-11-17 22:52:02 +00:00
Ulya Trofimovich 66c6b1cd96 Make --loop-switch work with storable state.
With the loop/switch approach there is no `goto` statement, so it is
impossible to jump from a `getstate:re2c` block to an YYFILL label,
bypassing all the user-defined code in the middle (as it normally
happens with the goto/label approach).

Therefore `getstate:re2c` is forbidden with --loop-switch, and the
initial switch on storable state is merged into the `yystate` switch:

  - case -1 falls through to case 0 (the initial state)

  - transitions to YYFILL states are moved to cases that occupy
    contiguous numbers after the last regular DFA state

In order to make it possible to wrap the generated lexer in a
user-defined loop, an `YYSETSTATE(1)` statement is emitted in each final
state before the user-defined semantic action: if the action jumps to
the head of the outer loop, the next iteration will start from state -1,
as the user might expect. Note that `YYSETSTATE(1)` is not needed with
goto/label approach, because one can jump directly to the start label.
2021-11-17 21:26:48 +00:00
Ulya Trofimovich e42b3db26a Make --loop-switch and --eager-skip options non-configurable.
Having configuration for the --loop-switch option is only useful if one
needs to enable loop/switch approach on a per-block basis. Mixing the
two approaches (goto/label and loop/switch) would mean that some of the
blocks have start labels and can use cross-block transitions, while
others cannot. This would create confusion for whole-program directives
like `getstate:re2c` that cover multiple blocks.

With the loop/switch approach `getstate:re2c` should be forbidden, as it
requires a cross-block transition from the state switch to the lexer
entry, which is unsupported in the absence of the `goto` statement.
2021-11-16 07:32:01 +00:00
Ulya Trofimovich 1def5f1874 With --loop-switch, start state enumeration from zero in each block.
Normally with the goto/label approach re2c uses a program-wide state
enumeration that starts from zero and increases with each block. Since
each state has a label, global enumeration allows one to have multiple
blocks in the same function without the risk of label collision.
Moreover, different blocks can be connected with cross-block `goto`
transitions, so all state labels should have different numbers.

The situation is different with the loop/switch approach: there are no
labels and no cross-block `goto` transtions; instead every state has a
switch on the `yystate` variable. Since different block have different
switches, it makes sense to start state enumeration from zero in each
block (so that switch cases start from zero, and the zeroth case
corresponds to the start state of every block).
2021-11-14 18:54:37 +00:00
Ulya Trofimovich 29d6adcee1 Drop useless -c option in some of the Go examples.
These examples do not use conditions, so -c does nothing. It has been
accidentally copy-pasted from another example that uses conditions.
2021-11-09 23:08:44 +00:00
Ulya Trofimovich 81feecfbfe Add tests for the new --loop-switch option (for C and Go backends).
The added tests are copy-pasted from examples, but the generated code is
different: it uses loop/switch constructs instead of goto/label. All
tests except for conditions and storable state are valid programs that
can be compiled and run without errors.
2021-11-09 23:02:21 +00:00
Raekye a03b3b4d54 Initial implementation of the --loop-switch option.
With this option, instead of the usual goto/label approach DFA is
wrapped in a loop with a `switch` statement on `yystate` variable. Each
DFA state is an individual `case`. Transitions between states are done
by setting the `yystate` variable to the numeric index of the target
state and executing `continue` that jumps to the head of the loop and
goes via the `switch` again. This is less efficient than direct jumps
with the goto/label approach, but it is the only way we can implement a
DFA for languages that do not have `goto` statement (such as Rust).

YYFILL labels are problematic for the loop/switch approach, because they
are normally in the middle of a DFA state (after the YYSKIP operation,
but before YYPEEK and transitions to other states). To fix this, we use
the --eager-skip option that moves YYSKIP operations from DFA states to
transitions.

The initial value of `yystate` is the numeric index (label) of the first
DFA state. It is _not_ the intiial label, because initial label does not
have a state (so there is no `case` in the `switch` for it), and it is
only needed to bypass YYSKIP operation in the first DFA state (which is
not there anyway because of the --eager-skip option).

Current limitations:
  - Conditions are not supported. We need a per-condition `yystate`, and
    ideally transitions between conditions should also use a loop/switch
    approach instead of goto/label (it will be required for Rust, but it
    is optional for C and Go).

  - Storable state is not supported. We need to rewrite user-defined
    code to use the same state variable as `yystate` and the one in
    `YYGETSTATE`/`YYSETSTATE`, as well as to put `getstate` block
    immediately before DFA entry (if there is any code between state
    dispatch and DFA entry, it needs to be bypassed when re-entering
    the lexer after YYFILL, which is easily done with goto/label
    approach in C and Go, but not in Rust that has no `goto`).

Test results with the --loop-switch option enabled by default:
  - Skeleton tests pass (320 soft errors).

  - Examples for C and Go compile and run without errors, except for
    conditions, storable state and cases when user-defined code needs to
    be rewritten (e.g. when it uses an outer loop and `continue`/`break`
    statements end up referring to the wrong loop).
2021-11-09 21:54:48 +00:00
Raekye 10f84e73ed Don't generate initial label if --eager-skip option is used.
The initial label is not needed in such cases as there is no need to
bypass YYSKIP statement (--eager-skip moves it from states to
transitions).
2021-11-09 21:54:48 +00:00
Raekye bddcb93fa9 Add stubs for --loop-switch option and related configurations.
The following has been added:
  - option --loop-switch
  - configuration `re2c:flags:loop-switch`
  - configuration `re2c:variable:yystate`

Currently they are no-ops.
2021-11-09 21:54:48 +00:00
Raekye 1c8451883f Move --eager-skip option from constant to mutable.
This is necessary to support the future --loop-switch option.

With --loop-switch we cannot have labels (goto targets) in the middle of
a state. This affects YYFILL labels which normally go after YYSKIP but
before YYPEEK. The --eager-skip option resolves the problem by moving
YYSKIP statements from states to transitions. The plan is to enforce it
automatically if --loop-switch is enabled, but since --loop-switch will
be a mutable option, --eager-skip also needs to be mutable.
2021-11-09 21:54:48 +00:00
Raekye e3e76b0fe4 Skeleton: use goto-loop instead of for-loop.
To support the future --loop-switch option, this change avoids
unexpected control flow with `continue`.

The skeleton tests are verified and updated.
2021-11-09 21:54:48 +00:00
131 changed files with 10833 additions and 3412 deletions
+11 -11
View File
@@ -53,7 +53,7 @@
{
"name": "linux-gcc-ubsan-ootree-full",
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (full)",
"inherits": ["linux-gcc-ubsan-ootree-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "linux-gcc-ubsan-ootree-fast"]
},
{
"name": "linux-gcc-asan-ootree-fast",
@@ -71,7 +71,7 @@
{
"name": "linux-gcc-asan-ootree-full",
"displayName": "Linux x86_64: GCC / Asan / Out of tree (full)",
"inherits": ["linux-gcc-asan-ootree-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "linux-gcc-asan-ootree-fast"]
},
{
"name": "linux-gcc-debug-ootree-skeleton-fast",
@@ -89,7 +89,7 @@
{
"name": "linux-gcc-debug-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (full)",
"inherits": ["linux-gcc-debug-ootree-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "linux-gcc-debug-ootree-skeleton-fast"]
},
{
"name": "linux-gcc-debug-intree-m32-skeleton-fast",
@@ -113,7 +113,7 @@
{
"name": "linux-gcc-debug-intree-m32-skeleton-full",
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (full)",
"inherits": ["linux-gcc-debug-intree-m32-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "linux-gcc-debug-intree-m32-skeleton-fast"]
},
{
"name": "linux-gcc-release-ootree-skeleton-fast",
@@ -129,7 +129,7 @@
{
"name": "linux-gcc-release-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (full)",
"inherits": ["linux-gcc-release-ootree-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "linux-gcc-release-ootree-skeleton-fast"]
},
@@ -167,7 +167,7 @@
{
"name": "macos-clang-debug-ootree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (full)",
"inherits": ["macos-clang-debug-ootree-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "macos-clang-debug-ootree-skeleton-fast"]
},
{
"name": "macos-clang-debug-intree-skeleton-fast",
@@ -185,7 +185,7 @@
{
"name": "macos-clang-debug-intree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (full)",
"inherits": ["macos-clang-debug-intree-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "macos-clang-debug-intree-skeleton-fast"]
},
{
"name": "macos-clang-release-ootree-skeleton-fast",
@@ -201,7 +201,7 @@
{
"name": "macos-clang-release-ootree-skeleton-full",
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (full)",
"inherits": ["macos-clang-release-ootree-skeleton-fast", "default-full-configuration"]
"inherits": ["default-full-configuration", "macos-clang-release-ootree-skeleton-fast"]
},
@@ -237,7 +237,7 @@
{
"name": "windows-msvc-debug-ootree-full",
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (full)",
"inherits": ["windows-msvc-debug-ootree-fast", "default-full-configuration"],
"inherits": ["default-full-configuration", "windows-msvc-debug-ootree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
@@ -256,7 +256,7 @@
{
"name": "windows-msvc-debug-intree-full",
"displayName": "Windows x86_64: MSVC / Debug / In tree (full)",
"inherits": ["windows-msvc-debug-intree-fast", "default-full-configuration"],
"inherits": ["default-full-configuration", "windows-msvc-debug-intree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
@@ -275,7 +275,7 @@
{
"name": "windows-msvc-release-ootree-full",
"displayName": "Windows x86_64: MSVC / Release / Out of tree (full)",
"inherits": ["windows-msvc-release-ootree-fast", "default-full-configuration"],
"inherits": ["default-full-configuration", "windows-msvc-release-ootree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
File diff suppressed because it is too large Load Diff
+415 -409
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
/* Generated by re2c 2.1.1 on Fri Jul 16 08:52:38 2021 */
/* Generated by re2c 2.2 on Mon Nov 22 21:59:42 2021 */
#ifndef _RE2C_PARSE_LEX_
#define _RE2C_PARSE_LEX_
File diff suppressed because it is too large Load Diff
+4
View File
@@ -1,8 +1,10 @@
function(re2c_gen_manpage source target bootstrap lang)
if(RE2C_REBUILD_DOCS)
get_filename_component(targetdir "${target}" DIRECTORY)
set(source_l "${source}.${lang}")
add_custom_command(
OUTPUT "${target}"
COMMAND "${CMAKE_COMMAND}" -E make_directory ${targetdir}
COMMAND "${re2c_splitman}" "${source}" "${source_l}" "${lang}"
COMMAND "${RST2MAN}" --tab-width=4 "${source_l}" "${target}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${target}" "${bootstrap}"
@@ -23,8 +25,10 @@ endfunction()
function(re2c_gen_help source target bootstrap)
if(RE2C_REBUILD_DOCS)
get_filename_component(targetdir "${target}" DIRECTORY)
add_custom_command(
OUTPUT "${target}"
COMMAND "${CMAKE_COMMAND}" -E make_directory ${targetdir}
COMMAND "${RST2MAN}" "${source}" "${target}.1"
COMMAND "${re2c_genhelp}" "${target}.1" "${target}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${target}" "${bootstrap}"
-3
View File
@@ -5,9 +5,6 @@
#include <assert.h>
static const uint64_t ERROR = ~0lu;
enum YYCONDTYPE {
};
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
@@ -4,7 +4,6 @@
#include <assert.h>
static const uint64_t ERROR = ~0lu;
/*!types:re2c*/
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go -c $INPUT -o $OUTPUT -i
//go:generate re2go $INPUT -o $OUTPUT -i
package main
import (
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate re2go -c $INPUT -o $OUTPUT -i
//go:generate re2go $INPUT -o $OUTPUT -i
package main
import (
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go -c $INPUT -o $OUTPUT -i
//go:generate re2go $INPUT -o $OUTPUT -i
package main
import "testing"
+1 -1
View File
@@ -1,4 +1,4 @@
//go:generate re2go -c $INPUT -o $OUTPUT -i
//go:generate re2go $INPUT -o $OUTPUT -i
package main
import "testing"
-4
View File
@@ -510,10 +510,6 @@ void DFA::calc_stats(OutputBlock &out)
out.stags.insert(stagnames.begin(), stagnames.end());
out.mtags.insert(mtagnames.begin(), mtagnames.end());
}
if (!cond.empty()) {
out.conds.push_back(cond);
}
}
static bool can_hoist_tags(const State *s, const opt_t *opts)
+1 -2
View File
@@ -21,7 +21,7 @@
namespace re2c {
class Msg;
class Output;
struct Output;
struct dfa_t;
struct opt_t;
struct Label;
@@ -130,7 +130,6 @@ struct DFA {
void reorder();
void prepare(const opt_t *opts);
void calc_stats(OutputBlock &out);
void emit_body(Output &output, CodeList *program) const;
void emit_dot(Output &output, CodeList *program) const;
private:
+37 -17
View File
@@ -9,6 +9,7 @@
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include "src/constants.h"
#include "src/dfa/tcmd.h"
@@ -16,7 +17,6 @@
#include "src/msg/location.h"
#include "src/util/forbid_copy.h"
#include "src/util/slab_allocator.h"
#include "src/util/uniq_vector.h"
#include "src/util/smart_ptr.h"
#include "src/util/string_utils.h"
@@ -30,8 +30,8 @@ struct opt_t;
struct Code;
struct CodeGo;
struct CodeGoIf;
class Output;
class OutputBlock;
struct Output;
struct OutputBlock;
struct State;
struct DFA;
typedef std::vector<smart_ptr<DFA> > dfas_t;
@@ -315,6 +315,7 @@ struct Code {
CodeVar var;
CodeFmt fmt;
CodeLabel label;
CodeList *loop;
loc_t loc;
};
@@ -374,6 +375,13 @@ inline Code *code_stmt(code_alc_t &alc, const char *text)
return x;
}
inline Code *code_loop(code_alc_t &alc, CodeList *loop)
{
Code *x = new_code(alc, CODE_LOOP);
x->loop = loop;
return x;
}
inline Code *code_text(code_alc_t &alc, const char *text)
{
Code *x = new_code(alc, CODE_TEXT);
@@ -463,6 +471,11 @@ inline Code *code_yyaccept_def(code_alc_t &alc)
return new_code(alc, CODE_YYACCEPT);
}
inline Code *code_yystate_def(code_alc_t &alc)
{
return new_code(alc, CODE_YYSTATE);
}
inline Code *code_line_info_output(code_alc_t &alc)
{
return new_code(alc, CODE_LINE_INFO_OUTPUT);
@@ -628,12 +641,10 @@ struct CodegenCtxGlobal {
Scratchbuf &scratchbuf;
Msg &msg;
const opt_t *opts;
const blocks_t &cblocks;
const blocks_t &hblocks;
const blocks_t *pblocks;
uniq_vector_t<std::string> conds;
tagnames_t stags;
tagnames_t mtags;
size_t max_fill;
size_t max_nmatch;
mutable blocks_t tmpblocks; // temporary buffer for block lists in directives
const bool warn_cond_ord;
};
@@ -662,6 +673,14 @@ struct OutputFragment {
uint32_t indent;
};
struct StartCond {
std::string name;
uint32_t number;
};
typedef std::vector<StartCond> StartConds;
typedef std::map<uint32_t, CodeList*> storable_states_t;
struct OutputBlock {
InputBlockKind kind;
std::string name;
@@ -669,7 +688,7 @@ struct OutputBlock {
std::vector<OutputFragment> fragments;
bool used_yyaccept;
bool have_user_code;
std::vector<std::string> conds;
StartConds conds;
tagnames_t stags;
tagnames_t mtags;
const opt_t *opts;
@@ -678,25 +697,23 @@ struct OutputBlock {
size_t max_nmatch; // YYMAXNMATCH
// Used in the state switch (with `-f --storable-state` option).
Label *start_label; // label of the DFA start state
uint32_t fill_index_start; // start of YYFILL index range
uint32_t fill_index_end; // end of YYFILL index range (not included)
std::vector<CodeList*> fill_goto; // transitions to YYFILL states
Label *start_label; // label of the DFA start state
uint32_t fill_index; // next free index in YYFILL state enumeration
uint32_t fill_state; // index of the most recent YYFILL state
storable_states_t fill_goto; // transitions to YYFILL states
OutputBlock(InputBlockKind kind, const std::string &name, const loc_t &loc);
~OutputBlock();
FORBID_COPY(OutputBlock);
};
class Output {
struct Output {
blocks_t cblocks; /* .c file */
blocks_t hblocks; /* .h file */
blocks_t *pblocks; /* selector */
public:
uint32_t label_counter;
bool state_goto;
bool cond_enum_in_hdr;
bool cond_enum_autogen; // true unless an explicit `types:re2c` directive is used
bool cond_goto;
bool warn_condition_order;
bool need_header;
@@ -746,6 +763,9 @@ void gen_goto(Output &output, const DFA &dfa, CodeList *stmts, const State *from
const CodeJump &jump);
const char *gen_lessthan(Scratchbuf &o, const opt_t *opts, size_t n);
void gen_code(Output &output, dfas_t &dfas);
void gen_dfa_as_blocks_with_labels(Output &output, const DFA &dfa, CodeList *stmts);
void gen_dfa_as_switch_cases(Output &output, DFA &dfa, CodeCases *cases);
void wrap_dfas_in_loop_switch(Output &output, CodeList *stmts, CodeCases *cases);
void expand_pass_1(CodegenCtxPass1 &ctx, Code *code);
void expand_pass_2(CodegenCtxPass2 &ctx, Code *code);
+36 -31
View File
@@ -12,7 +12,7 @@
namespace re2c {
static CodeGoIf *code_goif(code_alc_t &alc, CodeGoIf::Kind kind, const Span *sp,
uint32_t nsp, State *next, bool skip, uint32_t eof);
uint32_t nsp, State *next, bool skip, uint32_t eof, const opt_t *opts);
static bool is_eof(uint32_t eof, uint32_t ub)
{
@@ -97,7 +97,7 @@ static CodeCmp *code_cmp(code_alc_t &alc, const char *cmp, uint32_t val)
}
static CodeGoIfB *code_goifb(code_alc_t &alc, const Span *s, uint32_t n, State *next,
bool skip, uint32_t eof)
bool skip, uint32_t eof, const opt_t *opts)
{
const uint32_t l = n / 2;
const uint32_t h = n - l;
@@ -106,13 +106,13 @@ static CodeGoIfB *code_goifb(code_alc_t &alc, const Span *s, uint32_t n, State *
CodeGoIfB *x = alc.alloct<CodeGoIfB>(1);
x->cond = code_cmp(alc, "<=", s[l - 1].ub - 1);
x->gothen = code_goif(alc, tkind, &s[0], l, next, skip, eof);
x->goelse = code_goif(alc, ekind, &s[l], h, next, skip, eof);
x->gothen = code_goif(alc, tkind, &s[0], l, next, skip, eof, opts);
x->goelse = code_goif(alc, ekind, &s[l], h, next, skip, eof, opts);
return x;
}
static void add_branch(CodeGoIfL *go, const CodeCmp *cond, State *to, State *next,
const Span &sp, bool skip, uint32_t eof)
const Span &sp, bool skip, uint32_t eof, const opt_t *opts)
{
CodeGoIfL::Branch &b = go->branches[go->nbranches++];
b.cond = cond;
@@ -121,11 +121,16 @@ static void add_branch(CodeGoIfL *go, const CodeCmp *cond, State *to, State *nex
b.jump.tags = sp.tags;
b.jump.skip = skip && consume(sp.to);
b.jump.eof = is_eof(eof, sp.ub);
b.jump.elide = !to;
// TODO: support transition elision with --loop-switch option. This requires
// used-label analysis before the start of code generation phase, because in Rust
// there is no fallthrough between cases and we can only elide a transition if we can
// elide the whole case, which is only possible if there are no other transitions into
// this state (so its label is unused).
b.jump.elide = !opts->loop_switch && !to;
}
static CodeGoIfL *code_goifl(code_alc_t &alc, const Span *s, uint32_t n, State *next,
bool skip, uint32_t eof)
bool skip, uint32_t eof, const opt_t *opts)
{
CodeGoIfL *x = alc.alloct<CodeGoIfL>(1);
x->nbranches = 0;
@@ -134,17 +139,17 @@ static CodeGoIfL *code_goifl(code_alc_t &alc, const Span *s, uint32_t n, State *
for (;;) {
if (n == 1 && s[0].to == next) {
add_branch(x, NULL, NULL, next, s[0], skip, eof);
add_branch(x, NULL, NULL, next, s[0], skip, eof, opts);
break;
}
else if (n == 1) {
add_branch(x, NULL, s[0].to, next, s[0], skip, eof);
add_branch(x, NULL, s[0].to, next, s[0], skip, eof, opts);
break;
}
else if (n == 2 && s[0].to == next) {
CodeCmp *cmp = code_cmp(alc, ">=", s[0].ub);
add_branch(x, cmp, s[1].to, next, s[1], skip, eof);
add_branch(x, NULL, NULL, next, s[0], skip, eof);
add_branch(x, cmp, s[1].to, next, s[1], skip, eof, opts);
add_branch(x, NULL, NULL, next, s[0], skip, eof, opts);
break;
}
else if (n == 3
@@ -153,8 +158,8 @@ static CodeGoIfL *code_goifl(code_alc_t &alc, const Span *s, uint32_t n, State *
&& s[2].to == s[0].to
&& s[2].tags == s[0].tags) {
CodeCmp *cmp = code_cmp(alc, "!=", s[0].ub);
add_branch(x, cmp, s[0].to, next, s[0], skip, eof);
add_branch(x, NULL, NULL, next, s[1], skip, eof);
add_branch(x, cmp, s[0].to, next, s[0], skip, eof, opts);
add_branch(x, NULL, NULL, next, s[1], skip, eof, opts);
break;
}
else if (n >= 3
@@ -162,13 +167,13 @@ static CodeGoIfL *code_goifl(code_alc_t &alc, const Span *s, uint32_t n, State *
&& s[2].to == s[0].to
&& s[2].tags == s[0].tags) {
CodeCmp *cmp = code_cmp(alc, "==", s[0].ub);
add_branch(x, cmp, s[1].to, next, s[1], skip, eof);
add_branch(x, cmp, s[1].to, next, s[1], skip, eof, opts);
n -= 2;
s += 2;
}
else {
CodeCmp *cmp = code_cmp(alc, "<=", s[0].ub - 1);
add_branch(x, cmp, s[0].to, next, s[0], skip, eof);
add_branch(x, cmp, s[0].to, next, s[0], skip, eof, opts);
n -= 1;
s += 1;
}
@@ -178,35 +183,35 @@ static CodeGoIfL *code_goifl(code_alc_t &alc, const Span *s, uint32_t n, State *
}
static CodeGoIf *code_goif(code_alc_t &alc, CodeGoIf::Kind kind, const Span *sp,
uint32_t nsp, State *next, bool skip, uint32_t eof)
uint32_t nsp, State *next, bool skip, uint32_t eof, const opt_t *opts)
{
CodeGoIf *x = alc.alloct<CodeGoIf>(1);
x->kind = kind;
if (kind == CodeGoIf::BINARY) {
x->goifb = code_goifb(alc, sp, nsp, next, skip, eof);
x->goifb = code_goifb(alc, sp, nsp, next, skip, eof, opts);
}
else {
x->goifl = code_goifl(alc, sp, nsp, next, skip, eof);
x->goifl = code_goifl(alc, sp, nsp, next, skip, eof, opts);
}
return x;
}
static CodeGoSwIf *code_goswif(code_alc_t &alc, const Span *sp, uint32_t nsp,
State *next, bool sflag, bool skip, uint32_t eof)
State *next, bool skip, uint32_t eof, const opt_t *opts)
{
CodeGoSwIf *x = alc.alloct<CodeGoSwIf>(1);
if ((!sflag && nsp > 2)
if ((!opts->sFlag && nsp > 2)
|| (nsp > 8 && (sp[nsp - 2].ub - sp[0].ub <= 3 * (nsp - 2)))) {
x->kind = CodeGoSwIf::SWITCH;
x->gosw = code_gosw(alc, sp, nsp, skip, eof);
}
else if (nsp > 5) {
x->kind = CodeGoSwIf::IF;
x->goif = code_goif(alc, CodeGoIf::BINARY, sp, nsp, next, skip, eof);
x->goif = code_goif(alc, CodeGoIf::BINARY, sp, nsp, next, skip, eof, opts);
}
else {
x->kind = CodeGoSwIf::IF;
x->goif = code_goif(alc, CodeGoIf::LINEAR, sp, nsp, next, skip, eof);
x->goif = code_goif(alc, CodeGoIf::LINEAR, sp, nsp, next, skip, eof, opts);
}
return x;
}
@@ -241,7 +246,7 @@ static uint32_t unmap(Span *new_span, const Span *old_span, uint32_t old_nspans,
static CodeGoBm *code_gobm(code_alc_t &alc, const Span *span, uint32_t nSpans,
const Span *hspan, uint32_t hSpans, const CodeBmState *bm, State *next,
bool sflag, uint32_t eof)
uint32_t eof, const opt_t *opts)
{
CodeGoBm *x = alc.alloct<CodeGoBm>(1);
x->bitmap = bm;
@@ -251,11 +256,11 @@ static CodeGoBm *code_gobm(code_alc_t &alc, const Span *span, uint32_t nSpans,
Span *bspan = allocate<Span>(nSpans); // temporary
uint32_t bSpans = unmap (bspan, span, nSpans, bm->state);
x->lgo = bSpans == 0 ? NULL
: code_goswif(alc, bspan, bSpans, next, sflag, false, eof);
: code_goswif(alc, bspan, bSpans, next, false, eof, opts);
// if there are any low spans, then next state for high spans
// must be NULL to trigger explicit goto generation in linear 'if'
x->hgo = hSpans == 0 ? NULL
: code_goswif(alc, hspan, hSpans, x->lgo ? NULL : next, sflag, false, eof);
: code_goswif(alc, hspan, hSpans, x->lgo ? NULL : next, false, eof, opts);
x->bitmap->state->label->used = true;
operator delete(bspan);
@@ -282,11 +287,11 @@ static CodeGoCpTable *code_gocp_table(code_alc_t &alc, const Span * span, uint32
}
static CodeGoCp *code_gocp(code_alc_t &alc, const Span *span, uint32_t nSpans,
const Span *hspan, uint32_t hSpans, State *next, bool sflag, uint32_t eof)
const Span *hspan, uint32_t hSpans, State *next, uint32_t eof, const opt_t *opts)
{
CodeGoCp *x = alc.alloct<CodeGoCp>(1);
x->hgo = hSpans == 0 ? NULL
: code_goswif(alc, hspan, hSpans, next, sflag, false, eof);
: code_goswif(alc, hspan, hSpans, next, false, eof, opts);
x->table = code_gocp_table(alc, span, nSpans);
return x;
}
@@ -356,18 +361,18 @@ void code_go(code_alc_t &alc, CodeGo *go, const State *from, const opt_t *opts,
&& !low_spans_have_tags) {
go->kind = CodeGo::CPGOTO;
go->gocp = code_gocp(alc, go->span, go->nspans, hspan, hSpans, from->next,
opts->sFlag, eof);
eof, opts);
}
else if (opts->bFlag && !part_skip && nBitmaps > 0) {
go->kind = CodeGo::BITMAP;
go->gobm = code_gobm(alc, go->span, go->nspans, hspan, hSpans, bm,
from->next, opts->sFlag, eof);
from->next, eof, opts);
bitmap->used = true;
}
else {
go->kind = CodeGo::SWITCH_IF;
go->goswif = code_goswif(alc, go->span, go->nspans, from->next, opts->sFlag,
part_skip, eof);
go->goswif = code_goswif(alc, go->span, go->nspans, from->next,
part_skip, eof, opts);
}
}
+233 -120
View File
@@ -9,18 +9,44 @@
namespace re2c {
static const OutputBlock *find_block_with_name(CodegenCtxPass1 &ctx,
const char *name, const char *where)
static OutputBlock *find_block_with_name(CodegenCtxPass1 &ctx, const char *name)
{
const blocks_t &blocks = *ctx.global->pblocks;
for (size_t i = 0; i < blocks.size(); ++i) {
const OutputBlock *b = blocks[i];
if (b->name.compare(name) == 0) return b;
const blocks_t &cblocks = ctx.global->cblocks;
for (size_t i = 0; i < cblocks.size(); ++i) {
if (cblocks[i]->name.compare(name) == 0) return cblocks[i];
}
const blocks_t &hblocks = ctx.global->hblocks;
for (size_t i = 0; i < hblocks.size(); ++i) {
if (hblocks[i]->name.compare(name) == 0) return hblocks[i];
}
error("cannot find block '%s' listed in `%s` directive", name, where);
return NULL;
}
static void find_blocks(CodegenCtxPass1 &ctx, const BlockNameList *names,
blocks_t &blocks, const char *directive)
{
blocks.clear();
for (const BlockNameList *p = names; p; p = p->next) {
OutputBlock *b = find_block_with_name(ctx, p->name);
if (b) {
blocks.push_back(b);
} else {
error("cannot find block '%s' listed in `%s` directive", p->name, directive);
exit(1);
}
}
}
static std::string output_cond_get(const opt_t *opts)
{
return opts->cond_get + (opts->cond_get_naked ? "" : "()");
}
static std::string output_state_get(const opt_t *opts)
{
return opts->state_get + (opts->state_get_naked ? "" : "()");
}
void gen_tags(Scratchbuf &buf, const opt_t *opts, Code *code, const tagnames_t &tags)
{
DASSERT(code->kind == CODE_STAGS || code->kind == CODE_MTAGS);
@@ -49,6 +75,18 @@ void gen_tags(Scratchbuf &buf, const opt_t *opts, Code *code, const tagnames_t &
code->raw.data = buf.flush();
}
static void add_tags_from_blocks(const blocks_t &blocks, tagnames_t &tags, bool multival)
{
for (size_t i = 0; i < blocks.size(); ++i) {
const OutputBlock &block = *blocks[i];
if (multival) {
tags.insert(block.mtags.begin(), block.mtags.end());
} else {
tags.insert(block.stags.begin(), block.stags.end());
}
}
}
static void expand_tags_directive(CodegenCtxPass1 &ctx, Code *code)
{
DASSERT(code->kind == CODE_STAGS || code->kind == CODE_MTAGS);
@@ -58,31 +96,24 @@ static void expand_tags_directive(CodegenCtxPass1 &ctx, Code *code)
}
Scratchbuf &buf = ctx.global->scratchbuf;
bool oneval = (code->kind == CODE_STAGS);
bool multival = (code->kind == CODE_MTAGS);
tagnames_t tags;
if (code->fmt.block_names == NULL) {
// Use the global set of tags accumulated from all blocks.
gen_tags(buf, ctx.block->opts, code,
oneval ? ctx.global->stags : ctx.global->mtags);
// Gather tags from all blocks in the output and header files.
add_tags_from_blocks(ctx.global->cblocks, tags, multival);
add_tags_from_blocks(ctx.global->hblocks, tags, multival);
} else {
// Gather tags from the blocks on the list.
const char *directive = oneval ? "stags:re2c" : "mtags:re2c";
tagnames_t tags;
for (BlockNameList *p = code->fmt.block_names; p; p = p->next) {
const OutputBlock *b = find_block_with_name(ctx, p->name, directive);
if (!b) exit(1);
if (oneval) {
tags.insert(b->stags.begin(), b->stags.end());
} else {
tags.insert(b->mtags.begin(), b->mtags.end());
}
}
gen_tags(buf, ctx.block->opts, code, tags);
const char *directive = multival ? "mtags:re2c" : "stags:re2c";
find_blocks(ctx, code->fmt.block_names, ctx.global->tmpblocks, directive);
add_tags_from_blocks(ctx.global->tmpblocks, tags, multival);
}
gen_tags(buf, ctx.block->opts, code, tags);
}
static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code,
const opt_t *opts, const uniq_vector_t<std::string> &conds)
static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code, const opt_t *opts,
const StartConds &conds)
{
DASSERT(opts->target == TARGET_CODE);
@@ -93,10 +124,11 @@ static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code,
for (size_t i = 0; i < conds.size(); ++i) {
if (i > 0 && sep) buf.cstr(sep);
std::ostringstream s(fmt);
// The main substitution (the one allowing unnamed sigil) must go
// last, or else it will erroneously substitute all the named ones.
argsubst(s, opts->api_sigil, "num", false, i);
argsubst(s, opts->api_sigil, "cond", true, conds[i]);
// The main substitution (the one allowing unnamed sigil) must go last, or
// else it will erroneously substitute all the named ones.
size_t cid = opts->loop_switch ? conds[i].number : i;
argsubst(s, opts->api_sigil, "num", false, cid);
argsubst(s, opts->api_sigil, "cond", true, conds[i].name);
buf.str(s.str());
}
buf.cstr("\n");
@@ -106,7 +138,7 @@ static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code,
code->raw.data = buf.flush();
} else {
const char *text, *start = NULL, *end = NULL;
const char *start = NULL, *end = NULL;
CodeList *stmts = code_list(alc);
CodeList *block = code_list(alc);
@@ -114,15 +146,23 @@ static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code,
start = buf.cstr("enum ").str(opts->yycondtype).cstr(" {").flush();
end = "};";
for (size_t i = 0; i < conds.size(); ++i) {
text = buf.str(conds[i]).cstr(",").flush();
append(block, code_text(alc, text));
buf.str(conds[i].name);
if (opts->loop_switch) {
buf.cstr(" = ").u32(conds[i].number);
}
append(block, code_text(alc, buf.cstr(",").flush()));
}
} else if (opts->lang == LANG_GO) {
start = buf.cstr("const (").flush();
end = ")";
for (size_t i = 0; i < conds.size(); ++i) {
text = buf.str(conds[i]).cstr(i == 0 ? " = iota" : "").flush();
append(block, code_text(alc, text));
buf.str(conds[i].name);
if (opts->loop_switch) {
buf.cstr(" = ").u32(conds[i].number);
} else if (i == 0) {
buf.cstr(" = iota");
}
append(block, code_text(alc, buf.flush()));
}
}
@@ -136,6 +176,43 @@ static void gen_cond_enum(Scratchbuf &buf, code_alc_t &alc, Code *code,
}
}
static void add_condition_from_block(const OutputBlock &block, StartConds &conds,
const StartCond &cond)
{
// Condition prefix is specific to the block that defines it. If a few blocks define
// conditions with the same name, but a different prefix, they should have different
// enum entries.
StartCond sc = cond;
sc.name = block.opts->condEnumPrefix + sc.name;
for (size_t i = 0; i < conds.size(); ++i) {
if (conds[i].name == sc.name) {
if (conds[i].number == sc.number) {
// A duplicate condition, it's not an error but don't add it.
return;
} else {
// An error: conditions with idetical names but different numbers.
error("cannot generate condition enumeration: conditon '%s' has "
"different numbers in different blocks (use `re2c:condenumprefix` "
"configuration to set per-block prefix)", sc.name.c_str());
exit(1);
}
}
}
conds.push_back(sc);
}
static void add_conditions_from_blocks(const blocks_t &blocks, StartConds &conds)
{
for (size_t i = 0; i < blocks.size(); ++i) {
const OutputBlock &block = *blocks[i];
for (size_t i = 0; i < block.conds.size(); ++i) {
add_condition_from_block(block, conds, block.conds[i]);
}
}
}
static void expand_cond_enum(CodegenCtxPass1 &ctx, Code *code)
{
Scratchbuf &buf = ctx.global->scratchbuf;
@@ -151,37 +228,34 @@ static void expand_cond_enum(CodegenCtxPass1 &ctx, Code *code)
return;
}
StartConds conds;
if (code->fmt.block_names == NULL) {
// Use the global set of conditions accumulated from all blocks.
gen_cond_enum(buf, alc, code, globopts, ctx.global->conds);
// Gather conditions from all blocks in the output and header files.
add_conditions_from_blocks(ctx.global->cblocks, conds);
add_conditions_from_blocks(ctx.global->hblocks, conds);
} else {
// Gather conditions from the blocks on the list.
uniq_vector_t<std::string> conds;
for (BlockNameList *p = code->fmt.block_names; p; p = p->next) {
const OutputBlock *b = find_block_with_name(ctx, p->name, "types:re2c");
if (!b) exit(1);
for (size_t i = 0; i < b->conds.size(); ++i) {
// Condition prefix is specific to the block that defines it.
// If a few blocks define conditions with the same name, but a
// different prefix, they result in different enum entries.
conds.find_or_add(b->opts->condEnumPrefix + b->conds[i]);
}
}
gen_cond_enum(buf, alc, code, globopts, conds);
find_blocks(ctx, code->fmt.block_names, ctx.global->tmpblocks, "types:re2c");
add_conditions_from_blocks(ctx.global->tmpblocks, conds);
}
// Do not generate empty condition enum. Some compilers or language standards
// allow it, but generally it's more likely to indicate an error in user code.
if (conds.empty()) {
code->kind = CODE_EMPTY;
return;
}
gen_cond_enum(buf, alc, code, globopts, conds);
}
static void gen_state_goto_cases(CodegenCtxPass1 &ctx, CodeCases *cases,
const OutputBlock *block)
{
code_alc_t &alc = ctx.global->allocator;
uint32_t index_count = block->fill_index_end - block->fill_index_start;
DASSERT(index_count <= block->fill_goto.size());
for (uint32_t i = 0; i < index_count; ++i) {
append(cases, code_case_number(alc, block->fill_goto[i],
static_cast<int32_t>(i + block->fill_index_start)));
for (storable_states_t::const_iterator i = block->fill_goto.begin();
i != block->fill_goto.end(); ++i) {
append(cases, code_case_number(alc, i->second, static_cast<int32_t>(i->first)));
}
}
@@ -200,6 +274,10 @@ static void gen_state_goto(CodegenCtxPass1 &ctx, Code *code)
return;
}
// Loop/switch approach is handled differently (by appending special cases
// to the state switch).
DASSERT(!globopts->loop_switch);
Scratchbuf &o = ctx.global->scratchbuf;
code_alc_t &alc = ctx.global->allocator;
const char *text;
@@ -245,19 +323,18 @@ static void gen_state_goto(CodegenCtxPass1 &ctx, Code *code)
}
} else {
// Generate a switch for all specified named blocks.
for (BlockNameList *p = code->fmt.block_names; p; p = p->next) {
const OutputBlock *b = find_block_with_name(ctx, p->name, "getstate:re2c");
if (!b) exit(1);
blocks_t &blocks = ctx.global->tmpblocks;
find_blocks(ctx, code->fmt.block_names, blocks, "getstate:re2c");
for (size_t i = 0; i < blocks.size(); ++i) {
const OutputBlock *b = blocks[i];
if (!b->start_label) {
error("block '%s' does not generate code, so it should not be"
" listed in `getstate:re2c` directive", p->name);
" listed in `getstate:re2c` directive", b->name.c_str());
exit(1);
}
gen_state_goto_cases(ctx, cases, b);
// Use start label of the first block on the list.
if (!bstart) bstart = b;
gen_state_goto_cases(ctx, cases, b);
}
}
@@ -283,7 +360,7 @@ static void gen_state_goto(CodegenCtxPass1 &ctx, Code *code)
}
CodeList *stmts = code_list(alc);
text = o.str(globopts->state_get).cstr(globopts->state_get_naked ? "" : "()").flush();
text = o.str(output_state_get(globopts)).flush();
append(stmts, code_switch(alc, text, cases));
if (globopts->bUseStateNext) {
@@ -322,6 +399,45 @@ static void gen_yyaccept_def(const opt_t *opts, Code *code, bool used_yyaccept)
}
}
static void gen_yystate_def(CodegenCtxPass1 &ctx, Code *code)
{
const opt_t *opts = ctx.block->opts;
Scratchbuf &o = ctx.global->scratchbuf;
if (opts->loop_switch) {
code->kind = CODE_VAR;
code->var.name = o.str(opts->yystate).flush();
if (opts->fFlag) {
// With storable state `yystate` should be initialized to YYGETSTATE.
// Since there is a -1 case, `yystate` should have a signed type.
// If conditions are also used, YYGETSTATE takes priority over YYGETCONDITION,
// because the lexer may be reentered after an YYFILL invocation. In that case
// we use YYSETSTATE instead of YYSETCONDITION in the final states.
code->var.type = "int";
code->var.init = o.str(output_state_get(opts)).flush();
} else if (opts->cFlag) {
// Else with start conditions yystate should be initialized to YYGETCONDITION.
code->var.type = "unsigned int";
code->var.init = o.str(output_cond_get(opts)).flush();
} else {
// Else it should be the start DFA state (always case 0 with --loop-switch).
code->var.type = "unsigned int";
code->var.init = "0";
}
} else {
code->kind = CODE_EMPTY;
}
}
static size_t max_among_blocks(const blocks_t &blocks, size_t max, CodeKind kind)
{
for (size_t i = 0; i < blocks.size(); ++i) {
const OutputBlock &block = *blocks[i];
max = std::max(max, kind == CODE_MAXFILL ? block.max_fill : block.max_nmatch);
}
return max;
}
static void gen_yymax(CodegenCtxPass1 &ctx, Code *code)
{
const opt_t *opts = ctx.block->opts;
@@ -332,22 +448,19 @@ static void gen_yymax(CodegenCtxPass1 &ctx, Code *code)
return;
}
bool is_maxfill = (code->kind == CODE_MAXFILL);
const char *dirname = is_maxfill ? "max:re2c" : "maxnmatch:re2c";
const char *varname = is_maxfill ? "YYMAXFILL" : "YYMAXNMATCH";
CodeKind kind = code->kind;
const char *dirname = kind == CODE_MAXFILL ? "max:re2c" : "maxnmatch:re2c";
const char *varname = kind == CODE_MAXFILL ? "YYMAXFILL" : "YYMAXNMATCH";
size_t max;
size_t max = 1;
if (code->fmt.block_names == NULL) {
// Global maximum in the file.
max = is_maxfill ? ctx.global->max_fill : ctx.global->max_nmatch;
// Gather max value from all blocks in the output and header files.
max = max_among_blocks(ctx.global->cblocks, max, kind);
max = max_among_blocks(ctx.global->hblocks, max, kind);
} else {
// Maximum among the blocks listed in the directive.
max = 0;
for (BlockNameList *p = code->fmt.block_names; p; p = p->next) {
const OutputBlock *b = find_block_with_name(ctx, p->name, dirname);
if (!b) exit(1);
max = std::max(max, is_maxfill ? b->max_fill : b->max_nmatch);
}
find_blocks(ctx, code->fmt.block_names, ctx.global->tmpblocks, dirname);
max = max_among_blocks(ctx.global->tmpblocks, max, kind);
}
if (code->fmt.format) {
@@ -388,65 +501,59 @@ static void gen_yymax(CodegenCtxPass1 &ctx, Code *code)
* dispatch shrinks to unconditional jump
*/
static std::string output_cond_get(const opt_t *opts)
static CodeList *gen_cond_goto_binary(CodegenCtxPass1 &ctx, size_t lower, size_t upper)
{
return opts->cond_get + (opts->cond_get_naked ? "" : "()");
}
const opt_t *opts = ctx.block->opts;
code_alc_t &alc = ctx.global->allocator;
Scratchbuf &o = ctx.global->scratchbuf;
static CodeList *gen_cond_goto_binary(Scratchbuf &o, code_alc_t &alc,
const std::vector<std::string> &conds, const opt_t *opts,
size_t lower, size_t upper)
{
CodeList *stmts = code_list(alc);
const char *text;
if (lower == upper) {
text = o.cstr("goto ").str(opts->condPrefix).str(conds[lower]).flush();
append(stmts, code_stmt(alc, text));
}
else {
o.cstr("goto ").str(opts->condPrefix).str(ctx.block->conds[lower].name);
append(stmts, code_stmt(alc, o.flush()));
} else {
const size_t middle = lower + (upper - lower + 1) / 2;
text = o.str(output_cond_get(opts)).cstr(" < ").u64(middle).flush();
CodeList *if_then = gen_cond_goto_binary(o, alc, conds, opts, lower, middle - 1);
CodeList *if_else = gen_cond_goto_binary(o, alc, conds, opts, middle, upper);
append(stmts, code_if_then_else(alc, text, if_then, if_else));
CodeList *if_then = gen_cond_goto_binary(ctx, lower, middle - 1);
CodeList *if_else = gen_cond_goto_binary(ctx, middle, upper);
o.str(output_cond_get(opts)).cstr(" < ").u64(middle);
append(stmts, code_if_then_else(alc, o.flush(), if_then, if_else));
}
return stmts;
}
static void gen_cond_goto(Scratchbuf &o, code_alc_t &alc, Code *code,
const std::vector<std::string> &conds, const opt_t *opts, Msg &msg,
bool warn_cond_ord, const loc_t &loc)
static void gen_cond_goto(CodegenCtxPass1 &ctx, Code *code)
{
const opt_t *opts = ctx.block->opts;
code_alc_t &alc = ctx.global->allocator;
Scratchbuf &o = ctx.global->scratchbuf;
const StartConds &conds = ctx.block->conds;
bool warn_cond_ord = ctx.global->warn_cond_ord;
const size_t ncond = conds.size();
CodeList *stmts = code_list(alc);
const char *text;
if (opts->target == TARGET_DOT) {
for (size_t i = 0; i < ncond; ++i) {
const std::string &cond = conds[i];
const std::string &cond = conds[i].name;
text = o.cstr("0 -> ").str(cond).cstr(" [label=\"state=").str(cond)
.cstr("\"]").flush();
append(stmts, code_text(alc, text));
}
}
else {
} else {
if (opts->gFlag) {
text = o.cstr("goto *").str(opts->yyctable).cstr("[")
.str(output_cond_get(opts)).cstr("]").flush();
append(stmts, code_stmt(alc, text));
}
else if (opts->sFlag) {
} else if (opts->sFlag) {
warn_cond_ord &= ncond > 1;
append(stmts, gen_cond_goto_binary(o, alc, conds, opts, 0, ncond - 1));
}
else {
append(stmts, gen_cond_goto_binary(ctx, 0, ncond - 1));
} else {
warn_cond_ord = false;
CodeCases *ccases = code_cases(alc);
for (size_t i = 0; i < ncond; ++i) {
const std::string &cond = conds[i];
const std::string &cond = conds[i].name;
CodeList *body = code_list(alc);
text = o.cstr("goto ").str(opts->condPrefix).str(cond).flush();
@@ -462,7 +569,7 @@ static void gen_cond_goto(Scratchbuf &o, code_alc_t &alc, Code *code,
// see note [condition order]
warn_cond_ord &= opts->header_file.empty();
if (warn_cond_ord) {
msg.warn.condition_order(loc);
ctx.global->msg.warn.condition_order(ctx.block->loc);
}
}
@@ -471,22 +578,23 @@ static void gen_cond_goto(Scratchbuf &o, code_alc_t &alc, Code *code,
code->block.fmt = CodeBlock::RAW;
}
static void gen_cond_table(Scratchbuf &o, code_alc_t &alc, Code *code,
const std::vector<std::string> &conds, const opt_t *opts)
static void gen_cond_table(CodegenCtxPass1 &ctx, Code *code)
{
const opt_t *opts = ctx.block->opts;
code_alc_t &alc = ctx.global->allocator;
Scratchbuf &o = ctx.global->scratchbuf;
const StartConds &conds = ctx.block->conds;
const size_t ncond = conds.size();
CodeList *stmts = code_list(alc);
const char *text;
text = o.cstr("static void *").str(opts->yyctable).cstr("[").u64(ncond)
.cstr("] = {").flush();
append(stmts, code_text(alc, text));
o.cstr("static void *").str(opts->yyctable).cstr("[").u64(ncond).cstr("] = {");
append(stmts, code_text(alc, o.flush()));
CodeList *block = code_list(alc);
for (size_t i = 0; i < ncond; ++i) {
text = o.cstr("&&").str(opts->condPrefix).str(conds[i]).cstr(",").flush();
append(block, code_text(alc, text));
o.cstr("&&").str(opts->condPrefix).str(conds[i].name).cstr(",");
append(block, code_text(alc, o.flush()));
}
append(stmts, code_block(alc, block, CodeBlock::INDENTED));
@@ -508,9 +616,6 @@ static void expand_pass_1_list(CodegenCtxPass1 &ctx, CodeList *stmts)
void expand_pass_1(CodegenCtxPass1 &ctx, Code *code)
{
const opt_t *opts = ctx.block->opts;
Scratchbuf &buf = ctx.global->scratchbuf;
code_alc_t &alc = ctx.global->allocator;
switch (code->kind) {
case CODE_BLOCK:
expand_pass_1_list(ctx, code->block.stmts);
@@ -524,6 +629,9 @@ void expand_pass_1(CodegenCtxPass1 &ctx, Code *code)
expand_pass_1_list(ctx, x->body);
}
break;
case CODE_LOOP:
expand_pass_1_list(ctx, code->loop);
break;
case CODE_STAGS:
case CODE_MTAGS:
expand_tags_directive(ctx, code);
@@ -538,15 +646,17 @@ void expand_pass_1(CodegenCtxPass1 &ctx, Code *code)
case CODE_YYACCEPT:
gen_yyaccept_def(opts, code, ctx.block->used_yyaccept);
break;
case CODE_YYSTATE:
gen_yystate_def(ctx, code);
break;
case CODE_COND_ENUM:
expand_cond_enum(ctx, code);
break;
case CODE_COND_GOTO:
gen_cond_goto(buf, alc, code, ctx.block->conds, opts,
ctx.global->msg, ctx.global->warn_cond_ord, ctx.block->loc);
gen_cond_goto(ctx, code);
break;
case CODE_COND_TABLE:
gen_cond_table(buf, alc, code, ctx.block->conds, opts);
gen_cond_table(ctx, code);
break;
case CODE_STATE_GOTO:
gen_state_goto(ctx, code);
@@ -618,6 +728,9 @@ void expand_pass_2(CodegenCtxPass2 &ctx, Code *code)
expand_pass_2_list(ctx, x->body);
}
break;
case CODE_LOOP:
expand_pass_2_list(ctx, code->loop);
break;
case CODE_LABEL:
gen_label(ctx.scratchbuf, ctx.opts, code);
break;
+138 -49
View File
@@ -40,7 +40,9 @@ static void emit_state(Output &output, const State *s, CodeList *stmts)
Scratchbuf &o = output.scratchbuf;
const char *text;
append(stmts, code_nlabel(alc, s->label));
if (!opts->loop_switch) {
append(stmts, code_nlabel(alc, s->label));
}
if (opts->dFlag && (s->action.type != Action::INITIAL)) {
text = o.str(opts->yydebug).cstr("(").label(*s->label).cstr(", ")
@@ -49,31 +51,84 @@ static void emit_state(Output &output, const State *s, CodeList *stmts)
}
}
void DFA::emit_body(Output &output, CodeList *stmts) const
static void gen_storable_state_cases(Output &output, CodeCases *cases)
{
OutputBlock &block = output.block();
const opt_t *opts = block.opts;
code_alc_t &alc = output.allocator;
if (!opts->fFlag || !opts->loop_switch) return;
// TODO: If `re2c:eof` is not used, some of these cases are redundant (they contain a
// single transition to the DFA state that has the corresponding YYFILL invocation).
for (storable_states_t::const_iterator i = block.fill_goto.begin();
i != block.fill_goto.end(); ++i) {
append(cases, code_case_number(alc, i->second, static_cast<int32_t>(i->first)));
}
CodeList *start_case = code_list(alc);
if (opts->lang == LANG_GO) {
append(start_case, code_stmt(alc, "fallthrough"));
}
prepend(cases, code_case_number(alc, start_case, -1));
}
void gen_dfa_as_blocks_with_labels(Output &output, const DFA &dfa, CodeList *stmts)
{
const opt_t *opts = output.block().opts;
code_alc_t &alc = output.allocator;
Scratchbuf &o = output.scratchbuf;
const char *text;
gen_settags(output, stmts, *this, tags0, false /* delayed */);
gen_settags(output, stmts, dfa, dfa.tags0, /*delayed*/ false);
// If DFA has transitions to initial state, then initial state
// has a piece of code that advances input position. Wee must
// skip it when entering DFA.
if (head->label->used) {
initial_label->used = true;
text = o.cstr("goto ").str(opts->labelPrefix).label(*initial_label).flush();
append(stmts, code_stmt(alc, text));
// If DFA has transitions into the initial state and --eager-skip option is not used,
// then the initial state must have a YYSKIP statement that must be bypassed when
// first entering the DFA. With --loop-switch that would be impossible, because there
// can be no transitions in the middle of a state.
DASSERT(!opts->loop_switch);
if (dfa.head->label->used && !opts->eager_skip) {
dfa.initial_label->used = true;
o.cstr("goto ").str(opts->labelPrefix).label(*dfa.initial_label);
append(stmts, code_stmt(alc, o.flush()));
}
for (State * s = head; s; s = s->next) {
for (State *s = dfa.head; s; s = s->next) {
emit_state(output, s, stmts);
emit_action(output, *this, s, stmts);
gen_go(output, *this, &s->go, s, stmts);
emit_action(output, dfa, s, stmts);
gen_go(output, dfa, &s->go, s, stmts);
}
}
void gen_dfa_as_switch_cases(Output &output, DFA &dfa, CodeCases *cases)
{
const opt_t *opts = output.block().opts;
code_alc_t &alc = output.allocator;
DASSERT(opts->loop_switch);
DASSERT(dfa.tags0 == TCID0);
for (State *s = dfa.head; s; s = s->next) {
CodeList *body = code_list(alc);
emit_state(output, s, body);
emit_action(output, dfa, s, body);
gen_go(output, dfa, &s->go, s, body);
append(cases, code_case_number(alc, body, static_cast<int32_t>(s->label->index)));
}
}
void wrap_dfas_in_loop_switch(Output &output, CodeList *stmts, CodeCases *cases)
{
const opt_t *opts = output.block().opts;
code_alc_t &alc = output.allocator;
DASSERT(opts->loop_switch);
CodeList* loop = code_list(alc);
gen_storable_state_cases(output, cases);
append(loop, code_switch(alc, opts->yystate.c_str(), cases));
append(stmts, code_loop(alc, loop));
}
void DFA::emit_dot(Output &output, CodeList *program) const
{
code_alc_t &alc = output.allocator;
@@ -143,28 +198,50 @@ void gen_code(Output &output, dfas_t &dfas)
const bool first = i == b;
DFA &dfa = *(*i);
if (first) {
if (opts->startlabel_force) {
// User-enforced start label.
dfa.start_label = new_label(alc, output.label_counter++);
dfa.start_label->used = true;
} else if (opts->fFlag) {
// Start label is needed in `-f` mode: it points to state 0 (the
// beginning of block, before condition dispatch in `-c` mode).
dfa.start_label = new_label(alc, output.label_counter++);
}
oblock.start_label = dfa.start_label;
if (opts->loop_switch && (first || opts->target == TARGET_SKELETON)) {
// With loop/switch there are no labels, and each block has its own state
// switch where all conditions are joined. Restart state counter from zero so
// that cases start from zero. With skeleton conditions are separate.
output.label_counter = 0;
}
// Initial label points to the beginning of the DFA (after condition
// dispatch in `-c` mode).
dfa.initial_label = new_label(alc, output.label_counter++);
dfa.head->action.set_initial();
if (!opts->loop_switch) {
if (first) {
if (opts->startlabel_force) {
// User-enforced start label.
dfa.start_label = new_label(alc, output.label_counter++);
dfa.start_label->used = true;
} else if (opts->fFlag) {
// Start label is needed in `-f` mode: it points to state 0 (the
// beginning of block, before condition dispatch in `-c` mode).
dfa.start_label = new_label(alc, output.label_counter++);
}
oblock.start_label = dfa.start_label;
}
// Initial label points to the beginning of the DFA (after condition
// dispatch in `-c` mode).
dfa.initial_label = new_label(alc, output.label_counter++);
dfa.head->action.set_initial();
}
for (State *s = dfa.head; s; s = s->next) {
s->label = new_label(alc, output.label_counter++);
}
// With loop/switch storable states need their own cases in the state switch, as
// they have some logic on top of transition to the state that invoked YYFILL.
// Give them continuous indices after the last state index.
if (opts->loop_switch && opts->fFlag) {
oblock.fill_index = output.label_counter;
}
if (!dfa.cond.empty()) {
// If loop/switch is used, condition numbers are the numeric indices of their
// initial DFA state. Otherwise we do not assign explicit numbers, and
// conditions are implicitly assigned consecutive numbers starting from zero.
StartCond sc = {dfa.cond, opts->loop_switch ? dfa.head->label->index : 0};
oblock.conds.push_back(sc);
}
if (opts->bFlag) {
dfa.bitmap = code_bitmap(alc, std::min(dfa.ubChar, 256u));
for (State *s = dfa.head; s; s = s->next) {
@@ -205,6 +282,7 @@ void gen_code(Output &output, dfas_t &dfas)
const char *text;
CodeList *program1 = code_list(alc);
CodeCases *cases = code_cases(alc);
for (i = b; i != e; ++i) {
const bool first = i == b;
DFA &dfa = *(*i);
@@ -220,6 +298,9 @@ void gen_code(Output &output, dfas_t &dfas)
append(program1, code_yych_decl(alc));
append(program1, code_yyaccept_def(alc));
}
if (first) {
append(program1, code_yystate_def(alc));
}
if (!is_cond_block && bms) {
append(program1, bms);
@@ -229,7 +310,7 @@ void gen_code(Output &output, dfas_t &dfas)
append(program1, code_cond_table(alc));
}
if (opts->fFlag && !output.state_goto) {
if (opts->fFlag && !output.state_goto && !opts->loop_switch) {
append(program1, code_state_goto(alc,
block_list_for_implicit_state_goto(alc, oblock)));
output.state_goto = true;
@@ -246,12 +327,11 @@ void gen_code(Output &output, dfas_t &dfas)
append(program1, code_slabel(alc, text));
}
if (is_cond_block && !output.cond_goto) {
append(program1, code_cond_goto(alc));
output.cond_goto = true;
}
if (is_cond_block && !dfa.cond.empty()) {
if (is_cond_block && !opts->loop_switch) {
if (!output.cond_goto) {
append(program1, code_cond_goto(alc));
output.cond_goto = true;
}
if (opts->condDivider.length()) {
o.str(opts->condDivider);
argsubst(o.stream(), opts->condDividerParam, "cond", true, dfa.cond);
@@ -261,20 +341,29 @@ void gen_code(Output &output, dfas_t &dfas)
append(program1, code_slabel(alc, text));
}
// generate code for DFA
CodeList *body = code_list(alc);
dfa.emit_body(output, body);
if (is_cond_block && bms) {
CodeList *block = code_list(alc);
append(block, bms);
append(block, body);
append(program1, code_block(alc, block, CodeBlock::WRAPPED));
}
else {
append(program1, body);
if (!opts->loop_switch) {
// In the goto/label mode, generate DFA states as blocks of code preceded
// with labels, and `goto` transitions between states.
CodeList *body = code_list(alc);
gen_dfa_as_blocks_with_labels(output, dfa, body);
if (is_cond_block && bms) {
CodeList *block = code_list(alc);
append(block, bms);
append(block, body);
append(program1, code_block(alc, block, CodeBlock::WRAPPED));
} else {
append(program1, body);
}
} else {
// In the loop/switch mode append all DFA states as cases of the `yystate`
// switch. Merge DFAs for different conditions together in one switch.
DASSERT(!bms);
gen_dfa_as_switch_cases(output, dfa, cases);
}
}
if (opts->loop_switch) {
wrap_dfas_in_loop_switch(output, program1, cases);
}
const bool prolog = (opts->fFlag && opts->gFlag)
|| (!opts->fFlag && (oblock.used_yyaccept || opts->bEmitYYCh))
+12 -42
View File
@@ -14,7 +14,6 @@
#include "src/util/file_utils.h"
#include "src/util/string_utils.h"
#include "src/util/temp_file.h"
#include "src/util/uniq_vector.h"
namespace re2c {
@@ -59,8 +58,8 @@ OutputBlock::OutputBlock(InputBlockKind kind, const std::string &name,
, max_fill(1)
, max_nmatch(1)
, start_label(NULL)
, fill_index_start(0)
, fill_index_end(0)
, fill_index(0)
, fill_state(0)
, fill_goto()
{}
@@ -75,7 +74,7 @@ Output::Output(Msg &msg)
, pblocks(&cblocks)
, label_counter(0)
, state_goto(false)
, cond_enum_in_hdr(false)
, cond_enum_autogen(true)
, cond_goto(false)
, warn_condition_order(true)
, need_header(false)
@@ -169,8 +168,7 @@ void Output::new_block(Opt &opts, InputBlockKind kind, const std::string &name,
b->opts = opts.snapshot();
b->fill_index_start = total_fill_index;
b->fill_index_end = total_fill_index;
b->fill_index = total_fill_index;
pblocks->push_back(b);
@@ -183,7 +181,7 @@ void Output::gather_info_from_block()
{
DASSERT(!pblocks->empty());
const OutputBlock *b = pblocks->back();
total_fill_index = b->fill_index_end;
total_fill_index = b->fill_index;
}
static void fix_first_block_opts(const blocks_t &blocks)
@@ -276,23 +274,6 @@ bool Output::emit_blocks(const std::string &fname, const CodegenCtxGlobal &globa
return true;
}
static void add_symbols(const OutputBlock &block, CodegenCtxGlobal &ctx)
{
DASSERT(!block.name.empty());
const std::string &condenumprefix = block.opts->condEnumPrefix;
for (size_t j = 0; j < block.conds.size(); ++j) {
ctx.conds.find_or_add(condenumprefix + block.conds[j]);
}
const tagnames_t &st = block.stags, &mt = block.mtags;
ctx.stags.insert(st.begin(), st.end());
ctx.mtags.insert(mt.begin(), mt.end());
ctx.max_fill = std::max(ctx.max_fill, block.max_fill);
ctx.max_nmatch = std::max(ctx.max_nmatch, block.max_nmatch);
}
bool Output::emit()
{
if (msg.warn.error()) return false;
@@ -302,42 +283,31 @@ bool Output::emit()
, scratchbuf
, msg
, total_opts
, cblocks
, hblocks
, /*pblocks*/ NULL
, /*conditions*/ uniq_vector_t<std::string>()
, /*stags*/ tagnames_t()
, /*mtags*/ tagnames_t()
, /*max_fill*/ 1
, /*max_nmatch*/ 1
, /*tmpblocks*/ blocks_t()
, warn_condition_order
};
// gather global data accumulated across all blocks and files
for (uint32_t i = 0; i < cblocks.size(); ++i) {
add_symbols (*cblocks[i], ctx);
}
for (uint32_t i = 0; i < hblocks.size(); ++i) {
add_symbols (*hblocks[i], ctx);
}
// global options are last block's options
const opt_t *opts = block().opts;
bool ok = true;
// emit .h file
// Emit header file.
if (!opts->header_file.empty() || need_header) {
// old-style -t, --type-headers usage implies condition generation
if (!ctx.conds.empty() && !this->cond_enum_in_hdr) {
if (opts->cFlag && this->cond_enum_autogen) {
// Old-style -t, --type-headers usage implies condition enum.
header_mode(true);
wdelay_stmt(0, code_newline(allocator));
wdelay_stmt(0, code_fmt(allocator, CODE_COND_ENUM, NULL, NULL, NULL));
header_mode(false);
}
ctx.pblocks = &hblocks;
ok &= emit_blocks(opts->header_file, ctx);
}
// emit .c file
// Emit output file.
ctx.pblocks = &cblocks;
ok &= emit_blocks(opts->output_file, ctx);
+107 -61
View File
@@ -226,54 +226,7 @@ void emit_accept(Output &output, CodeList *stmts, const DFA &dfa, const accept_t
append(stmts, code_switch(alc, text, cases));
}
void emit_rule(Output &output, CodeList *stmts, const DFA &dfa, size_t rule_idx)
{
const opt_t *opts = output.block().opts;
const Rule &rule = dfa.rules[rule_idx];
const SemAct *semact = rule.semact;
const std::string &cond = semact->cond;
code_alc_t &alc = output.allocator;
Scratchbuf &o = output.scratchbuf;
std::string s;
const char *text;
gen_fintags(output, stmts, dfa, rule);
if (opts->target == TARGET_SKELETON) {
emit_skeleton_action(output, stmts, dfa, rule_idx);
}
else {
if (!cond.empty() && dfa.cond != cond) {
o.str(opts->cond_set);
argsubst(o.stream(), opts->cond_set_arg, "cond", true,
opts->condEnumPrefix + cond);
if (opts->cond_set_naked) {
append(stmts, code_text(alc, o.flush()));
} else {
o.cstr("(").str(opts->condEnumPrefix).str(cond).cstr(")");
append(stmts, code_stmt(alc, o.flush()));
}
}
if (!semact->autogen) {
if (!dfa.setup.empty()) {
text = o.str(dfa.setup).flush();
append(stmts, code_text(alc, text));
}
append(stmts, code_line_info_input(alc, semact->loc));
text = o.str(semact->text).flush();
append(stmts, code_text(alc, text));
append(stmts, code_line_info_output(alc));
}
else if (!cond.empty()) {
o.str(opts->condGoto);
argsubst(o.stream(), opts->condGotoParam, "cond", true,
opts->condPrefix + cond);
append(stmts, code_text(alc, o.flush()));
}
}
}
static void gen_setstate(Output &output, CodeList *stmts, uint32_t fillidx)
static void gen_setstate(Output &output, CodeList *stmts, const char *fillidx)
{
const opt_t *opts = output.block().opts;
code_alc_t &alc = output.allocator;
@@ -284,11 +237,85 @@ static void gen_setstate(Output &output, CodeList *stmts, uint32_t fillidx)
if (opts->state_set_naked) {
append(stmts, code_text(alc, o.flush()));
} else {
o.cstr("(").u32(fillidx).cstr(")");
o.cstr("(").cstr(fillidx).cstr(")");
append(stmts, code_stmt(alc, o.flush()));
}
}
static void gen_setcondition(Output &output, CodeList *stmts, const char *cond)
{
const opt_t *opts = output.block().opts;
code_alc_t &alc = output.allocator;
Scratchbuf &o = output.scratchbuf;
o.str(opts->cond_set);
argsubst(o.stream(), opts->cond_set_arg, "cond", true, cond);
if (opts->cond_set_naked) {
append(stmts, code_text(alc, o.flush()));
} else {
o.cstr("(").cstr(cond).cstr(")");
append(stmts, code_stmt(alc, o.flush()));
}
}
void emit_rule(Output &output, CodeList *stmts, const DFA &dfa, size_t rule_idx)
{
const opt_t *opts = output.block().opts;
const Rule &rule = dfa.rules[rule_idx];
const SemAct *semact = rule.semact;
code_alc_t &alc = output.allocator;
Scratchbuf &o = output.scratchbuf;
gen_fintags(output, stmts, dfa, rule);
if (opts->target == TARGET_SKELETON) {
emit_skeleton_action(output, stmts, dfa, rule_idx);
return;
}
bool is_cond_block = !dfa.cond.empty();
const std::string &cond = semact->cond.empty() ? dfa.cond : semact->cond;
const char *next = is_cond_block
? o.str(opts->condEnumPrefix).str(cond).flush() : "0";
if (opts->loop_switch && opts->fFlag) {
// With loop/switch and storable state we need YYSETSTATE in the final state,
// because the user may enclose lexer in an outer loop and expect the next
// iteration to start from the initial DFA state. Since `yystate` is initialized
// to YYGETSTATE, we need to do YYSETSTATE before the user-defined action.
//
// If both storable state and start conditions are used, only one of YYGETSTATE
// and YYGETCONDITION can be used to initialize `yystate`. It must be YYGETSTATE
// because the lexer may be reentered after an YYFILL invocation. Consequently we
// need to use YYSETSTATE instead of YYSETCONDITION in the final state to match
// YYGETSTATE in `yystate` initialization.
gen_setstate(output, stmts, next);
} else if (is_cond_block && cond != dfa.cond) {
// Omit YYSETCONDITION if the current condition is the same as the new one.
gen_setcondition(output, stmts, next);
}
if (!semact->autogen) {
// User-defined semantic action.
if (!dfa.setup.empty()) append(stmts, code_text(alc, o.str(dfa.setup).flush()));
append(stmts, code_line_info_input(alc, semact->loc));
append(stmts, code_text(alc, o.str(semact->text).flush()));
append(stmts, code_line_info_output(alc));
} else if (opts->loop_switch) {
// Autogenerated action for the :=> rule, loop/switch mode: set `yystate` to the
// initial state of the next condition and continue to the head of the loop.
o.str(opts->yystate).cstr(" = ").cstr(next);
append(stmts, code_stmt(alc, o.flush()));
append(stmts, code_stmt(alc, "continue"));
} else {
// Autogenerated action for the :=> rule, goto/label mode: emit `cond:goto`
// configuration with `cond:goto@cond` replaced by the next condition label.
o.str(opts->condGoto);
argsubst(o.stream(), opts->condGotoParam, "cond", true, opts->condPrefix + cond);
append(stmts, code_text(alc, o.flush()));
}
}
static CodeList *gen_fill_falllback(Output &output, const DFA &dfa,
const State *from, const State *to)
{
@@ -330,8 +357,14 @@ static CodeList *gen_fill_falllback(Output &output, const DFA &dfa,
// go to fallback state
fallback->label->used = true;
append(fallback_trans, code_stmt(alc, output.scratchbuf.cstr("goto ")
.str(opts->labelPrefix).label(*fallback->label).flush()));
if (!opts->loop_switch) {
append(fallback_trans, code_stmt(alc, output.scratchbuf.cstr("goto ")
.str(opts->labelPrefix).label(*fallback->label).flush()));
} else {
append(fallback_trans, code_stmt(alc, output.scratchbuf.str(opts->yystate)
.cstr(" = ").label(*fallback->label).flush()));
append(fallback_trans, code_stmt(alc, "continue"));
}
}
else {
// Transition can be elided, because control flow "falls through" to an
@@ -346,7 +379,7 @@ static void gen_fill(Output &output, CodeList *stmts, const DFA &dfa,
{
const opt_t *opts = output.block().opts;
const bool eof_rule = opts->eof != NOEOF;
const uint32_t fillidx = output.block().fill_index_end - 1;
const uint32_t fillidx = output.block().fill_index - 1;
const size_t need = eof_rule ? 1 : from->fill;
code_alc_t &alc = output.allocator;
Scratchbuf &o = output.scratchbuf;
@@ -356,13 +389,19 @@ static void gen_fill(Output &output, CodeList *stmts, const DFA &dfa,
// Transition to YYFILL label from the initial state dispatch.
CodeList *goto_fill = code_list(alc);
const char *flabel = gen_fill_label(output, fillidx);
append(goto_fill, code_stmt(alc, o.cstr("goto ").cstr(flabel).flush()));
if (!opts->loop_switch) {
const char *flabel = gen_fill_label(output, fillidx);
append(goto_fill, code_stmt(alc, o.cstr("goto ").cstr(flabel).flush()));
} else {
o.str(opts->yystate).cstr(" = ").u32(output.block().fill_state);
append(goto_fill, code_stmt(alc, o.flush()));
append(goto_fill, code_stmt(alc, "continue"));
}
CodeList *fill = code_list(alc);
if (opts->fFlag) {
gen_setstate(output, eof_rule ? fill : stmts, fillidx);
gen_setstate(output, eof_rule ? fill : stmts, o.u32(fillidx).flush());
}
if (opts->fill_use) {
@@ -407,7 +446,7 @@ static void gen_fill(Output &output, CodeList *stmts, const DFA &dfa,
}
}
if (opts->fFlag) {
output.block().fill_goto.push_back(goto_fill);
output.block().fill_goto[fillidx] = goto_fill;
}
if (opts->fill_check && fill->head) {
@@ -429,7 +468,8 @@ void gen_fill_and_label(Output &output, CodeList *stmts, const DFA &dfa, const S
const bool need_fill_label = need_fill_on_trans || (need_fill_in_state && opts->fFlag);
if (need_fill_label) {
++output.block().fill_index_end;
++output.block().fill_index;
output.block().fill_state = s->label->index;
}
if (need_fill_in_state) {
@@ -445,8 +485,8 @@ void gen_fill_and_label(Output &output, CodeList *stmts, const DFA &dfa, const S
gen_settags(output, stmts, dfa, s->go.tags, opts->stadfa /* delayed */);
}
if (need_fill_label) {
const char *flabel = gen_fill_label(output, output.block().fill_index_end - 1);
if (need_fill_label && !opts->loop_switch) {
const char *flabel = gen_fill_label(output, output.block().fill_index - 1);
append(stmts, code_slabel(output.allocator, flabel));
}
}
@@ -475,8 +515,14 @@ void gen_goto(Output &output, const DFA &dfa, CodeList *stmts, const State *from
if (!jump.elide) {
jump.to->label->used = true;
text = o.cstr("goto ").str(opts->labelPrefix).label(*jump.to->label).flush();
append(stmts, code_stmt(alc, text));
if (!opts->loop_switch) {
text = o.cstr("goto ").str(opts->labelPrefix).label(*jump.to->label).flush();
append(stmts, code_stmt(alc, text));
} else {
text = o.str(opts->yystate).cstr(" = ").label(*jump.to->label).flush();
append(stmts, code_stmt(alc, text));
append(stmts, code_stmt(alc, "continue"));
}
}
else {
// Goto can be elided, because control flow "falls through" to the
+29
View File
@@ -334,6 +334,31 @@ static void render_func(RenderContext &rctx, const CodeFunc *func)
++rctx.line;
}
static void render_loop(RenderContext &rctx, const CodeList *loop)
{
std::ostringstream &os = rctx.os;
const opt_t *opts = rctx.opts;
os << indent(rctx.ind, opts->indString);
switch (opts->lang) {
case LANG_C:
os << "for (;;) {";
break;
case LANG_GO:
os << "for {";
break;
}
os << std::endl;
++rctx.line;
++rctx.ind;
render_list(rctx, loop);
--rctx.ind;
os << indent(rctx.ind, opts->indString) << "}" << std::endl;
++rctx.line;
}
static inline void yych_conv(std::ostream &os, const opt_t *opts)
{
if (opts->yychConversion) {
@@ -509,6 +534,9 @@ void render(RenderContext &rctx, const Code *code)
case CODE_FUNC:
render_func(rctx, &code->func);
break;
case CODE_LOOP:
render_loop(rctx, code->loop);
break;
case CODE_TEXT_RAW:
os << code->text << std::endl;
line += count_lines_text(code->text) + 1;
@@ -576,6 +604,7 @@ void render(RenderContext &rctx, const Code *code)
case CODE_MAXNMATCH:
case CODE_YYCH:
case CODE_YYACCEPT:
case CODE_YYSTATE:
case CODE_COND_ENUM:
case CODE_COND_GOTO:
case CODE_COND_TABLE:
+8
View File
@@ -204,6 +204,14 @@ void compile(Scanner &input, Output &output, Opt &opts)
output.total_opts = accum_opts ? accum_opts : rblocks.last_opts();
// For special targets (skeleton and .dot) merge header into the output file.
if (globopts->target != TARGET_CODE && output.need_header) {
output.need_header = false;
blocks_t &cblocks = output.cblocks, &hblocks = output.hblocks;
cblocks.insert(cblocks.end(), hblocks.begin(), hblocks.end());
hblocks.clear();
}
if (globopts->target == TARGET_SKELETON) {
output.wdelay_stmt(0, emit_skeleton_epilog(output));
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace re2c {
class Scanner;
struct Opt;
class Output;
struct Output;
void compile(Scanner &input, Output &output, Opt &opts);
+2
View File
@@ -97,8 +97,10 @@ enum CodeKind {
CODE_MAXNMATCH,
CODE_YYCH,
CODE_YYACCEPT,
CODE_YYSTATE,
CODE_VAR,
CODE_STMT,
CODE_LOOP,
CODE_TEXT,
CODE_TEXT_RAW,
CODE_RAW,
+35 -4
View File
@@ -16,10 +16,6 @@ static void fix_conopt(conopt_t &glob)
glob.iFlag = true;
}
if (!glob.lookahead) {
glob.eager_skip = true;
}
// append directory separator '/' to all paths that do not have it
for (size_t i = 0; i < glob.incpaths.size(); ++i) {
std::string &p = glob.incpaths[i];
@@ -29,6 +25,13 @@ static void fix_conopt(conopt_t &glob)
}
}
if (!glob.lookahead || glob.loop_switch) {
// for loop-switch enable eager-skip always (not only in cases when
// YYFILL labels are used) to avoid special handling of initial state
// when there are transitions into it.
glob.eager_skip = true;
}
if (!glob.dep_file.empty() && glob.output_file.empty()) {
error("cannot generate dep file, output file not specified");
exit(1);
@@ -120,6 +123,7 @@ static void fix_mutopt(const conopt_t &glob, const mutopt_t &defaults,
real.yych = defaults.yych;
real.bEmitYYCh = defaults.bEmitYYCh;
real.yychConversion = defaults.yychConversion;
real.yystate = defaults.yystate;
real.fill = defaults.fill;
real.fill_use = defaults.fill_use;
real.fill_check = defaults.fill_check;
@@ -248,6 +252,14 @@ static void fix_mutopt(const conopt_t &glob, const mutopt_t &defaults,
if (real.fill_naked) {
real.fill_arg_use = false;
}
if (glob.loop_switch) {
// With --loop-switch there is no `goto`.
real.yyctable = defaults.yyctable;
real.condDivider = defaults.condDivider;
real.condDividerParam = defaults.condDividerParam;
real.condGoto = defaults.condGoto;
real.condGotoParam = defaults.condGotoParam;
}
// errors
if (glob.target == TARGET_SKELETON && glob.lang != LANG_C) {
@@ -310,6 +322,25 @@ static void fix_mutopt(const conopt_t &glob, const mutopt_t &defaults,
error("storable state requires YYFILL to be enabled");
exit(1);
}
if (glob.loop_switch) {
if (real.gFlag) {
error("cannot combine loop switch and computed gotos");
exit(1);
}
if (real.bFlag) {
// TODO: generate bitmaps before the joined loop/switch for all conditions.
error("bitmaps with loop switch are not supported");
exit(1);
}
if (!glob.lookahead) {
// The problem here is that TDFA(0) may have initial register operations that
// should be executed on DFA entry. With loop/switch these operations would
// need to go in a special initial state (they can't go before the `yystate`
// loop because that would not work with start conditions).
error("TDFA(0) with loop switch is not supported");
exit(1);
}
}
}
Opt::Opt(const conopt_t &globopts)
+3 -1
View File
@@ -70,11 +70,12 @@ class Msg;
CONSTOPT (posix_prectable_alg_t, posix_prectable, POSIX_PRECTABLE_COMPLEX) \
CONSTOPT (fixed_tags_t, fixed_tags, FIXTAG_ALL) \
CONSTOPT (bool, lookahead, true) \
CONSTOPT (bool, eager_skip, false) \
CONSTOPT (bool, optimize_tags, true) \
CONSTOPT (bool, backward, false) \
CONSTOPT (bool, stadfa, false) \
CONSTOPT (bool, nested_negative_tags, true) \
CONSTOPT (bool, eager_skip, false) \
CONSTOPT (bool, loop_switch, false) \
/* debug */ \
CONSTOPT (bool, dump_nfa, false) \
CONSTOPT (bool, dump_dfa_raw, false) \
@@ -136,6 +137,7 @@ class Msg;
MUTOPT (bool, yybmHexTable, false) \
MUTOPT (bool, gFlag, false) \
MUTOPT (bool, case_ranges, false) \
MUTOPT (std::string, yystate, "yystate") \
MUTOPT (std::string, yytarget, "yytarget") \
MUTOPT (uint32_t, cGotoThreshold, 9) \
MUTOPT (uint32_t, eof, NOEOF) \
+2 -1
View File
@@ -167,6 +167,8 @@ opt_long:
"no-generation-date" end { globopts.bNoGenerationDate = true; goto opt; }
"no-version" end { globopts.version = false; goto opt; }
"skeleton" end { globopts.target = TARGET_SKELETON; goto opt; }
"eager-skip" end { globopts.eager_skip = true; goto opt; }
"loop-switch" end { globopts.loop_switch = true; goto opt; }
"bit-vectors" end { opts.set_bFlag (true); goto opt; }
"debug-output" end { opts.set_dFlag (true); goto opt; }
@@ -209,7 +211,6 @@ opt_long:
"fixed-tags" end { NEXT_ARG("--fixed-tags", opt_fixed_tags); }
"no-lookahead" end { globopts.lookahead = false; goto opt; }
"no-optimize-tags" end { globopts.optimize_tags = false; goto opt; }
"eager-skip" end { globopts.eager_skip = true; goto opt; }
"stadfa" end { globopts.stadfa = true; goto opt; }
// debug options
+1 -1
View File
@@ -9,7 +9,7 @@ namespace re2c {
struct AST;
struct loc_t;
struct Msg;
class Msg;
typedef std::map<std::string, const AST*> symtab_t;
+1 -1
View File
@@ -13,7 +13,7 @@
namespace re2c {
struct SemAct;
template <class _Ty> class free_list;
template <class Ty> class free_list;
struct ASTChar {
uint32_t chr;
+8 -2
View File
@@ -189,7 +189,7 @@ loop:
}
"/*!types:re2c" {
out.cond_enum_in_hdr = out.in_header();
out.cond_enum_autogen = false;
out.warn_condition_order = false; // see note [condition order]
uint32_t allow = DCONF_FORMAT | DCONF_SEPARATOR;
if (!lex_block(out, CODE_COND_ENUM, opts->topIndent, allow)) return INPUT_ERROR;
@@ -202,6 +202,12 @@ loop:
msg.error(cur_loc(), "`getstate:re2c` without `-f --storable-state` option");
return INPUT_ERROR;
}
if (opts->loop_switch) {
msg.error(cur_loc(), "`getstate:re2c` is incompatible with the --loop-switch "
"option, as it requires cross-block transitions that are unsupported "
"without the `goto` statement");
return INPUT_ERROR;
}
if (!lex_block(out, CODE_STATE_GOTO, opts->topIndent, 0)) return INPUT_ERROR;
goto next;
}
@@ -209,7 +215,7 @@ loop:
"/*!header:re2c:on" {
out.wraw(tok, ptr);
out.header_mode(true);
out.need_header = opts->target == TARGET_CODE;
out.need_header = true;
if (!lex_block_end(out)) return INPUT_ERROR;
goto next;
}
+1
View File
@@ -119,6 +119,7 @@ void Scanner::lex_conf(Opt &opts)
return;
}
"variable:yytarget" { opts.set_yytarget (lex_conf_string ()); return; }
"variable:yystate" { opts.set_yystate (lex_conf_string ()); return; }
"define:YYCURSOR" { opts.set_yycursor (lex_conf_string ()); return; }
"define:YYMARKER" { opts.set_yymarker (lex_conf_string ()); return; }
+1 -1
View File
@@ -21,7 +21,7 @@
namespace re2c {
class Msg;
class Output;
struct Output;
struct AST;
struct ASTChar;
struct BlockNameList;
+1 -1
View File
@@ -14,7 +14,7 @@ namespace re2c {
struct AST;
struct ASTRule;
struct Msg;
class Msg;
struct Rule;
struct opt_t;
+28 -7
View File
@@ -152,7 +152,7 @@ static void emit_skeleton_defines(Output &output, CodeList *code, const DFA &dfa
append(code, code_textraw(alc, "#define YYSHIFT(o) cursor += o"));
append(code, code_textraw(alc, "#define YYLESSTHAN(n) (limit - cursor) < n"));
append(code, code_textraw(alc, opts->eof == NOEOF
? "#define YYFILL(n) { break; }"
? "#define YYFILL(n) { goto loop_end; }"
: "#define YYFILL(n) 1 /* fail */"));
append(code, code_newline(alc));
}
@@ -610,8 +610,17 @@ static void emit_skeleton_function_lex(Output &output, CodeList *code, DFA &dfa)
append(block, code_textraw(alc, ""));
append(block, code_text(alc,
"for (i = 0; status == 0 && cursor < eof && i < keys_count;) {"));
// Don't use `for` loops or `while` loops: the generated code may contain an
// inner loop if the --loop-switch option is used, and then `continue` and
// `break` statements may result in unexpected control flow. Use `goto` loop.
append(block, code_stmt(alc, "i = 0"));
append(block, code_slabel(alc, "loop"));
block2 = code_list(alc);
append(block2, code_stmt(alc, "goto loop_end"));
append(block, code_if_then_else(alc,
"!(status == 0 && cursor < eof && i < keys_count)", block2, NULL));
block2 = code_list(alc);
append(block2, code_stmt(alc, "token = cursor"));
if (dfa.need_backup) {
@@ -624,6 +633,9 @@ static void emit_skeleton_function_lex(Output &output, CodeList *code, DFA &dfa)
if (dfa.need_accept) {
append(block2, code_stmt(alc, "unsigned int yyaccept = 0"));
}
if (opts->loop_switch) {
append(block2, code_stmt(alc, o.cstr("unsigned int yystate = 0").flush()));
}
// autogenerated stag variables
if (!dfa.stagnames.empty()) {
text = o.cstr("\n").str(indent(2, opts->indString))
@@ -676,11 +688,20 @@ static void emit_skeleton_function_lex(Output &output, CodeList *code, DFA &dfa)
}
append(block2, code_textraw(alc, ""));
dfa.emit_body(output, block2);
if (!opts->loop_switch) {
gen_dfa_as_blocks_with_labels(output, dfa, block2);
} else {
// With loop/switch there are no labels, and each block has its own state switch.
// Restart state counter from zero so that cases start from zero.
output.label_counter = 0;
CodeCases *cases = code_cases(alc);
gen_dfa_as_switch_cases(output, dfa, cases);
wrap_dfas_in_loop_switch(output, block2, cases);
}
append(block2, code_textraw(alc, ""));
append(block, code_block(alc, block2, CodeBlock::INDENTED));
append(block, code_text(alc, "}"));
append(block, code_block(alc, block2, CodeBlock::WRAPPED));
append(block, code_slabel(alc, "loop_end"));
if_code = code_list(alc);
@@ -837,7 +858,7 @@ void emit_skeleton_action(Output &output, CodeList *code, const DFA &dfa, size_t
append(code, code_block(alc, hangafter, CodeBlock::INDENTED));
append(code, code_stmt(alc, "continue"));
append(code, code_stmt(alc, "goto loop"));
}
} // namespace re2c
+8 -8
View File
@@ -6,25 +6,25 @@
namespace re2c {
template<class _Ty>
class free_list: protected std::set<_Ty>
template<class Ty>
class free_list: protected std::set<Ty>
{
public:
typedef typename std::set<_Ty>::iterator iterator;
typedef typename std::set<_Ty>::size_type size_type;
typedef typename std::set<_Ty>::key_type key_type;
typedef typename std::set<Ty>::iterator iterator;
typedef typename std::set<Ty>::size_type size_type;
typedef typename std::set<Ty>::key_type key_type;
free_list(): in_clear(false)
{
}
using std::set<_Ty>::insert;
using std::set<Ty>::insert;
size_type erase(const key_type& key)
{
if (!in_clear)
{
return std::set<_Ty>::erase(key);
return std::set<Ty>::erase(key);
}
return 0;
}
@@ -37,7 +37,7 @@ public:
{
delete *it;
}
std::set<_Ty>::clear();
std::set<Ty>::clear();
in_clear = false;
}
-2
View File
@@ -2,8 +2,6 @@
#line 1 "bug152.re"
// re2c $INPUT -o $OUTPUT -c
#line 5 "bug152.c"
enum YYCONDTYPE {
};
#line 2 "bug152.re"
+54
View File
@@ -0,0 +1,54 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --case-ranges --loop-switch -i
int lex(const char *YYCURSOR) { // C/C++ code
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *YYCURSOR;
++YYCURSOR;
switch (yych) {
case 'A' ... 'Z':
case 'a' ... 'z':
yystate = 3;
continue;
default:
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return 1; }
case 3:
yych = *YYCURSOR;
yystate = 4;
continue;
case 4:
switch (yych) {
case '0' ... '9':
case 'A' ... 'Z':
case 'a' ... 'z':
++YYCURSOR;
yystate = 3;
continue;
default:
yystate = 5;
continue;
}
case 5:
{ return 0; }
}
}
}
} //
//
int main() { // C/C++ code
return lex("qwerty42"); //
} //
+18
View File
@@ -0,0 +1,18 @@
// re2c $INPUT -o $OUTPUT --case-ranges --loop-switch -i
int lex(const char *YYCURSOR) { // C/C++ code
/*!re2c
re2c:define:YYCTYPE = char; // configurations
re2c:yyfill:enable = 0; //
//
alpha = [a-zA-Z]; // named definitions
ident = alpha (alpha | [0-9])*; //
//
ident { return 0; } // rules
* { return 1; } //
*/
} //
//
int main() { // C/C++ code
return lex("qwerty42"); //
} //
@@ -0,0 +1,121 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define YYMAXFILL 1
// expect YYMAXFILL-padded string
static int lex(const char *str, unsigned int len)
{
const char *YYCURSOR = str, *YYLIMIT = str + len + YYMAXFILL;
int count = 0;
loop:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
if (YYLIMIT <= YYCURSOR) return -1;
yych = *YYCURSOR;
++YYCURSOR;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case ' ':
yystate = 5;
continue;
case '\'':
yystate = 8;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return YYCURSOR + YYMAXFILL - 1 == YYLIMIT ? count : -1; }
case 3:
yystate = 4;
continue;
case 4:
{ return -1; }
case 5:
if (YYLIMIT <= YYCURSOR) return -1;
yych = *YYCURSOR;
yystate = 6;
continue;
case 6:
switch (yych) {
case ' ':
++YYCURSOR;
yystate = 5;
continue;
default:
yystate = 7;
continue;
}
case 7:
{ goto loop; }
case 8:
if (YYLIMIT <= YYCURSOR) return -1;
yych = *YYCURSOR;
yystate = 9;
continue;
case 9:
++YYCURSOR;
switch (yych) {
case '\'':
yystate = 10;
continue;
case '\\':
yystate = 12;
continue;
default:
yystate = 8;
continue;
}
case 10:
yystate = 11;
continue;
case 11:
{ ++count; goto loop; }
case 12:
if (YYLIMIT <= YYCURSOR) return -1;
yych = *YYCURSOR;
++YYCURSOR;
yystate = 8;
continue;
}
}
}
}
// make a copy of the string with YYMAXFILL zeroes at the end
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len + YYMAXFILL);
memcpy(s, str, len);
memset(s + len, 0, YYMAXFILL);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
@@ -0,0 +1,46 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*!max:re2c*/
// expect YYMAXFILL-padded string
static int lex(const char *str, unsigned int len)
{
const char *YYCURSOR = str, *YYLIMIT = str + len + YYMAXFILL;
int count = 0;
loop:
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYFILL = "return -1;";
* { return -1; }
[\x00] { return YYCURSOR + YYMAXFILL - 1 == YYLIMIT ? count : -1; }
['] ([^'\\] | [\\][^])* ['] { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
// make a copy of the string with YYMAXFILL zeroes at the end
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len + YYMAXFILL);
memcpy(s, str, len);
memset(s + len, 0, YYMAXFILL);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
+134
View File
@@ -0,0 +1,134 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
// expect a null-terminated string
static int lex(const char *str, unsigned int len)
{
const char *YYCURSOR = str, *YYLIMIT = str + len, *YYMARKER;
int count = 0;
loop:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *YYCURSOR;
switch (yych) {
case ' ':
++YYCURSOR;
yystate = 3;
continue;
case '\'':
++YYCURSOR;
yystate = 6;
continue;
default:
if (YYLIMIT <= YYCURSOR) {
yystate = 12;
continue;
}
++YYCURSOR;
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return -1; }
case 3:
yych = *YYCURSOR;
yystate = 4;
continue;
case 4:
switch (yych) {
case ' ':
++YYCURSOR;
yystate = 3;
continue;
default:
yystate = 5;
continue;
}
case 5:
{ goto loop; }
case 6:
YYMARKER = YYCURSOR;
yych = *YYCURSOR;
if (yych >= 0x01) {
yystate = 8;
continue;
}
if (YYLIMIT <= YYCURSOR) {
yystate = 2;
continue;
}
++YYCURSOR;
yystate = 7;
continue;
case 7:
yych = *YYCURSOR;
yystate = 8;
continue;
case 8:
switch (yych) {
case '\'':
++YYCURSOR;
yystate = 9;
continue;
case '\\':
++YYCURSOR;
yystate = 11;
continue;
default:
if (YYLIMIT <= YYCURSOR) {
yystate = 13;
continue;
}
++YYCURSOR;
yystate = 7;
continue;
}
case 9:
yystate = 10;
continue;
case 10:
{ ++count; goto loop; }
case 11:
yych = *YYCURSOR;
if (yych <= 0x00) {
if (YYLIMIT <= YYCURSOR) {
yystate = 13;
continue;
}
++YYCURSOR;
yystate = 7;
continue;
}
++YYCURSOR;
yystate = 7;
continue;
case 12:
{ return count; }
case 13:
YYCURSOR = YYMARKER;
yystate = 2;
continue;
}
}
}
}
#define TEST(s, r) assert(lex(s, sizeof(s) - 1) == r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
// expect a null-terminated string
static int lex(const char *str, unsigned int len)
{
const char *YYCURSOR = str, *YYLIMIT = str + len, *YYMARKER;
int count = 0;
loop:
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
re2c:eof = 0;
* { return -1; }
$ { return count; }
['] ([^'\\] | [\\][^])* ['] { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
#define TEST(s, r) assert(lex(s, sizeof(s) - 1) == r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
+207
View File
@@ -0,0 +1,207 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define SIZE 4096
typedef struct {
FILE *file;
char buf[SIZE + 1], *lim, *cur, *mar, *tok;
int eof;
} Input;
static int fill(Input *in)
{
if (in->eof) {
return 1;
}
const size_t free = in->tok - in->buf;
if (free < 1) {
return 2;
}
memmove(in->buf, in->tok, in->lim - in->tok);
in->lim -= free;
in->cur -= free;
in->mar -= free;
in->tok -= free;
in->lim += fread(in->lim, 1, free, in->file);
in->lim[0] = 0;
in->eof |= in->lim < in->buf + SIZE;
return 0;
}
static void init(Input *in, FILE *file)
{
in->file = file;
in->cur = in->mar = in->tok = in->lim = in->buf + SIZE;
in->eof = 0;
fill(in);
}
static int lex(Input *in)
{
int count = 0;
loop:
in->tok = in->cur;
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *in->cur;
switch (yych) {
case ' ':
++in->cur;
yystate = 3;
continue;
case '\'':
++in->cur;
yystate = 6;
continue;
default:
if (in->lim <= in->cur) {
if (fill(in) == 0) {
yystate = 0;
continue;
}
yystate = 12;
continue;
}
++in->cur;
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return -1; }
case 3:
yych = *in->cur;
yystate = 4;
continue;
case 4:
switch (yych) {
case ' ':
++in->cur;
yystate = 3;
continue;
default:
if (in->lim <= in->cur) {
if (fill(in) == 0) {
yystate = 3;
continue;
}
}
yystate = 5;
continue;
}
case 5:
{ goto loop; }
case 6:
in->mar = in->cur;
yych = *in->cur;
if (yych >= 0x01) {
yystate = 8;
continue;
}
if (in->lim <= in->cur) {
if (fill(in) == 0) {
yystate = 6;
continue;
}
yystate = 2;
continue;
}
++in->cur;
yystate = 7;
continue;
case 7:
yych = *in->cur;
yystate = 8;
continue;
case 8:
switch (yych) {
case '\'':
++in->cur;
yystate = 9;
continue;
case '\\':
++in->cur;
yystate = 11;
continue;
default:
if (in->lim <= in->cur) {
if (fill(in) == 0) {
yystate = 7;
continue;
}
yystate = 13;
continue;
}
++in->cur;
yystate = 7;
continue;
}
case 9:
yystate = 10;
continue;
case 10:
{ ++count; goto loop; }
case 11:
yych = *in->cur;
if (yych <= 0x00) {
if (in->lim <= in->cur) {
if (fill(in) == 0) {
yystate = 11;
continue;
}
yystate = 13;
continue;
}
++in->cur;
yystate = 7;
continue;
}
++in->cur;
yystate = 7;
continue;
case 12:
{ return count; }
case 13:
in->cur = in->mar;
yystate = 2;
continue;
}
}
}
}
int main()
{
const char *fname = "input";
const char str[] = "'qu\0tes' 'are' 'fine: \\'' ";
FILE *f;
Input in;
// prepare input file: a few times the size of the buffer,
// containing strings with zeroes and escaped quotes
f = fopen(fname, "w");
for (int i = 0; i < SIZE; ++i) {
fwrite(str, 1, sizeof(str) - 1, f);
}
fclose(f);
f = fopen(fname, "r");
init(&in, f);
assert(lex(&in) == SIZE * 3);
fclose(f);
remove(fname);
return 0;
}
+86
View File
@@ -0,0 +1,86 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define SIZE 4096
typedef struct {
FILE *file;
char buf[SIZE + 1], *lim, *cur, *mar, *tok;
int eof;
} Input;
static int fill(Input *in)
{
if (in->eof) {
return 1;
}
const size_t free = in->tok - in->buf;
if (free < 1) {
return 2;
}
memmove(in->buf, in->tok, in->lim - in->tok);
in->lim -= free;
in->cur -= free;
in->mar -= free;
in->tok -= free;
in->lim += fread(in->lim, 1, free, in->file);
in->lim[0] = 0;
in->eof |= in->lim < in->buf + SIZE;
return 0;
}
static void init(Input *in, FILE *file)
{
in->file = file;
in->cur = in->mar = in->tok = in->lim = in->buf + SIZE;
in->eof = 0;
fill(in);
}
static int lex(Input *in)
{
int count = 0;
loop:
in->tok = in->cur;
/*!re2c
re2c:eof = 0;
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = in->cur;
re2c:define:YYMARKER = in->mar;
re2c:define:YYLIMIT = in->lim;
re2c:define:YYFILL = "fill(in) == 0";
* { return -1; }
$ { return count; }
['] ([^'\\] | [\\][^])* ['] { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
int main()
{
const char *fname = "input";
const char str[] = "'qu\0tes' 'are' 'fine: \\'' ";
FILE *f;
Input in;
// prepare input file: a few times the size of the buffer,
// containing strings with zeroes and escaped quotes
f = fopen(fname, "w");
for (int i = 0; i < SIZE; ++i) {
fwrite(str, 1, sizeof(str) - 1, f);
}
fclose(f);
f = fopen(fname, "r");
init(&in, f);
assert(lex(&in) == SIZE * 3);
fclose(f);
remove(fname);
return 0;
}
+161
View File
@@ -0,0 +1,161 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define YYMAXFILL 1
#define SIZE 4096
typedef struct {
FILE *file;
char buf[SIZE + YYMAXFILL], *lim, *cur, *mar, *tok;
int eof;
} Input;
static int fill(Input *in, size_t need)
{
if (in->eof) {
return 1;
}
const size_t free = in->tok - in->buf;
if (free < need) {
return 2;
}
memmove(in->buf, in->tok, in->lim - in->tok);
in->lim -= free;
in->cur -= free;
in->mar -= free;
in->tok -= free;
in->lim += fread(in->lim, 1, free, in->file);
if (in->lim < in->buf + SIZE) {
in->eof = 1;
memset(in->lim, 0, YYMAXFILL);
in->lim += YYMAXFILL;
}
return 0;
}
static void init(Input *in, FILE *file)
{
in->file = file;
in->cur = in->mar = in->tok = in->lim = in->buf + SIZE;
in->eof = 0;
fill(in, 1);
}
static int lex(Input *in)
{
int count = 0;
loop:
in->tok = in->cur;
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
if (in->lim <= in->cur) if (fill(in, 1) != 0) return -1;
yych = *in->cur;
++in->cur;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case ' ':
yystate = 5;
continue;
case '\'':
yystate = 8;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return (in->lim - in->cur == YYMAXFILL - 1) ? count : -1; }
case 3:
yystate = 4;
continue;
case 4:
{ return -1; }
case 5:
if (in->lim <= in->cur) if (fill(in, 1) != 0) return -1;
yych = *in->cur;
yystate = 6;
continue;
case 6:
switch (yych) {
case ' ':
++in->cur;
yystate = 5;
continue;
default:
yystate = 7;
continue;
}
case 7:
{ goto loop; }
case 8:
if (in->lim <= in->cur) if (fill(in, 1) != 0) return -1;
yych = *in->cur;
yystate = 9;
continue;
case 9:
++in->cur;
switch (yych) {
case '\'':
yystate = 10;
continue;
case '\\':
yystate = 12;
continue;
default:
yystate = 8;
continue;
}
case 10:
yystate = 11;
continue;
case 11:
{ ++count; goto loop; }
case 12:
if (in->lim <= in->cur) if (fill(in, 1) != 0) return -1;
yych = *in->cur;
++in->cur;
yystate = 8;
continue;
}
}
}
}
int main()
{
const char *fname = "input";
const char str[] = "'qu\0tes' 'are' 'fine: \\'' ";
FILE *f;
Input in;
// prepare input file: a few times the size of the buffer,
// containing strings with zeroes and escaped quotes
f = fopen(fname, "w");
for (int i = 0; i < SIZE; ++i) {
fwrite(str, 1, sizeof(str) - 1, f);
}
fclose(f);
f = fopen(fname, "r");
init(&in, f);
assert(lex(&in) == SIZE * 3);
fclose(f);
remove(fname);
return 0;
}
+89
View File
@@ -0,0 +1,89 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
/*!max:re2c*/
#define SIZE 4096
typedef struct {
FILE *file;
char buf[SIZE + YYMAXFILL], *lim, *cur, *mar, *tok;
int eof;
} Input;
static int fill(Input *in, size_t need)
{
if (in->eof) {
return 1;
}
const size_t free = in->tok - in->buf;
if (free < need) {
return 2;
}
memmove(in->buf, in->tok, in->lim - in->tok);
in->lim -= free;
in->cur -= free;
in->mar -= free;
in->tok -= free;
in->lim += fread(in->lim, 1, free, in->file);
if (in->lim < in->buf + SIZE) {
in->eof = 1;
memset(in->lim, 0, YYMAXFILL);
in->lim += YYMAXFILL;
}
return 0;
}
static void init(Input *in, FILE *file)
{
in->file = file;
in->cur = in->mar = in->tok = in->lim = in->buf + SIZE;
in->eof = 0;
fill(in, 1);
}
static int lex(Input *in)
{
int count = 0;
loop:
in->tok = in->cur;
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = in->cur;
re2c:define:YYMARKER = in->mar;
re2c:define:YYLIMIT = in->lim;
re2c:define:YYFILL = "if (fill(in, @@) != 0) return -1;";
* { return -1; }
[\x00] { return (in->lim - in->cur == YYMAXFILL - 1) ? count : -1; }
['] ([^'\\] | [\\][^])* ['] { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
int main()
{
const char *fname = "input";
const char str[] = "'qu\0tes' 'are' 'fine: \\'' ";
FILE *f;
Input in;
// prepare input file: a few times the size of the buffer,
// containing strings with zeroes and escaped quotes
f = fopen(fname, "w");
for (int i = 0; i < SIZE; ++i) {
fwrite(str, 1, sizeof(str) - 1, f);
}
fclose(f);
f = fopen(fname, "r");
init(&in, f);
assert(lex(&in) == SIZE * 3);
fclose(f);
remove(fname);
return 0;
}
@@ -0,0 +1,146 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// expect a string without terminating null
static int lex(const char *str, unsigned int len)
{
const char *cur = str, *lim = str + len, *mar;
int count = 0;
loop:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = cur < lim ? *cur : 0;
switch (yych) {
case ' ':
++cur;
yystate = 3;
continue;
case '\'':
++cur;
yystate = 6;
continue;
default:
if (cur >= lim) {
yystate = 12;
continue;
}
++cur;
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return -1; }
case 3:
yych = cur < lim ? *cur : 0;
yystate = 4;
continue;
case 4:
switch (yych) {
case ' ':
++cur;
yystate = 3;
continue;
default:
yystate = 5;
continue;
}
case 5:
{ goto loop; }
case 6:
mar = cur;
yych = cur < lim ? *cur : 0;
if (yych >= 0x01) {
yystate = 8;
continue;
}
if (cur >= lim) {
yystate = 2;
continue;
}
++cur;
yystate = 7;
continue;
case 7:
yych = cur < lim ? *cur : 0;
yystate = 8;
continue;
case 8:
switch (yych) {
case '\'':
++cur;
yystate = 9;
continue;
case '\\':
++cur;
yystate = 11;
continue;
default:
if (cur >= lim) {
yystate = 13;
continue;
}
++cur;
yystate = 7;
continue;
}
case 9:
yystate = 10;
continue;
case 10:
{ ++count; goto loop; }
case 11:
yych = cur < lim ? *cur : 0;
if (yych <= 0x00) {
if (cur >= lim) {
yystate = 13;
continue;
}
++cur;
yystate = 7;
continue;
}
++cur;
yystate = 7;
continue;
case 12:
{ return count; }
case 13:
cur = mar;
yystate = 2;
continue;
}
}
}
}
// make a copy of the string without terminating null
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len);
memcpy(s, str, len);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
@@ -0,0 +1,50 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// expect a string without terminating null
static int lex(const char *str, unsigned int len)
{
const char *cur = str, *lim = str + len, *mar;
int count = 0;
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:eof = 0;
re2c:flags:input = custom;
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYLESSTHAN = "cur >= lim";
re2c:define:YYPEEK = "cur < lim ? *cur : 0"; // fake null
re2c:define:YYSKIP = "++cur;";
re2c:define:YYBACKUP = "mar = cur;";
re2c:define:YYRESTORE = "cur = mar;";
* { return -1; }
$ { return count; }
['] ([^'\\] | [\\][^])* ['] { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
// make a copy of the string without terminating null
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len);
memcpy(s, str, len);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
@@ -0,0 +1,152 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// expect a string without terminating null
static int lex(const char *str, unsigned int len)
{
const char *cur = str, *lim = str + len;
int count = 0;
loop:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = cur < lim ? *cur : 0;
++cur;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case ' ':
yystate = 5;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
yystate = 8;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return count; }
case 3:
yystate = 4;
continue;
case 4:
{ return -1; }
case 5:
yych = cur < lim ? *cur : 0;
yystate = 6;
continue;
case 6:
switch (yych) {
case ' ':
++cur;
yystate = 5;
continue;
default:
yystate = 7;
continue;
}
case 7:
{ goto loop; }
case 8:
yych = cur < lim ? *cur : 0;
yystate = 9;
continue;
case 9:
switch (yych) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
++cur;
yystate = 8;
continue;
default:
yystate = 10;
continue;
}
case 10:
{ ++count; goto loop; }
}
}
}
}
// make a copy of the string without terminating null
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len);
memcpy(s, str, len);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("one two three ", 3);
TEST("f0ur", -1);
return 0;
}
@@ -0,0 +1,47 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// expect a string without terminating null
static int lex(const char *str, unsigned int len)
{
const char *cur = str, *lim = str + len;
int count = 0;
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:flags:input = custom;
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYLESSTHAN = "cur >= lim";
re2c:define:YYPEEK = "cur < lim ? *cur : 0"; // fake null
re2c:define:YYSKIP = "++cur;";
* { return -1; }
[\x00] { return count; }
[a-z]+ { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
// make a copy of the string without terminating null
static void test(const char *str, unsigned int len, int res)
{
char *s = (char*) malloc(len);
memcpy(s, str, len);
int r = lex(s, len);
free(s);
assert(r == res);
}
#define TEST(s, r) test(s, sizeof(s) - 1, r)
int main()
{
TEST("", 0);
TEST("one two three ", 3);
TEST("f0ur", -1);
return 0;
}
@@ -0,0 +1,366 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -i --loop-switch
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = ~0lu;
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
u = u * BASE + d;
if (u > UINT32_MAX) u = ERROR;
}
static uint64_t parse_u32(const char *s)
{
const char *YYMARKER;
uint64_t u = 0;
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case '0':
yystate = 3;
continue;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 5;
continue;
default:
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return ERROR; }
case 3:
YYMARKER = s;
yych = *s;
switch (yych) {
case 'B':
case 'b':
++s;
yystate = 7;
continue;
case 'X':
case 'x':
++s;
yystate = 9;
continue;
default:
yystate = 4;
continue;
}
case 4:
{ goto oct; }
case 5:
yystate = 6;
continue;
case 6:
s -= 1;
{ goto dec; }
case 7:
yych = *s;
switch (yych) {
case '0':
case '1':
++s;
yystate = 10;
continue;
default:
yystate = 8;
continue;
}
case 8:
s = YYMARKER;
yystate = 4;
continue;
case 9:
yych = *s;
switch (yych) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
++s;
yystate = 12;
continue;
default:
yystate = 8;
continue;
}
case 10:
yystate = 11;
continue;
case 11:
s -= 1;
{ goto bin; }
case 12:
yystate = 13;
continue;
case 13:
s -= 1;
{ goto hex; }
}
}
}
bin:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case '0':
case '1':
yystate = 5;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return u; }
case 3:
yystate = 4;
continue;
case 4:
{ return ERROR; }
case 5:
yystate = 6;
continue;
case 6:
{ adddgt<2>(u, s[-1] - '0'); goto bin; }
}
}
}
oct:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
yystate = 5;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return u; }
case 3:
yystate = 4;
continue;
case 4:
{ return ERROR; }
case 5:
yystate = 6;
continue;
case 6:
{ adddgt<8>(u, s[-1] - '0'); goto oct; }
}
}
}
dec:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 5;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return u; }
case 3:
yystate = 4;
continue;
case 4:
{ return ERROR; }
case 5:
yystate = 6;
continue;
case 6:
{ adddgt<10>(u, s[-1] - '0'); goto dec; }
}
}
}
hex:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 5;
continue;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
yystate = 7;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
yystate = 9;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return u; }
case 3:
yystate = 4;
continue;
case 4:
{ return ERROR; }
case 5:
yystate = 6;
continue;
case 6:
{ adddgt<16>(u, s[-1] - '0'); goto hex; }
case 7:
yystate = 8;
continue;
case 8:
{ adddgt<16>(u, s[-1] - 'A' + 10); goto hex; }
case 9:
yystate = 10;
continue;
case 10:
{ adddgt<16>(u, s[-1] - 'a' + 10); goto hex; }
}
}
}
}
int main()
{
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
assert(parse_u32("") == ERROR);
return 0;
}
@@ -0,0 +1,69 @@
// re2c $INPUT -o $OUTPUT -i --loop-switch
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = ~0lu;
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
u = u * BASE + d;
if (u > UINT32_MAX) u = ERROR;
}
static uint64_t parse_u32(const char *s)
{
const char *YYMARKER;
uint64_t u = 0;
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = s;
end = "\x00";
'0b' / [01] { goto bin; }
"0" { goto oct; }
"" / [1-9] { goto dec; }
'0x' / [0-9a-fA-F] { goto hex; }
* { return ERROR; }
*/
bin:
/*!re2c
end { return u; }
[01] { adddgt<2>(u, s[-1] - '0'); goto bin; }
* { return ERROR; }
*/
oct:
/*!re2c
end { return u; }
[0-7] { adddgt<8>(u, s[-1] - '0'); goto oct; }
* { return ERROR; }
*/
dec:
/*!re2c
end { return u; }
[0-9] { adddgt<10>(u, s[-1] - '0'); goto dec; }
* { return ERROR; }
*/
hex:
/*!re2c
end { return u; }
[0-9] { adddgt<16>(u, s[-1] - '0'); goto hex; }
[a-f] { adddgt<16>(u, s[-1] - 'a' + 10); goto hex; }
[A-F] { adddgt<16>(u, s[-1] - 'A' + 10); goto hex; }
* { return ERROR; }
*/
}
int main()
{
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
assert(parse_u32("") == ERROR);
return 0;
}
@@ -0,0 +1,340 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -ci --loop-switch
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = ~0lu;
enum YYCONDTYPE {
yycinit = 0,
yycbin = 14,
yycdec = 21,
yychex = 28,
yycoct = 39,
};
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
u = u * BASE + d;
if (u > UINT32_MAX) u = ERROR;
}
static uint64_t parse_u32(const char *s)
{
const char *YYMARKER;
int c = yycinit;
uint64_t u = 0;
loop:
{
char yych;
unsigned int yystate = c;
for (;;) {
switch (yystate) {
case 0:
yych = *s;
++s;
switch (yych) {
case '0':
yystate = 3;
continue;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 5;
continue;
default:
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return ERROR; }
case 3:
YYMARKER = s;
yych = *s;
switch (yych) {
case 'B':
case 'b':
++s;
yystate = 7;
continue;
case 'X':
case 'x':
++s;
yystate = 9;
continue;
default:
yystate = 4;
continue;
}
case 4:
c = yycoct;
yystate = yycoct;
continue;
case 5:
yystate = 6;
continue;
case 6:
s -= 1;
c = yycdec;
yystate = yycdec;
continue;
case 7:
yych = *s;
switch (yych) {
case '0':
case '1':
++s;
yystate = 10;
continue;
default:
yystate = 8;
continue;
}
case 8:
s = YYMARKER;
yystate = 4;
continue;
case 9:
yych = *s;
switch (yych) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
++s;
yystate = 12;
continue;
default:
yystate = 8;
continue;
}
case 10:
yystate = 11;
continue;
case 11:
s -= 1;
c = yycbin;
yystate = yycbin;
continue;
case 12:
yystate = 13;
continue;
case 13:
s -= 1;
c = yychex;
yystate = yychex;
continue;
case 14:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 15;
continue;
case '0':
case '1':
yystate = 19;
continue;
default:
yystate = 17;
continue;
}
case 15:
yystate = 16;
continue;
case 16:
{ return u; }
case 17:
yystate = 18;
continue;
case 18:
{ return ERROR; }
case 19:
yystate = 20;
continue;
case 20:
{ adddgt<2> (u, s[-1] - '0'); goto loop; }
case 21:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 22;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 26;
continue;
default:
yystate = 24;
continue;
}
case 22:
yystate = 23;
continue;
case 23:
{ return u; }
case 24:
yystate = 25;
continue;
case 25:
{ return ERROR; }
case 26:
yystate = 27;
continue;
case 27:
{ adddgt<10>(u, s[-1] - '0'); goto loop; }
case 28:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 29;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
yystate = 33;
continue;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
yystate = 35;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
yystate = 37;
continue;
default:
yystate = 31;
continue;
}
case 29:
yystate = 30;
continue;
case 30:
{ return u; }
case 31:
yystate = 32;
continue;
case 32:
{ return ERROR; }
case 33:
yystate = 34;
continue;
case 34:
{ adddgt<16>(u, s[-1] - '0'); goto loop; }
case 35:
yystate = 36;
continue;
case 36:
{ adddgt<16>(u, s[-1] - 'A' + 10); goto loop; }
case 37:
yystate = 38;
continue;
case 38:
{ adddgt<16>(u, s[-1] - 'a' + 10); goto loop; }
case 39:
yych = *s;
++s;
switch (yych) {
case 0x00:
yystate = 40;
continue;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
yystate = 44;
continue;
default:
yystate = 42;
continue;
}
case 40:
yystate = 41;
continue;
case 41:
{ return u; }
case 42:
yystate = 43;
continue;
case 43:
{ return ERROR; }
case 44:
yystate = 45;
continue;
case 45:
{ adddgt<8> (u, s[-1] - '0'); goto loop; }
}
}
}
}
int main()
{
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
assert(parse_u32("") == ERROR);
return 0;
}
@@ -0,0 +1,57 @@
// re2c $INPUT -o $OUTPUT -ci --loop-switch
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = ~0lu;
/*!types:re2c*/
template<int BASE> static void adddgt(uint64_t &u, unsigned int d)
{
u = u * BASE + d;
if (u > UINT32_MAX) u = ERROR;
}
static uint64_t parse_u32(const char *s)
{
const char *YYMARKER;
int c = yycinit;
uint64_t u = 0;
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:api:style = free-form;
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = s;
re2c:define:YYGETCONDITION = "c";
re2c:define:YYSETCONDITION = "c = @@;";
<*> * { return ERROR; }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" { return u; }
<bin> [01] { adddgt<2> (u, s[-1] - '0'); goto loop; }
<oct> [0-7] { adddgt<8> (u, s[-1] - '0'); goto loop; }
<dec> [0-9] { adddgt<10>(u, s[-1] - '0'); goto loop; }
<hex> [0-9] { adddgt<16>(u, s[-1] - '0'); goto loop; }
<hex> [a-f] { adddgt<16>(u, s[-1] - 'a' + 10); goto loop; }
<hex> [A-F] { adddgt<16>(u, s[-1] - 'A' + 10); goto loop; }
*/
}
int main()
{
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
assert(parse_u32("") == ERROR);
return 0;
}
+229
View File
@@ -0,0 +1,229 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --input-encoding utf8 --loop-switch -i
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
#include <assert.h>
#include <stdint.h>
static int lex_utf8(const uint8_t *YYCURSOR)
{
const uint8_t *YYMARKER;
{
uint8_t yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *YYCURSOR;
++YYCURSOR;
switch (yych) {
case 0xE2:
yystate = 3;
continue;
default:
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return 1; }
case 3:
YYMARKER = YYCURSOR;
yych = *YYCURSOR;
switch (yych) {
case 0x88:
++YYCURSOR;
yystate = 4;
continue;
default:
yystate = 2;
continue;
}
case 4:
yych = *YYCURSOR;
switch (yych) {
case 0x80:
++YYCURSOR;
yystate = 6;
continue;
default:
yystate = 5;
continue;
}
case 5:
YYCURSOR = YYMARKER;
yystate = 2;
continue;
case 6:
yych = *YYCURSOR;
switch (yych) {
case 'x':
++YYCURSOR;
yystate = 7;
continue;
default:
yystate = 5;
continue;
}
case 7:
yych = *YYCURSOR;
switch (yych) {
case ' ':
++YYCURSOR;
yystate = 8;
continue;
default:
yystate = 5;
continue;
}
case 8:
yych = *YYCURSOR;
switch (yych) {
case 0xE2:
++YYCURSOR;
yystate = 9;
continue;
default:
yystate = 5;
continue;
}
case 9:
yych = *YYCURSOR;
switch (yych) {
case 0x88:
++YYCURSOR;
yystate = 10;
continue;
default:
yystate = 5;
continue;
}
case 10:
yych = *YYCURSOR;
switch (yych) {
case 0x83:
++YYCURSOR;
yystate = 11;
continue;
default:
yystate = 5;
continue;
}
case 11:
yych = *YYCURSOR;
switch (yych) {
case 'y':
++YYCURSOR;
yystate = 12;
continue;
default:
yystate = 5;
continue;
}
case 12:
yystate = 13;
continue;
case 13:
{ return 0; }
}
}
}
}
static int lex_utf32(const uint32_t *YYCURSOR)
{
const uint32_t *YYMARKER;
{
uint32_t yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *YYCURSOR;
++YYCURSOR;
if (yych == 0x00002200) {
yystate = 3;
continue;
}
yystate = 1;
continue;
case 1:
yystate = 2;
continue;
case 2:
{ return 1; }
case 3:
YYMARKER = YYCURSOR;
yych = *YYCURSOR;
if (yych != 'x') {
yystate = 2;
continue;
}
++YYCURSOR;
yystate = 4;
continue;
case 4:
yych = *YYCURSOR;
if (yych == ' ') {
++YYCURSOR;
yystate = 6;
continue;
}
yystate = 5;
continue;
case 5:
YYCURSOR = YYMARKER;
yystate = 2;
continue;
case 6:
yych = *YYCURSOR;
if (yych != 0x00002203) {
yystate = 5;
continue;
}
++YYCURSOR;
yystate = 7;
continue;
case 7:
yych = *YYCURSOR;
if (yych != 'y') {
yystate = 5;
continue;
}
++YYCURSOR;
yystate = 8;
continue;
case 8:
yystate = 9;
continue;
case 9:
{ return 0; }
}
}
}
}
int main()
{
static const uint8_t s8[] = // UTF-8
{ 0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79 };
static const uint32_t s32[] = // UTF32
{ 0x00002200, 0x00000078, 0x00000020, 0x00002203, 0x00000079 };
assert(lex_utf8(s8) == 0);
assert(lex_utf32(s32) == 0);
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
// re2c $INPUT -o $OUTPUT --input-encoding utf8 --loop-switch -i
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
#include <assert.h>
#include <stdint.h>
/*!rules:re2c
re2c:yyfill:enable = 0;
"∀x ∃y" { return 0; }
* { return 1; }
*/
static int lex_utf8(const uint8_t *YYCURSOR)
{
const uint8_t *YYMARKER;
/*!use:re2c
re2c:define:YYCTYPE = uint8_t;
re2c:flags:8 = 1;
*/
}
static int lex_utf32(const uint32_t *YYCURSOR)
{
const uint32_t *YYMARKER;
/*!use:re2c
re2c:define:YYCTYPE = uint32_t;
re2c:flags:8 = 0;
re2c:flags:u = 1;
*/
}
int main()
{
static const uint8_t s8[] = // UTF-8
{ 0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79 };
static const uint32_t s32[] = // UTF32
{ 0x00002200, 0x00000078, 0x00000020, 0x00002203, 0x00000079 };
assert(lex_utf8(s8) == 0);
assert(lex_utf32(s32) == 0);
return 0;
}
+137
View File
@@ -0,0 +1,137 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
// expect a null-terminated string
static int lex(const char *YYCURSOR)
{
int count = 0;
loop:
{
char yych;
unsigned int yystate = 0;
for (;;) {
switch (yystate) {
case 0:
yych = *YYCURSOR;
++YYCURSOR;
switch (yych) {
case 0x00:
yystate = 1;
continue;
case ' ':
yystate = 5;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
yystate = 8;
continue;
default:
yystate = 3;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
{ return count; }
case 3:
yystate = 4;
continue;
case 4:
{ return -1; }
case 5:
yych = *YYCURSOR;
yystate = 6;
continue;
case 6:
switch (yych) {
case ' ':
++YYCURSOR;
yystate = 5;
continue;
default:
yystate = 7;
continue;
}
case 7:
{ goto loop; }
case 8:
yych = *YYCURSOR;
yystate = 9;
continue;
case 9:
switch (yych) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
++YYCURSOR;
yystate = 8;
continue;
default:
yystate = 10;
continue;
}
case 10:
{ ++count; goto loop; }
}
}
}
}
int main()
{
assert(lex("") == 0);
assert(lex("one two three") == 3);
assert(lex("f0ur") == -1);
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
// re2c $INPUT -o $OUTPUT --loop-switch -i
#include <assert.h>
// expect a null-terminated string
static int lex(const char *YYCURSOR)
{
int count = 0;
loop:
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
* { return -1; }
[\x00] { return count; }
[a-z]+ { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
int main()
{
assert(lex("") == 0);
assert(lex("one two three") == 3);
assert(lex("f0ur") == -1);
return 0;
}
+298
View File
@@ -0,0 +1,298 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -f --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define DEBUG 1
#define LOG(...) if (DEBUG) fprintf(stderr, __VA_ARGS__);
#define BUFSIZE 10
typedef struct {
FILE *file;
char buf[BUFSIZE + 1], *lim, *cur, *mar, *tok;
unsigned yyaccept;
int state;
} Input;
static void init(Input *in, FILE *f)
{
in->file = f;
in->cur = in->mar = in->tok = in->lim = in->buf + BUFSIZE;
in->lim[0] = 0; // append sentinel symbol
in->yyaccept = 0;
in->state = -1;
}
typedef enum {END, READY, WAITING, BAD_PACKET, BIG_PACKET} Status;
static Status fill(Input *in)
{
const size_t shift = in->tok - in->buf;
const size_t free = BUFSIZE - (in->lim - in->tok);
if (free < 1) return BIG_PACKET;
memmove(in->buf, in->tok, BUFSIZE - shift);
in->lim -= shift;
in->cur -= shift;
in->mar -= shift;
in->tok -= shift;
const size_t read = fread(in->lim, 1, free, in->file);
in->lim += read;
in->lim[0] = 0; // append sentinel symbol
return READY;
}
static Status lex(Input *in, unsigned int *recv)
{
char yych;
loop:
int yystate = in->state;
for (;;) {
switch (yystate) {
case -1:
case 0:
yych = *in->cur;
switch (yych) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
++in->cur;
yystate = 3;
continue;
default:
if (in->lim <= in->cur) {
in->state = 10;
return WAITING;
}
++in->cur;
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
in->state = 0;
{ return BAD_PACKET; }
case 3:
in->mar = in->cur;
yych = *in->cur;
switch (yych) {
case ';':
++in->cur;
yystate = 4;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
++in->cur;
yystate = 6;
continue;
default:
if (in->lim <= in->cur) {
in->state = 11;
return WAITING;
}
yystate = 2;
continue;
}
case 4:
yystate = 5;
continue;
case 5:
in->state = 0;
{ *recv = *recv + 1; in->tok = in->cur; goto loop; }
case 6:
yych = *in->cur;
yystate = 7;
continue;
case 7:
switch (yych) {
case ';':
++in->cur;
yystate = 4;
continue;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
++in->cur;
yystate = 6;
continue;
default:
if (in->lim <= in->cur) {
in->state = 12;
return WAITING;
}
yystate = 8;
continue;
}
case 8:
in->cur = in->mar;
yystate = 2;
continue;
case 9:
in->state = 0;
{ return END; }
case 10:
if (in->lim <= in->cur) {
yystate = 9;
continue;
}
yystate = 0;
continue;
case 11:
if (in->lim <= in->cur) {
yystate = 2;
continue;
}
yystate = 3;
continue;
case 12:
if (in->lim <= in->cur) {
yystate = 8;
continue;
}
yystate = 6;
continue;
}
}
}
void test(const char **packets, Status status)
{
const char *fname = "pipe";
FILE *fw = fopen(fname, "w");
FILE *fr = fopen(fname, "r");
setvbuf(fw, NULL, _IONBF, 0);
setvbuf(fr, NULL, _IONBF, 0);
Input in;
init(&in, fr);
Status st;
unsigned int send = 0, recv = 0;
for (;;) {
st = lex(&in, &recv);
if (st == END) {
LOG("done: got %u packets\n", recv);
break;
} else if (st == WAITING) {
LOG("waiting...\n");
if (*packets) {
LOG("sent packet %u\n", send);
fprintf(fw, "%s", *packets++);
++send;
}
st = fill(&in);
LOG("queue: '%s'\n", in.buf);
if (st == BIG_PACKET) {
LOG("error: packet too big\n");
break;
}
assert(st == READY);
} else {
assert(st == BAD_PACKET);
LOG("error: ill-formed packet\n");
break;
}
}
LOG("\n");
assert(st == status);
if (st == END) assert(recv == send);
fclose(fw);
fclose(fr);
remove(fname);
}
int main()
{
const char *packets1[] = {0};
const char *packets2[] = {"zero;", "one;", "two;", "three;", "four;", 0};
const char *packets3[] = {"zer0;", 0};
const char *packets4[] = {"goooooooooogle;", 0};
test(packets1, END);
test(packets2, END);
test(packets3, BAD_PACKET);
test(packets4, BIG_PACKET);
return 0;
}
@@ -0,0 +1,132 @@
// re2c $INPUT -o $OUTPUT -f --loop-switch -i
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define DEBUG 1
#define LOG(...) if (DEBUG) fprintf(stderr, __VA_ARGS__);
#define BUFSIZE 10
typedef struct {
FILE *file;
char buf[BUFSIZE + 1], *lim, *cur, *mar, *tok;
unsigned yyaccept;
int state;
} Input;
static void init(Input *in, FILE *f)
{
in->file = f;
in->cur = in->mar = in->tok = in->lim = in->buf + BUFSIZE;
in->lim[0] = 0; // append sentinel symbol
in->yyaccept = 0;
in->state = -1;
}
typedef enum {END, READY, WAITING, BAD_PACKET, BIG_PACKET} Status;
static Status fill(Input *in)
{
const size_t shift = in->tok - in->buf;
const size_t free = BUFSIZE - (in->lim - in->tok);
if (free < 1) return BIG_PACKET;
memmove(in->buf, in->tok, BUFSIZE - shift);
in->lim -= shift;
in->cur -= shift;
in->mar -= shift;
in->tok -= shift;
const size_t read = fread(in->lim, 1, free, in->file);
in->lim += read;
in->lim[0] = 0; // append sentinel symbol
return READY;
}
static Status lex(Input *in, unsigned int *recv)
{
char yych;
loop:
/*!re2c
re2c:eof = 0;
re2c:api:style = free-form;
re2c:define:YYCTYPE = "char";
re2c:define:YYCURSOR = "in->cur";
re2c:define:YYMARKER = "in->mar";
re2c:define:YYLIMIT = "in->lim";
re2c:define:YYGETSTATE = "in->state";
re2c:define:YYSETSTATE = "in->state = @@;";
re2c:define:YYFILL = "return WAITING;";
packet = [a-z]+[;];
* { return BAD_PACKET; }
$ { return END; }
packet { *recv = *recv + 1; in->tok = in->cur; goto loop; }
*/
}
void test(const char **packets, Status status)
{
const char *fname = "pipe";
FILE *fw = fopen(fname, "w");
FILE *fr = fopen(fname, "r");
setvbuf(fw, NULL, _IONBF, 0);
setvbuf(fr, NULL, _IONBF, 0);
Input in;
init(&in, fr);
Status st;
unsigned int send = 0, recv = 0;
for (;;) {
st = lex(&in, &recv);
if (st == END) {
LOG("done: got %u packets\n", recv);
break;
} else if (st == WAITING) {
LOG("waiting...\n");
if (*packets) {
LOG("sent packet %u\n", send);
fprintf(fw, "%s", *packets++);
++send;
}
st = fill(&in);
LOG("queue: '%s'\n", in.buf);
if (st == BIG_PACKET) {
LOG("error: packet too big\n");
break;
}
assert(st == READY);
} else {
assert(st == BAD_PACKET);
LOG("error: ill-formed packet\n");
break;
}
}
LOG("\n");
assert(st == status);
if (st == END) assert(recv == send);
fclose(fw);
fclose(fr);
remove(fname);
}
int main()
{
const char *packets1[] = {0};
const char *packets2[] = {"zero;", "one;", "two;", "three;", "four;", 0};
const char *packets3[] = {"zer0;", 0};
const char *packets4[] = {"goooooooooogle;", 0};
test(packets1, END);
test(packets2, END);
test(packets3, BAD_PACKET);
test(packets4, BIG_PACKET);
return 0;
}
@@ -0,0 +1 @@
codegen/loop_switch/c/storable_state_error_getstate.re:3:16: error: `getstate:re2c` is incompatible with the --loop-switch option, as it requires cross-block transitions that are unsupported without the `goto` statement
@@ -0,0 +1,3 @@
// re2c $INPUT -o $OUTPUT -f --loop-switch
/*!getstate:re2c*/
@@ -0,0 +1,88 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -fi --loop-switch
int yystate = YYGETSTATE();
for (;;) {
switch (yystate) {
case -1:
case 0:
YYSETSTATE(5);
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
++YYCURSOR;
switch (yych) {
case 'a':
yystate = 3;
continue;
default:
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
YYSETSTATE(0);
{ * }
case 3:
yystate = 4;
continue;
case 4:
YYSETSTATE(0);
{ a }
case 5:
yystate = 0;
continue;
}
}
int yystate = YYGETSTATE();
for (;;) {
switch (yystate) {
case -1:
case 0:
yych = *YYCURSOR;
switch (yych) {
case 'b':
++YYCURSOR;
yystate = 3;
continue;
default:
if (YYLIMIT <= YYCURSOR) {
YYSETSTATE(6);
YYFILL();
}
++YYCURSOR;
yystate = 1;
continue;
}
case 1:
yystate = 2;
continue;
case 2:
YYSETSTATE(0);
{ * }
case 3:
yystate = 4;
continue;
case 4:
YYSETSTATE(0);
{ b }
case 5:
YYSETSTATE(0);
{ $ }
case 6:
if (YYLIMIT <= YYCURSOR) {
yystate = 5;
continue;
}
yystate = 0;
continue;
}
}
@@ -0,0 +1,13 @@
// re2c $INPUT -o $OUTPUT -fi --loop-switch
/*!re2c
[a] { a }
* { * }
*/
/*!re2c
re2c:eof = 0;
[b] { b }
$ { $ }
* { * }
*/
+59
View File
@@ -0,0 +1,59 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main //
//
func lex(str string) { // Go code
var cursor int //
//
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
if (yych <= '0') {
yystate = 1
continue
}
if (yych <= '9') {
yystate = 3
continue
}
yystate = 1
continue
case 1:
yystate = 2
continue
case 2:
{ panic("error!"); }
case 3:
yych = str[cursor]
yystate = 4
continue
case 4:
if (yych <= '/') {
yystate = 5
continue
}
if (yych <= '9') {
cursor += 1
yystate = 3
continue
}
yystate = 5
continue
case 5:
{ return }
}
}
}
} //
//
func main() { // Go code
lex("1234\x00") //
} //
+24
View File
@@ -0,0 +1,24 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main //
//
func lex(str string) { // Go code
var cursor int //
//
/*!re2c
re2c:define:YYCTYPE = byte; //
re2c:define:YYPEEK = "str[cursor]"; //
re2c:define:YYSKIP = "cursor += 1"; // configurations
re2c:yyfill:enable = 0; //
re2c:flags:nested-ifs = 1; //
//
number = [1-9][0-9]*; // named definition
//
number { return } // rules
* { panic("error!"); } //
*/
} //
//
func main() { // Go code
lex("1234\x00") //
} //
@@ -0,0 +1,149 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"strings"
"testing"
)
var YYMAXFILL int = 1
// Expects YYMAXFILL-padded string.
func lex(str string) int {
var cursor int
limit := len(str)
count := 0
loop:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
if (limit - cursor < 1) {
return -1
}
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case ' ':
yystate = 5
continue
case '\'':
yystate = 8
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{
if limit - cursor == YYMAXFILL - 1 {
return count
} else {
return -1
}
}
case 3:
yystate = 4
continue
case 4:
{
return -1
}
case 5:
if (limit - cursor < 1) {
return -1
}
yych = str[cursor]
yystate = 6
continue
case 6:
switch (yych) {
case ' ':
cursor += 1
yystate = 5
continue
default:
yystate = 7
continue
}
case 7:
{
goto loop
}
case 8:
if (limit - cursor < 1) {
return -1
}
yych = str[cursor]
yystate = 9
continue
case 9:
cursor += 1
switch (yych) {
case '\'':
yystate = 10
continue
case '\\':
yystate = 12
continue
default:
yystate = 8
continue
}
case 10:
yystate = 11
continue
case 11:
{
count += 1;
goto loop
}
case 12:
if (limit - cursor < 1) {
return -1
}
yych = str[cursor]
cursor += 1
yystate = 8
continue
}
}
}
}
// Pad string with YYMAXFILL zeroes at the end.
func pad(str string) string {
return str + strings.Repeat("\000", YYMAXFILL)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(pad(x.str))
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,67 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"strings"
"testing"
)
/*!max:re2c*/
// Expects YYMAXFILL-padded string.
func lex(str string) int {
var cursor int
limit := len(str)
count := 0
loop:
/*!re2c
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYLESSTHAN = "limit - cursor < @@{len}";
re2c:define:YYFILL = "return -1";
* {
return -1
}
[\x00] {
if limit - cursor == YYMAXFILL - 1 {
return count
} else {
return -1
}
}
['] ([^'\\] | [\\][^])* ['] {
count += 1;
goto loop
}
[ ]+ {
goto loop
}
*/
}
// Pad string with YYMAXFILL zeroes at the end.
func pad(str string) string {
return str + strings.Repeat("\000", YYMAXFILL)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(pad(x.str))
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+146
View File
@@ -0,0 +1,146 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Expects a null-terminated string.
func lex(str string) int {
var cursor, marker int
limit := len(str) - 1 // limit points at the terminating null
count := 0
loop:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
switch (yych) {
case ' ':
cursor += 1
yystate = 3
continue
case '\'':
cursor += 1
yystate = 6
continue
default:
if (limit <= cursor) {
yystate = 12
continue
}
cursor += 1
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ return -1 }
case 3:
yych = str[cursor]
yystate = 4
continue
case 4:
switch (yych) {
case ' ':
cursor += 1
yystate = 3
continue
default:
yystate = 5
continue
}
case 5:
{ goto loop }
case 6:
marker = cursor
yych = str[cursor]
if (yych >= 0x01) {
yystate = 8
continue
}
if (limit <= cursor) {
yystate = 2
continue
}
cursor += 1
yystate = 7
continue
case 7:
yych = str[cursor]
yystate = 8
continue
case 8:
switch (yych) {
case '\'':
cursor += 1
yystate = 9
continue
case '\\':
cursor += 1
yystate = 11
continue
default:
if (limit <= cursor) {
yystate = 13
continue
}
cursor += 1
yystate = 7
continue
}
case 9:
yystate = 10
continue
case 10:
{ count += 1; goto loop }
case 11:
yych = str[cursor]
if (yych <= 0x00) {
if (limit <= cursor) {
yystate = 13
continue
}
cursor += 1
yystate = 7
continue
}
cursor += 1
yystate = 7
continue
case 12:
{ return count }
case 13:
cursor = marker
yystate = 2
continue
}
}
}
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, "\000"},
{3, "'qu\000tes' 'are' 'fine: \\'' \000"},
{-1, "'unterminated\\'\000"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+47
View File
@@ -0,0 +1,47 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Expects a null-terminated string.
func lex(str string) int {
var cursor, marker int
limit := len(str) - 1 // limit points at the terminating null
count := 0
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:eof = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYBACKUP = "marker = cursor";
re2c:define:YYRESTORE = "cursor = marker";
re2c:define:YYLESSTHAN = "limit <= cursor";
* { return -1 }
$ { return count }
['] ([^'\\] | [\\][^])* ['] { count += 1; goto loop }
[ ]+ { goto loop }
*/
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, "\000"},
{3, "'qu\000tes' 'are' 'fine: \\'' \000"},
{-1, "'unterminated\\'\000"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+249
View File
@@ -0,0 +1,249 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"os"
"testing"
)
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
eof bool
}
func fill(in *Input) int {
// If nothing can be read, fail.
if in.eof {
return 1
}
// Check if at least some space can be freed.
if in.token == 0 {
// In real life can reallocate a larger buffer.
panic("fill error: lexeme too long")
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
in.data[in.limit] = 0
// If read less than expected, this is the end of input.
in.eof = in.limit < SIZE
// If nothing has been read, fail.
if n == 0 {
return 1
}
return 0
}
func lex(in *Input) int {
count := 0
loop:
in.token = in.cursor
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = in.data[in.cursor]
switch (yych) {
case ' ':
in.cursor += 1
yystate = 3
continue
case '\'':
in.cursor += 1
yystate = 6
continue
default:
if (in.limit <= in.cursor) {
if (fill(in) == 0) {
yystate = 0
continue
}
yystate = 12
continue
}
in.cursor += 1
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ return -1 }
case 3:
yych = in.data[in.cursor]
yystate = 4
continue
case 4:
switch (yych) {
case ' ':
in.cursor += 1
yystate = 3
continue
default:
if (in.limit <= in.cursor) {
if (fill(in) == 0) {
yystate = 3
continue
}
}
yystate = 5
continue
}
case 5:
{ goto loop }
case 6:
in.marker = in.cursor
yych = in.data[in.cursor]
if (yych >= 0x01) {
yystate = 8
continue
}
if (in.limit <= in.cursor) {
if (fill(in) == 0) {
yystate = 6
continue
}
yystate = 2
continue
}
in.cursor += 1
yystate = 7
continue
case 7:
yych = in.data[in.cursor]
yystate = 8
continue
case 8:
switch (yych) {
case '\'':
in.cursor += 1
yystate = 9
continue
case '\\':
in.cursor += 1
yystate = 11
continue
default:
if (in.limit <= in.cursor) {
if (fill(in) == 0) {
yystate = 7
continue
}
yystate = 13
continue
}
in.cursor += 1
yystate = 7
continue
}
case 9:
yystate = 10
continue
case 10:
{ count += 1; goto loop }
case 11:
yych = in.data[in.cursor]
if (yych <= 0x00) {
if (in.limit <= in.cursor) {
if (fill(in) == 0) {
yystate = 11
continue
}
yystate = 13
continue
}
in.cursor += 1
yystate = 7
continue
}
in.cursor += 1
yystate = 7
continue
case 12:
{ return count }
case 13:
in.cursor = in.marker
yystate = 2
continue
}
}
}
}
// Prepare a file with the input text and run the lexer.
func test(data string) (result int) {
tmpfile := "input.txt"
f, _ := os.Create(tmpfile)
f.WriteString(data)
f.Seek(0, 0)
defer func() {
if r := recover(); r != nil {
result = -2
}
f.Close()
os.Remove(tmpfile)
}()
in := &Input{
file: f,
data: make([]byte, SIZE+1),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
eof: false,
}
return lex(in)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{2, "'one' 'two'"},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
{-2, "'loooooooooooong'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := test(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+128
View File
@@ -0,0 +1,128 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"os"
"testing"
)
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
eof bool
}
func fill(in *Input) int {
// If nothing can be read, fail.
if in.eof {
return 1
}
// Check if at least some space can be freed.
if in.token == 0 {
// In real life can reallocate a larger buffer.
panic("fill error: lexeme too long")
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
in.data[in.limit] = 0
// If read less than expected, this is the end of input.
in.eof = in.limit < SIZE
// If nothing has been read, fail.
if n == 0 {
return 1
}
return 0
}
func lex(in *Input) int {
count := 0
loop:
in.token = in.cursor
/*!re2c
re2c:eof = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "in.data[in.cursor]";
re2c:define:YYSKIP = "in.cursor += 1";
re2c:define:YYBACKUP = "in.marker = in.cursor";
re2c:define:YYRESTORE = "in.cursor = in.marker";
re2c:define:YYLESSTHAN = "in.limit <= in.cursor";
re2c:define:YYFILL = "fill(in) == 0";
* { return -1 }
$ { return count }
['] ([^'\\] | [\\][^])* ['] { count += 1; goto loop }
[ ]+ { goto loop }
*/
}
// Prepare a file with the input text and run the lexer.
func test(data string) (result int) {
tmpfile := "input.txt"
f, _ := os.Create(tmpfile)
f.WriteString(data)
f.Seek(0, 0)
defer func() {
if r := recover(); r != nil {
result = -2
}
f.Close()
os.Remove(tmpfile)
}()
in := &Input{
file: f,
data: make([]byte, SIZE+1),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
eof: false,
}
return lex(in)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{2, "'one' 'two'"},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
{-2, "'loooooooooooong'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := test(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+227
View File
@@ -0,0 +1,227 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"fmt"
"os"
"testing"
)
var YYMAXFILL int = 1
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
eof bool
}
func fill(in *Input, need int) int {
// End of input has already been reached, nothing to do.
if in.eof {
return -1 // Error: unexpected EOF
}
// Check if after moving the current lexeme to the beginning
// of buffer there will be enough free space.
if SIZE-(in.cursor-in.token) < need {
return -2 // Error: lexeme too long
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
// If read less than expected, this is the end of input.
in.eof = in.limit < SIZE
// If end of input, add padding so that the lexer can read
// the remaining characters at the end of buffer.
if in.eof {
for i := 0; i < YYMAXFILL; i += 1 {
in.data[in.limit+i] = 0
}
in.limit += YYMAXFILL
}
return 0
}
func lex(in *Input) int {
count := 0
loop:
in.token = in.cursor
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
if (in.limit-in.cursor < 1) {
if r := fill(in, 1); r != 0 { return r }
}
yych = in.data[in.cursor]
in.cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case ' ':
yystate = 5
continue
case '\'':
yystate = 8
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{
if in.limit - in.cursor == YYMAXFILL - 1 {
return count
} else {
return -1
}
}
case 3:
yystate = 4
continue
case 4:
{
return -1
}
case 5:
if (in.limit-in.cursor < 1) {
if r := fill(in, 1); r != 0 { return r }
}
yych = in.data[in.cursor]
yystate = 6
continue
case 6:
switch (yych) {
case ' ':
in.cursor += 1
yystate = 5
continue
default:
yystate = 7
continue
}
case 7:
{
goto loop
}
case 8:
if (in.limit-in.cursor < 1) {
if r := fill(in, 1); r != 0 { return r }
}
yych = in.data[in.cursor]
yystate = 9
continue
case 9:
in.cursor += 1
switch (yych) {
case '\'':
yystate = 10
continue
case '\\':
yystate = 12
continue
default:
yystate = 8
continue
}
case 10:
yystate = 11
continue
case 11:
{
count += 1;
goto loop
}
case 12:
if (in.limit-in.cursor < 1) {
if r := fill(in, 1); r != 0 { return r }
}
yych = in.data[in.cursor]
in.cursor += 1
yystate = 8
continue
}
}
}
}
// Prepare a file with the input text and run the lexer.
func test(data string) (result int) {
tmpfile := "input.txt"
f, _ := os.Create(tmpfile)
f.WriteString(data)
f.Seek(0, 0)
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
result = -2
}
f.Close()
os.Remove(tmpfile)
}()
in := &Input{
file: f,
data: make([]byte, SIZE+YYMAXFILL),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
eof: false,
}
return lex(in)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{2, "'one' 'two'"},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
{-2, "'loooooooooooong'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := test(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+147
View File
@@ -0,0 +1,147 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"fmt"
"os"
"testing"
)
/*!max:re2c*/
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
eof bool
}
func fill(in *Input, need int) int {
// End of input has already been reached, nothing to do.
if in.eof {
return -1 // Error: unexpected EOF
}
// Check if after moving the current lexeme to the beginning
// of buffer there will be enough free space.
if SIZE-(in.cursor-in.token) < need {
return -2 // Error: lexeme too long
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
// If read less than expected, this is the end of input.
in.eof = in.limit < SIZE
// If end of input, add padding so that the lexer can read
// the remaining characters at the end of buffer.
if in.eof {
for i := 0; i < YYMAXFILL; i += 1 {
in.data[in.limit+i] = 0
}
in.limit += YYMAXFILL
}
return 0
}
func lex(in *Input) int {
count := 0
loop:
in.token = in.cursor
/*!re2c
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "in.data[in.cursor]";
re2c:define:YYSKIP = "in.cursor += 1";
re2c:define:YYBACKUP = "in.marker = in.cursor";
re2c:define:YYRESTORE = "in.cursor = in.marker";
re2c:define:YYLESSTHAN = "in.limit-in.cursor < @@{len}";
re2c:define:YYFILL = "if r := fill(in, @@{len}); r != 0 { return r }";
* {
return -1
}
[\x00] {
if in.limit - in.cursor == YYMAXFILL - 1 {
return count
} else {
return -1
}
}
['] ([^'\\] | [\\][^])* ['] {
count += 1;
goto loop
}
[ ]+ {
goto loop
}
*/
}
// Prepare a file with the input text and run the lexer.
func test(data string) (result int) {
tmpfile := "input.txt"
f, _ := os.Create(tmpfile)
f.WriteString(data)
f.Seek(0, 0)
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
result = -2
}
f.Close()
os.Remove(tmpfile)
}()
in := &Input{
file: f,
data: make([]byte, SIZE+YYMAXFILL),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
eof: false,
}
return lex(in)
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{2, "'one' 'two'"},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
{-2, "'loooooooooooong'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := test(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,155 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Returns "fake" terminating null if cursor has reached limit.
func peek(str string, cursor int, limit int) byte {
if cursor >= limit {
return 0 // fake null
} else {
return str[cursor]
}
}
// Expects a string without terminating null.
func lex(str string) int {
var cursor, marker int
limit := len(str)
count := 0
loop:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = peek(str, cursor, limit)
switch (yych) {
case ' ':
cursor += 1
yystate = 3
continue
case '\'':
cursor += 1
yystate = 6
continue
default:
if (cursor >= limit) {
yystate = 12
continue
}
cursor += 1
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ return -1 }
case 3:
yych = peek(str, cursor, limit)
yystate = 4
continue
case 4:
switch (yych) {
case ' ':
cursor += 1
yystate = 3
continue
default:
yystate = 5
continue
}
case 5:
{ goto loop }
case 6:
marker = cursor
yych = peek(str, cursor, limit)
if (yych >= 0x01) {
yystate = 8
continue
}
if (cursor >= limit) {
yystate = 2
continue
}
cursor += 1
yystate = 7
continue
case 7:
yych = peek(str, cursor, limit)
yystate = 8
continue
case 8:
switch (yych) {
case '\'':
cursor += 1
yystate = 9
continue
case '\\':
cursor += 1
yystate = 11
continue
default:
if (cursor >= limit) {
yystate = 13
continue
}
cursor += 1
yystate = 7
continue
}
case 9:
yystate = 10
continue
case 10:
{ count += 1; goto loop }
case 11:
yych = peek(str, cursor, limit)
if (yych <= 0x00) {
if (cursor >= limit) {
yystate = 13
continue
}
cursor += 1
yystate = 7
continue
}
cursor += 1
yystate = 7
continue
case 12:
{ return count }
case 13:
cursor = marker
yystate = 2
continue
}
}
}
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,56 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Returns "fake" terminating null if cursor has reached limit.
func peek(str string, cursor int, limit int) byte {
if cursor >= limit {
return 0 // fake null
} else {
return str[cursor]
}
}
// Expects a string without terminating null.
func lex(str string) int {
var cursor, marker int
limit := len(str)
count := 0
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:eof = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYLESSTHAN = "cursor >= limit";
re2c:define:YYPEEK = "peek(str, cursor, limit)";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYBACKUP = "marker = cursor";
re2c:define:YYRESTORE = "cursor = marker";
* { return -1 }
$ { return count }
['] ([^'\\] | [\\][^])* ['] { count += 1; goto loop }
[ ]+ { goto loop }
*/
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "'qu\000tes' 'are' 'fine: \\'' "},
{-1, "'unterminated\\'"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,211 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Returns "fake" terminating null if cursor has reached limit.
func peek(str string, cursor int, limit int) byte {
if cursor >= limit {
return 0 // fake null
} else {
return str[cursor]
}
}
// Expects a string without terminating null.
func lex(str string) int {
var cursor int
limit := len(str)
count := 0
loop:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = peek(str, cursor, limit)
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case ' ':
yystate = 5
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
yystate = 8
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ return count }
case 3:
yystate = 4
continue
case 4:
{ return -1 }
case 5:
yych = peek(str, cursor, limit)
yystate = 6
continue
case 6:
switch (yych) {
case ' ':
cursor += 1
yystate = 5
continue
default:
yystate = 7
continue
}
case 7:
{ goto loop }
case 8:
yych = peek(str, cursor, limit)
yystate = 9
continue
case 9:
switch (yych) {
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
cursor += 1
yystate = 8
continue
default:
yystate = 10
continue
}
case 10:
{ count += 1; goto loop }
}
}
}
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "one two three"},
{-1, "f0ur"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,53 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// Returns "fake" terminating null if cursor has reached limit.
func peek(str string, cursor int, limit int) byte {
if cursor >= limit {
return 0 // fake null
} else {
return str[cursor]
}
}
// Expects a string without terminating null.
func lex(str string) int {
var cursor int
limit := len(str)
count := 0
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYLESSTHAN = "cursor >= limit";
re2c:define:YYPEEK = "peek(str, cursor, limit)";
re2c:define:YYSKIP = "cursor += 1";
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; goto loop }
[ ]+ { goto loop }
*/
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, ""},
{3, "one two three"},
{-1, "f0ur"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,463 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"errors"
"testing"
)
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
const u32Limit uint64 = 1<<32
func parse_u32(str string) (uint32, error) {
var cursor, marker int
result := uint64(0)
add_digit := func(base uint64, offset byte) {
result = result * base + uint64(str[cursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case '0':
yystate = 3
continue
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 5
continue
default:
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ goto err }
case 3:
marker = cursor
yych = str[cursor]
switch (yych) {
case 'B':
fallthrough
case 'b':
cursor += 1
yystate = 7
continue
case 'X':
fallthrough
case 'x':
cursor += 1
yystate = 9
continue
default:
yystate = 4
continue
}
case 4:
{ goto oct }
case 5:
yystate = 6
continue
case 6:
cursor += -1
{ goto dec }
case 7:
yych = str[cursor]
switch (yych) {
case '0':
fallthrough
case '1':
cursor += 1
yystate = 10
continue
default:
yystate = 8
continue
}
case 8:
cursor = marker
yystate = 4
continue
case 9:
yych = str[cursor]
switch (yych) {
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
fallthrough
case 'A':
fallthrough
case 'B':
fallthrough
case 'C':
fallthrough
case 'D':
fallthrough
case 'E':
fallthrough
case 'F':
fallthrough
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
cursor += 1
yystate = 12
continue
default:
yystate = 8
continue
}
case 10:
yystate = 11
continue
case 11:
cursor += -1
{ goto bin }
case 12:
yystate = 13
continue
case 13:
cursor += -1
{ goto hex }
}
}
}
bin:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case '0':
fallthrough
case '1':
yystate = 5
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ goto end }
case 3:
yystate = 4
continue
case 4:
{ goto err }
case 5:
yystate = 6
continue
case 6:
{ add_digit(2, '0'); goto bin }
}
}
}
oct:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
yystate = 5
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ goto end }
case 3:
yystate = 4
continue
case 4:
{ goto err }
case 5:
yystate = 6
continue
case 6:
{ add_digit(8, '0'); goto oct }
}
}
}
dec:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 5
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ goto end }
case 3:
yystate = 4
continue
case 4:
{ goto err }
case 5:
yystate = 6
continue
case 6:
{ add_digit(10, '0'); goto dec }
}
}
}
hex:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 5
continue
case 'A':
fallthrough
case 'B':
fallthrough
case 'C':
fallthrough
case 'D':
fallthrough
case 'E':
fallthrough
case 'F':
yystate = 7
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
yystate = 9
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ goto end }
case 3:
yystate = 4
continue
case 4:
{ goto err }
case 5:
yystate = 6
continue
case 6:
{ add_digit(16, '0'); goto hex }
case 7:
yystate = 8
continue
case 8:
{ add_digit(16, 'A'-10); goto hex }
case 9:
yystate = 10
continue
case 10:
{ add_digit(16, 'a'-10); goto hex }
}
}
}
end:
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
err:
return 0, eSyntax
}
func TestLex(t *testing.T) {
var tests = []struct {
num uint32
str string
err error
}{
{1234567890, "1234567890\000", nil},
{13, "0b1101\000", nil},
{0x7fe, "0x007Fe\000", nil},
{0644, "0644\000", nil},
{0, "9999999999\000", eOverflow},
{0, "123??\000", eSyntax},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
num, err := parse_u32(x.str)
if !(num == x.num && err == x.err) {
t.Errorf("got %d, want %d", num, x.num)
}
})
}
}
@@ -0,0 +1,102 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"errors"
"testing"
)
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
const u32Limit uint64 = 1<<32
func parse_u32(str string) (uint32, error) {
var cursor, marker int
result := uint64(0)
add_digit := func(base uint64, offset byte) {
result = result * base + uint64(str[cursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYSHIFT = "cursor += @@{shift}";
re2c:define:YYBACKUP = "marker = cursor";
re2c:define:YYRESTORE = "cursor = marker";
end = "\x00";
'0b' / [01] { goto bin }
"0" { goto oct }
"" / [1-9] { goto dec }
'0x' / [0-9a-fA-F] { goto hex }
* { goto err }
*/
bin:
/*!re2c
end { goto end }
[01] { add_digit(2, '0'); goto bin }
* { goto err }
*/
oct:
/*!re2c
end { goto end }
[0-7] { add_digit(8, '0'); goto oct }
* { goto err }
*/
dec:
/*!re2c
end { goto end }
[0-9] { add_digit(10, '0'); goto dec }
* { goto err }
*/
hex:
/*!re2c
end { goto end }
[0-9] { add_digit(16, '0'); goto hex }
[a-f] { add_digit(16, 'a'-10); goto hex }
[A-F] { add_digit(16, 'A'-10); goto hex }
* { goto err }
*/
end:
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
err:
return 0, eSyntax
}
func TestLex(t *testing.T) {
var tests = []struct {
num uint32
str string
err error
}{
{1234567890, "1234567890\000", nil},
{13, "0b1101\000", nil},
{0x7fe, "0x007Fe\000", nil},
{0644, "0644\000", nil},
{0, "9999999999\000", eOverflow},
{0, "123??\000", eSyntax},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
num, err := parse_u32(x.str)
if !(num == x.num && err == x.err) {
t.Errorf("got %d, want %d", num, x.num)
}
})
}
}
@@ -0,0 +1,454 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go -c $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"errors"
"testing"
)
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
const (
yycinit = 0
yycbin = 14
yycdec = 21
yychex = 28
yycoct = 39
)
const u32Limit uint64 = 1<<32
func parse_u32(str string) (uint32, error) {
var cursor, marker int
result := uint64(0)
cond := yycinit
add_digit := func(base uint64, offset byte) {
result = result * base + uint64(str[cursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
loop:
{
var yych byte
yystate := cond
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case '0':
yystate = 3
continue
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 5
continue
default:
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ return 0, eSyntax }
case 3:
marker = cursor
yych = str[cursor]
switch (yych) {
case 'B':
fallthrough
case 'b':
cursor += 1
yystate = 7
continue
case 'X':
fallthrough
case 'x':
cursor += 1
yystate = 9
continue
default:
yystate = 4
continue
}
case 4:
cond = yycoct
yystate = yycoct
continue
case 5:
yystate = 6
continue
case 6:
cursor += -1
cond = yycdec
yystate = yycdec
continue
case 7:
yych = str[cursor]
switch (yych) {
case '0':
fallthrough
case '1':
cursor += 1
yystate = 10
continue
default:
yystate = 8
continue
}
case 8:
cursor = marker
yystate = 4
continue
case 9:
yych = str[cursor]
switch (yych) {
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
fallthrough
case 'A':
fallthrough
case 'B':
fallthrough
case 'C':
fallthrough
case 'D':
fallthrough
case 'E':
fallthrough
case 'F':
fallthrough
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
cursor += 1
yystate = 12
continue
default:
yystate = 8
continue
}
case 10:
yystate = 11
continue
case 11:
cursor += -1
cond = yycbin
yystate = yycbin
continue
case 12:
yystate = 13
continue
case 13:
cursor += -1
cond = yychex
yystate = yychex
continue
case 14:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 15
continue
case '0':
fallthrough
case '1':
yystate = 19
continue
default:
yystate = 17
continue
}
case 15:
yystate = 16
continue
case 16:
{
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
case 17:
yystate = 18
continue
case 18:
{ return 0, eSyntax }
case 19:
yystate = 20
continue
case 20:
{ add_digit(2, '0'); goto loop }
case 21:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 22
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 26
continue
default:
yystate = 24
continue
}
case 22:
yystate = 23
continue
case 23:
{
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
case 24:
yystate = 25
continue
case 25:
{ return 0, eSyntax }
case 26:
yystate = 27
continue
case 27:
{ add_digit(10, '0'); goto loop }
case 28:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 29
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
fallthrough
case '8':
fallthrough
case '9':
yystate = 33
continue
case 'A':
fallthrough
case 'B':
fallthrough
case 'C':
fallthrough
case 'D':
fallthrough
case 'E':
fallthrough
case 'F':
yystate = 35
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
yystate = 37
continue
default:
yystate = 31
continue
}
case 29:
yystate = 30
continue
case 30:
{
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
case 31:
yystate = 32
continue
case 32:
{ return 0, eSyntax }
case 33:
yystate = 34
continue
case 34:
{ add_digit(16, '0'); goto loop }
case 35:
yystate = 36
continue
case 36:
{ add_digit(16, 'A'-10); goto loop }
case 37:
yystate = 38
continue
case 38:
{ add_digit(16, 'a'-10); goto loop }
case 39:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 40
continue
case '0':
fallthrough
case '1':
fallthrough
case '2':
fallthrough
case '3':
fallthrough
case '4':
fallthrough
case '5':
fallthrough
case '6':
fallthrough
case '7':
yystate = 44
continue
default:
yystate = 42
continue
}
case 40:
yystate = 41
continue
case 41:
{
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
case 42:
yystate = 43
continue
case 43:
{ return 0, eSyntax }
case 44:
yystate = 45
continue
case 45:
{ add_digit(8, '0'); goto loop }
}
}
}
}
func TestLex(t *testing.T) {
var tests = []struct {
num uint32
str string
err error
}{
{1234567890, "1234567890\000", nil},
{13, "0b1101\000", nil},
{0x7fe, "0x007Fe\000", nil},
{0644, "0644\000", nil},
{0, "9999999999\000", eOverflow},
{0, "123??\000", eSyntax},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
num, err := parse_u32(x.str)
if !(num == x.num && err == x.err) {
t.Errorf("got %d, want %d", num, x.num)
}
})
}
}
@@ -0,0 +1,88 @@
//go:generate re2go -c $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"errors"
"testing"
)
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
/*!types:re2c*/
const u32Limit uint64 = 1<<32
func parse_u32(str string) (uint32, error) {
var cursor, marker int
result := uint64(0)
cond := yycinit
add_digit := func(base uint64, offset byte) {
result = result * base + uint64(str[cursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYSHIFT = "cursor += @@{shift}";
re2c:define:YYBACKUP = "marker = cursor";
re2c:define:YYRESTORE = "cursor = marker";
re2c:define:YYGETCONDITION = "cond";
re2c:define:YYSETCONDITION = "cond = @@";
<*> * { return 0, eSyntax }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" {
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
<bin> [01] { add_digit(2, '0'); goto loop }
<oct> [0-7] { add_digit(8, '0'); goto loop }
<dec> [0-9] { add_digit(10, '0'); goto loop }
<hex> [0-9] { add_digit(16, '0'); goto loop }
<hex> [a-f] { add_digit(16, 'a'-10); goto loop }
<hex> [A-F] { add_digit(16, 'A'-10); goto loop }
*/
}
func TestLex(t *testing.T) {
var tests = []struct {
num uint32
str string
err error
}{
{1234567890, "1234567890\000", nil},
{13, "0b1101\000", nil},
{0x7fe, "0x007Fe\000", nil},
{0644, "0644\000", nil},
{0, "9999999999\000", eOverflow},
{0, "123??\000", eSyntax},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
num, err := parse_u32(x.str)
if !(num == x.num && err == x.err) {
t.Errorf("got %d, want %d", num, x.num)
}
})
}
}
+226
View File
@@ -0,0 +1,226 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --input-encoding utf8 --loop-switch -i
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
package main
import "testing"
func lexUTF8(str []uint8) int {
var cursor, marker int
{
var yych uint8
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0xE2:
yystate = 3
continue
default:
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
{ return 1; }
case 3:
marker = cursor
yych = str[cursor]
switch (yych) {
case 0x88:
cursor += 1
yystate = 4
continue
default:
yystate = 2
continue
}
case 4:
yych = str[cursor]
switch (yych) {
case 0x80:
cursor += 1
yystate = 6
continue
default:
yystate = 5
continue
}
case 5:
cursor = marker
yystate = 2
continue
case 6:
yych = str[cursor]
switch (yych) {
case 'x':
cursor += 1
yystate = 7
continue
default:
yystate = 5
continue
}
case 7:
yych = str[cursor]
switch (yych) {
case ' ':
cursor += 1
yystate = 8
continue
default:
yystate = 5
continue
}
case 8:
yych = str[cursor]
switch (yych) {
case 0xE2:
cursor += 1
yystate = 9
continue
default:
yystate = 5
continue
}
case 9:
yych = str[cursor]
switch (yych) {
case 0x88:
cursor += 1
yystate = 10
continue
default:
yystate = 5
continue
}
case 10:
yych = str[cursor]
switch (yych) {
case 0x83:
cursor += 1
yystate = 11
continue
default:
yystate = 5
continue
}
case 11:
yych = str[cursor]
switch (yych) {
case 'y':
cursor += 1
yystate = 12
continue
default:
yystate = 5
continue
}
case 12:
yystate = 13
continue
case 13:
{ return 0; }
}
}
}
}
func lexUTF32(str []uint32) int {
var cursor, marker int
{
var yych uint32
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
if (yych == 0x00002200) {
yystate = 3
continue
}
yystate = 1
continue
case 1:
yystate = 2
continue
case 2:
{ return 1; }
case 3:
marker = cursor
yych = str[cursor]
if (yych != 'x') {
yystate = 2
continue
}
cursor += 1
yystate = 4
continue
case 4:
yych = str[cursor]
if (yych == ' ') {
cursor += 1
yystate = 6
continue
}
yystate = 5
continue
case 5:
cursor = marker
yystate = 2
continue
case 6:
yych = str[cursor]
if (yych != 0x00002203) {
yystate = 5
continue
}
cursor += 1
yystate = 7
continue
case 7:
yych = str[cursor]
if (yych != 'y') {
yystate = 5
continue
}
cursor += 1
yystate = 8
continue
case 8:
yystate = 9
continue
case 9:
{ return 0; }
}
}
}
}
func TestLex(t *testing.T) {
s8 := []uint8{0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79};
if lexUTF8(s8) != 0 {
t.Errorf("utf8 failed")
}
s32 := []uint32{0x2200, 0x78, 0x20, 0x2203, 0x79};
if lexUTF32(s32) != 0 {
t.Errorf("utf32 failed")
}
}
+48
View File
@@ -0,0 +1,48 @@
//go:generate re2go $INPUT -o $OUTPUT --input-encoding utf8 --loop-switch -i
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
package main
import "testing"
/*!rules:re2c
re2c:yyfill:enable = 0;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:define:YYBACKUP = "marker = cursor";
re2c:define:YYRESTORE = "cursor = marker";
"∀x ∃y" { return 0; }
* { return 1; }
*/
func lexUTF8(str []uint8) int {
var cursor, marker int
/*!use:re2c
re2c:flags:8 = 1;
re2c:define:YYCTYPE = uint8;
*/
}
func lexUTF32(str []uint32) int {
var cursor, marker int
/*!use:re2c
re2c:flags:u = 1;
re2c:define:YYCTYPE = uint32;
*/
}
func TestLex(t *testing.T) {
s8 := []uint8{0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79};
if lexUTF8(s8) != 0 {
t.Errorf("utf8 failed")
}
s32 := []uint32{0x2200, 0x78, 0x20, 0x2203, 0x79};
if lexUTF32(s32) != 0 {
t.Errorf("utf32 failed")
}
}
+201
View File
@@ -0,0 +1,201 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// expect a null-terminated string
func lex(str string) int {
var cursor int
count := 0
loop:
{
var yych byte
yystate := 0
for {
switch (yystate) {
case 0:
yych = str[cursor]
cursor += 1
switch (yych) {
case 0x00:
yystate = 1
continue
case ' ':
yystate = 5
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
yystate = 8
continue
default:
yystate = 3
continue
}
case 1:
yystate = 2
continue
case 2:
{ return count }
case 3:
yystate = 4
continue
case 4:
{ return -1 }
case 5:
yych = str[cursor]
yystate = 6
continue
case 6:
switch (yych) {
case ' ':
cursor += 1
yystate = 5
continue
default:
yystate = 7
continue
}
case 7:
{ goto loop }
case 8:
yych = str[cursor]
yystate = 9
continue
case 9:
switch (yych) {
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
cursor += 1
yystate = 8
continue
default:
yystate = 10
continue
}
case 10:
{ count += 1; goto loop }
}
}
}
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, "\000"},
{3, "one two three\000"},
{-1, "f0ur\000"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
+42
View File
@@ -0,0 +1,42 @@
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
package main
import "testing"
// expect a null-terminated string
func lex(str string) int {
var cursor int
count := 0
loop:
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; goto loop }
[ ]+ { goto loop }
*/
}
func TestLex(t *testing.T) {
var tests = []struct {
res int
str string
}{
{0, "\000"},
{3, "one two three\000"},
{-1, "f0ur\000"},
}
for _, x := range tests {
t.Run(x.str, func(t *testing.T) {
res := lex(x.str)
if res != x.res {
t.Errorf("got %d, want %d", res, x.res)
}
})
}
}
@@ -0,0 +1,389 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"fmt"
"os"
"testing"
)
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
state int
yyaccept int
}
const (
lexEnd = iota
lexReady
lexWaitingForInput
lexPacketBroken
lexPacketTooBig
lexCountMismatch
)
func fill(in *Input) int {
if in.token == 0 {
// Error: no space can be freed.
// In real life can reallocate a larger buffer.
return lexPacketTooBig
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
in.data[in.limit] = 0 // append sentinel symbol
return lexReady
}
func lex(in *Input, recv *int) int {
var yych byte
loop:
yystate := in.state
for {
switch (yystate) {
case -1:
fallthrough
case 0:
yych = in.data[in.cursor]
switch (yych) {
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
in.cursor += 1
yystate = 3
continue
default:
if (in.limit <= in.cursor) {
in.state = 10
return lexWaitingForInput
}
in.cursor += 1
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
in.state = 0
{ return lexPacketBroken }
case 3:
in.marker = in.cursor
yych = in.data[in.cursor]
switch (yych) {
case ';':
in.cursor += 1
yystate = 4
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
in.cursor += 1
yystate = 6
continue
default:
if (in.limit <= in.cursor) {
in.state = 11
return lexWaitingForInput
}
yystate = 2
continue
}
case 4:
yystate = 5
continue
case 5:
in.state = 0
{ *recv = *recv + 1; in.token = in.cursor; goto loop }
case 6:
yych = in.data[in.cursor]
yystate = 7
continue
case 7:
switch (yych) {
case ';':
in.cursor += 1
yystate = 4
continue
case 'a':
fallthrough
case 'b':
fallthrough
case 'c':
fallthrough
case 'd':
fallthrough
case 'e':
fallthrough
case 'f':
fallthrough
case 'g':
fallthrough
case 'h':
fallthrough
case 'i':
fallthrough
case 'j':
fallthrough
case 'k':
fallthrough
case 'l':
fallthrough
case 'm':
fallthrough
case 'n':
fallthrough
case 'o':
fallthrough
case 'p':
fallthrough
case 'q':
fallthrough
case 'r':
fallthrough
case 's':
fallthrough
case 't':
fallthrough
case 'u':
fallthrough
case 'v':
fallthrough
case 'w':
fallthrough
case 'x':
fallthrough
case 'y':
fallthrough
case 'z':
in.cursor += 1
yystate = 6
continue
default:
if (in.limit <= in.cursor) {
in.state = 12
return lexWaitingForInput
}
yystate = 8
continue
}
case 8:
in.cursor = in.marker
yystate = 2
continue
case 9:
in.state = 0
{ return lexEnd }
case 10:
if (in.limit <= in.cursor) {
yystate = 9
continue
}
yystate = 0
continue
case 11:
if (in.limit <= in.cursor) {
yystate = 2
continue
}
yystate = 3
continue
case 12:
if (in.limit <= in.cursor) {
yystate = 8
continue
}
yystate = 6
continue
}
}
}
func test(packets []string) int {
fname := "pipe"
fw, _ := os.Create(fname);
fr, _ := os.Open(fname);
in := &Input{
file: fr,
data: make([]byte, SIZE+1),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
state: -1,
}
// data is zero-initialized, no need to write sentinel
var status int
send := 0
recv := 0
loop:
for {
status = lex(in, &recv)
if status == lexEnd {
if send != recv {
status = lexCountMismatch
}
break loop
} else if status == lexWaitingForInput {
if send < len(packets) {
fw.WriteString(packets[send])
send += 1
}
status = fill(in)
if status != lexReady {
break loop
}
} else if status == lexPacketBroken {
break loop
} else {
panic("unexpected status")
}
}
fr.Close()
fw.Close()
os.Remove(fname)
return status
}
func TestLex(t *testing.T) {
var tests = []struct {
status int
packets []string
}{
{lexEnd, []string{}},
{lexEnd, []string{"zero;", "one;", "two;", "three;", "four;"}},
{lexPacketBroken, []string{"??;"}},
{lexPacketTooBig, []string{"looooooooooooong;"}},
}
for i, x := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
status := test(x.packets)
if status != x.status {
t.Errorf("got %d, want %d", status, x.status)
}
})
}
}
@@ -0,0 +1,147 @@
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
package main
import (
"fmt"
"os"
"testing"
)
// Intentionally small to trigger buffer refill.
const SIZE int = 16
type Input struct {
file *os.File
data []byte
cursor int
marker int
token int
limit int
state int
yyaccept int
}
const (
lexEnd = iota
lexReady
lexWaitingForInput
lexPacketBroken
lexPacketTooBig
lexCountMismatch
)
func fill(in *Input) int {
if in.token == 0 {
// Error: no space can be freed.
// In real life can reallocate a larger buffer.
return lexPacketTooBig
}
// Discard everything up to the start of the current lexeme,
// shift buffer contents and adjust offsets.
copy(in.data[0:], in.data[in.token:in.limit])
in.cursor -= in.token
in.marker -= in.token
in.limit -= in.token
in.token = 0
// Read new data (as much as possible to fill the buffer).
n, _ := in.file.Read(in.data[in.limit:SIZE])
in.limit += n
in.data[in.limit] = 0 // append sentinel symbol
return lexReady
}
func lex(in *Input, recv *int) int {
var yych byte
loop:
/*!re2c
re2c:eof = 0;
re2c:define:YYPEEK = "in.data[in.cursor]";
re2c:define:YYSKIP = "in.cursor += 1";
re2c:define:YYBACKUP = "in.marker = in.cursor";
re2c:define:YYRESTORE = "in.cursor = in.marker";
re2c:define:YYLESSTHAN = "in.limit <= in.cursor";
re2c:define:YYFILL = "return lexWaitingForInput";
re2c:define:YYGETSTATE = "in.state";
re2c:define:YYSETSTATE = "in.state = @@{state}";
packet = [a-z]+[;];
* { return lexPacketBroken }
$ { return lexEnd }
packet { *recv = *recv + 1; in.token = in.cursor; goto loop }
*/
}
func test(packets []string) int {
fname := "pipe"
fw, _ := os.Create(fname);
fr, _ := os.Open(fname);
in := &Input{
file: fr,
data: make([]byte, SIZE+1),
cursor: SIZE,
marker: SIZE,
token: SIZE,
limit: SIZE,
state: -1,
}
// data is zero-initialized, no need to write sentinel
var status int
send := 0
recv := 0
loop:
for {
status = lex(in, &recv)
if status == lexEnd {
if send != recv {
status = lexCountMismatch
}
break loop
} else if status == lexWaitingForInput {
if send < len(packets) {
fw.WriteString(packets[send])
send += 1
}
status = fill(in)
if status != lexReady {
break loop
}
} else if status == lexPacketBroken {
break loop
} else {
panic("unexpected status")
}
}
fr.Close()
fw.Close()
os.Remove(fname)
return status
}
func TestLex(t *testing.T) {
var tests = []struct {
status int
packets []string
}{
{lexEnd, []string{}},
{lexEnd, []string{"zero;", "one;", "two;", "three;", "four;"}},
{lexPacketBroken, []string{"??;"}},
{lexPacketTooBig, []string{"looooooooooooong;"}},
}
for i, x := range tests {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
status := test(x.packets)
if status != x.status {
t.Errorf("got %d, want %d", status, x.status)
}
})
}
}
@@ -0,0 +1 @@
codegen/loop_switch/go/storable_state_error_getstate.re:3:16: error: `getstate:re2c` is incompatible with the --loop-switch option, as it requires cross-block transitions that are unsupported without the `goto` statement
@@ -0,0 +1,3 @@
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
/*!getstate:re2c*/
@@ -0,0 +1,92 @@
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
yystate := YYGETSTATE
for {
switch (yystate) {
case -1:
fallthrough
case 0:
YYSETSTATE
if (YYLESSTHAN) {
YYFILL
}
yych = YYPEEK
YYSKIP
switch (yych) {
case 'a':
yystate = 3
continue
default:
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
YYSETSTATE
{ * }
case 3:
yystate = 4
continue
case 4:
YYSETSTATE
{ a }
case 5:
yystate = 0
continue
}
}
yystate := YYGETSTATE
for {
switch (yystate) {
case -1:
fallthrough
case 0:
yych = YYPEEK
switch (yych) {
case 'b':
YYSKIP
yystate = 3
continue
default:
if (YYLESSTHAN) {
YYSETSTATE
YYFILL
}
YYSKIP
yystate = 1
continue
}
case 1:
yystate = 2
continue
case 2:
YYSETSTATE
{ * }
case 3:
yystate = 4
continue
case 4:
YYSETSTATE
{ b }
case 5:
YYSETSTATE
{ $ }
case 6:
if (YYLESSTHAN) {
yystate = 5
continue
}
yystate = 0
continue
}
}
@@ -0,0 +1,13 @@
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
/*!re2c
[a] { a }
* { * }
*/
/*!re2c
re2c:eof = 0;
[b] { b }
$ { $ }
* { * }
*/
-7
View File
@@ -1,7 +0,0 @@
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_01.h
/*!header:re2c:on */
/*!types:re2c */
/*!header:re2c:off */
/*!re2c
<a> * {}
*/
-27
View File
@@ -1,27 +0,0 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_02.h
enum YYCONDTYPE {
yyca,
};
{
YYCTYPE yych;
switch (YYGETCONDITION()) {
case yyca:
goto yyc_a;
}
/* *********************************** */
yyc_a:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR++;
{}
}
/* Generated by re2c */
enum YYCONDTYPE {
yyca,
};
-8
View File
@@ -1,8 +0,0 @@
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_02.h
/*!types:re2c */
/*!header:re2c:on */
/*!types:re2c */
/*!header:re2c:off */
/*!re2c
<a> * {}
*/
-31
View File
@@ -1,31 +0,0 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_03.h
enum YYCONDTYPE {
yyca,
};
{
YYCTYPE yych;
switch (YYGETCONDITION()) {
case yyca:
goto yyc_a;
}
/* *********************************** */
yyc_a:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR++;
{}
}
/* Generated by re2c */
enum YYCONDTYPE {
yyca,
};
enum YYCONDTYPE {
yyca,
};
-9
View File
@@ -1,9 +0,0 @@
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_03.h
/*!types:re2c */
/*!header:re2c:on */
/*!types:re2c */
/*!types:re2c */
/*!header:re2c:off */
/*!re2c
<a> * {}
*/
-27
View File
@@ -1,27 +0,0 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_04.h
{
YYCTYPE yych;
switch (YYGETCONDITION()) {
case yyca:
goto yyc_a;
}
/* *********************************** */
yyc_a:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR++;
{}
}
/* Generated by re2c */
enum YYCONDTYPE {
yyca,
};
enum YYCONDTYPE {
yyca,
};
-8
View File
@@ -1,8 +0,0 @@
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_04.h
/*!header:re2c:on */
/*!types:re2c */
/*!types:re2c */
/*!header:re2c:off */
/*!re2c
<a> * {}
*/
-25
View File
@@ -1,25 +0,0 @@
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_05.h
enum YYCONDTYPE {
yyca,
};
{
YYCTYPE yych;
switch (YYGETCONDITION()) {
case yyca:
goto yyc_a;
}
/* *********************************** */
yyc_a:
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR++;
{}
}
/* Generated by re2c */
enum YYCONDTYPE {
yyca,
};

Some files were not shown because too many files have changed in this diff Show More