Compare commits

..
35 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
Ulya Trofimovich 68e1ab7160 Correctly compute fixed tags in trailing context.
Tags in trailing context may be fixed on the rightmost cursor position.
Their values must be updated before cursor is reset to the position of
the trailing context, otherwise the values would be incorrect.
Previously this was not so, which resulted in incorrect tag values.

This fixes bug #380 "Tags incorrectly set with lookahead" reported by
Terry Sanders .
2021-11-04 23:51:01 +00:00
Ryan Mast 8920ef895b Setup Java for the benchmarks workflow using actions/setup-java 2021-11-02 07:25:00 +00:00
Perry E. Metzger 73dc6295a7 minor fixes to interface.rst_
Improve grammar, make some small style improvements, and keep the
lines consistently under 80 columns. Also update boostrap docs for
resulting changes.
2021-10-31 20:30:35 +00:00
Ulya Trofimovich 6d7c3cbb47 Small fixes in distcheck script.
Changes:
  - use 'sh' instead of 'bash' as interpreter
  - split build into stages 1 and 2 and rebuild lexers at stage 2
  - double-quote shell variables to make shellcheck happy
2021-10-24 13:10:47 +01:00
Ulya Trofimovich 072d49e59c Autotools: link google-benchmark binaries with pthread. 2021-10-22 16:52:36 +01:00
Ulya Trofimovich eb8720d5ea Use unsigned integers for register versions in TDFA states.
Previously we used negative numbers for register versions that were
allocated for transitions on negative tags. This is pointless, because
state mapping may biject positive versions to negative ones or vice
versa, erasing the distinction. And it wasn't used anyway.
2021-09-15 22:42:55 +01:00
Ulya Trofimovich b90b78c79c Mark each register as not interfering with itself.
Exclude diagonal pairs from interference table. This does not affect
subsequent register allocation, because the algorithm that partitions
registers into non-interfering classes does not check is a register
interferes with itself. So the fix does not affect the generated code,
except for one test that draws the interference table.
2021-09-04 09:41:08 +01:00
Ulya Trofimovich 6b434e934c run_tests.py: make tests fast again by fixing Wine check.
Commit ec1dc2b3ec incorrectly compared
Wine path against `None` to determine if the tests run under Wine. That
doesn't work as expected because the default value is empty string, not
`None`, so the checks always succeeded and as a result the script did
unnecessary work (patched newlines in the generated output). That made
it a few times slower.
2021-09-04 09:16:04 +01:00
Biswapriyo Nath 15376f4393 Define _POSIX_SOURCE for fdopen in cygwin
This fixes the error:
error: ‘fdopen’ was not declared in this scope; did you mean ‘fopen’?
2021-09-03 16:35:42 +01:00
Serghei Iakovlev dff1b6cd36 Provide support of CMake Presets
Since 3.19.8 CMake looks in the top source directory of a project for files
named CMakePresets.json and CMakeUserPresets.json. Neither file is required
to be present, but if the user tries to list or use presets, at least one of
the files must exist. If both files are present, they will effectively be
merged by reading the CMakePresets.json file first and then the
CMakeUserPresets.json file.

CMakePresets.json can be used to define presets for things like continuous
integration jobs or cross-compilation setups using toolchains provided as
part of the project. In general this feature allows us to simplify CI
configuration phase by taking out all the logic into presets-file and helps
us to make reproducible builds. From now on, you no longer need to carefully
read and copy all the flags locally in order to reproduce CI configuration or
build. Simple use the same one line command as at CI.

As a bonus, users of modern CMake can use a cross-platform and portable way
instead of build/__cmake*.sh scripts.

To see the list of available presets use:
    cmake --list-presets

To configure re2c use:
    cmake --preset=<NAME>

To build re2c use:
    cmake --build --preset=<NAME>

To test re2c use:
    cmake --build --preset=<NAME> --target tests

References:

- https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
- https://gitlab.kitware.com/cmake/cmake/-/issues/21164
- https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#presets
2021-08-12 12:30:44 +01:00
159 changed files with 11723 additions and 3768 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
python-version: '3.8'
- name: Setup Java
uses: actions/setup-python@v2
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
+26 -142
View File
@@ -24,89 +24,38 @@ jobs:
# Linux
- name: linux-gcc-ubsan-ootree
os: ubuntu-latest
generator: Unix Makefiles
build-type: Ubsan
build-dir: .build
cxx-flags: -O2
skeleton: ${{ false }}
- name: linux-gcc-asan-ootree
os: ubuntu-latest
generator: Unix Makefiles
build-type: Asan
build-dir: .build
cxx-flags: -O2
skeleton: ${{ false }}
- name: linux-gcc-debug-ootree-skeleton
os: ubuntu-latest
generator: Unix Makefiles
build-type: Debug
build-dir: .build
cxx-flags: -O2
skeleton: ${{ true }}
- name: linux-gcc-debug-intree-m32-skeleton
os: ubuntu-latest
generator: Unix Makefiles
build-type: Debug
build-dir: .
cxx-flags: -O2 -m32
ld-flags: -m32
skeleton: ${{ true }}
- name: linux-gcc-release-ootree-skeleton
os: ubuntu-latest
generator: Unix Makefiles
build-type: Release
build-dir: .build
skeleton: ${{ true }}
# macOS
- name: macos-clang-debug-ootree-skeleton
os: macos-latest
generator: Unix Makefiles
build-type: Debug
build-dir: .build
cxx-flags: -O2
skeleton: ${{ true }}
- name: macos-clang-debug-intree-skeleton
os: macos-latest
generator: Unix Makefiles
build-type: Debug
build-dir: .
cxx-flags: -O2
skeleton: ${{ true }}
- name: macos-clang-release-ootree-skeleton
os: macos-latest
generator: Unix Makefiles
build-type: Release
build-dir: .build
skeleton: ${{ true }}
# Windows
- name: windows-msvc-debug-ootree
os: windows-latest
generator: Visual Studio 16 2019
build-type: Debug
build-dir: .build
skeleton: ${{ false }}
- name: windows-msvc-debug-intree
os: windows-latest
generator: Visual Studio 16 2019
build-type: Debug
build-dir: .
skeleton: ${{ false }}
- name: windows-msvc-release-ootree
os: windows-latest
generator: Visual Studio 16 2019
build-type: Release
build-dir: .build
skeleton: ${{ false }}
steps:
- name: Checkout Code
@@ -127,16 +76,10 @@ jobs:
case ${{ runner.os }} in
Linux*)
sudo apt install gcc-multilib g++-multilib # for -m32
echo "CC=gcc" >> $GITHUB_ENV
echo "CXX=g++" >> $GITHUB_ENV
;;
macOS*)
sudo xcode-select -switch /Applications/Xcode.app
echo "CC=$(xcrun -f clang)" >> $GITHUB_ENV
echo "CXX=$(xcrun -f clang++)" >> $GITHUB_ENV
echo "SDKROOT=$(xcodebuild -version -sdk macosx Path)" >> $GITHUB_ENV
echo "$(xcodebuild -version -sdk macosx Path)" >> $GITHUB_PATH
;;
Windows*)
@@ -158,109 +101,50 @@ jobs:
python -m pip install --upgrade pip
python -m pip install docutils pygments
- name: Prepare Configure Command
id: configure-command
run: |
# Common CMake arguments
args=( -S . -B ${{ matrix.build-dir }} )
# Generator's specific stuff.
args+=( -G "${{ matrix.generator }}" )
case "${{ matrix.generator }}" in
'Visual Studio'*)
args+=( -A x64 )
;;
*)
# CMAKE_BUILD_TYPE is not used by multi-config build
# types (e.g Visual Studio) at the configure time
# thus we add it only for single-config build types
args+=( -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} )
;;
esac
# CFLAGS, CXXFLAGS, LDFLAGS
args+=( -DCMAKE_C_FLAGS="${{ matrix.cxx-flags }}" )
args+=( -DCMAKE_CXX_FLAGS="${{ matrix.cxx-flags }}" )
args+=( -DCMAKE_EXE_LINKER_FLAGS="${{ matrix.ld-flags }}" )
args+=( -DCMAKE_SHARED_LINKER_FLAGS="${{ matrix.ld-flags }}" )
# Quote each argument to prevent word split.
echo "::set-output name=cmake::$(printf " '%s'" "${args[@]}")"
- name: Check Available Configure Presets
run: cmake --list-presets
- name: Fast Configure
run: |
# Read prepared command into an array
args=( ${{ steps.configure-command.outputs.cmake }} )
# Amend Build Options
args+=( -DCMAKE_INSTALL_PREFIX="$(pwd)/install" )
# Disable some defaults for now to speed up the build as
# they are not necessary in a minimal installation
args+=( -DRE2C_BUILD_RE2GO=OFF )
# Print the resulting command line for debugging purposes
printf "cmake" && printf " '%s'" "${args[@]}" && printf "\n"
# Run CMake, pass quoted arguments to prevent word split.
printf " '%s'" "${args[@]}" | xargs cmake
run: cmake --preset=${{ matrix.name }}-fast
- name: Fast Build
run: cmake --build ${{ matrix.build-dir }} --config ${{ matrix.build-type }}
run: cmake --build --preset=${{ matrix.name }}-fast
- name: Install
run: |
cmake --build ${{ matrix.build-dir }} --config ${{ matrix.build-type }} --target install
echo -e "Install tree:\n$(find install | sed 's/^/-- /')"
run: cmake --build --preset=${{ matrix.name }}-fast --target install
- name: Minimal Install Test
working-directory: ./install/bin
run: ./re2c --version
- name: Full Configure
run: |
# Read prepared command into an array
args=( ${{ steps.configure-command.outputs.cmake }} )
# Amend Build Options
args+=( -DCMAKE_INSTALL_PREFIX="$(pwd)/install-2" -DRE2C_BUILD_RE2GO=ON -DRE2C_BUILD_LIBS=ON )
args+=( -DRE2C_REBUILD_LEXERS=ON -DRE2C_FOR_BUILD="$(pwd)/install/bin/re2c" )
# Windows does not have a native 'man' and 'sed' commands.
# Disabled until this https://github.com/skvadrik/re2c/issues/327
# will be implemented
case ${{ runner.os }} in
Windows*)
args+=( -DRE2C_REBUILD_DOCS=OFF )
;;
*)
args+=( -DRE2C_REBUILD_DOCS=ON )
;;
esac
# Print the resulting command line for debugging purposes
printf "cmake" && printf " '%s'" "${args[@]}" && printf "\n"
# Run CMake, pass quoted arguments to prevent word split.
printf " '%s'" "${args[@]}" | xargs cmake
run: cmake --preset=${{ matrix.name }}-full
- name: Full Build
run: |
find src -name '*.re' | xargs touch
cmake --build ${{ matrix.build-dir }} --config ${{ matrix.build-type }}
cmake --build --preset=${{ matrix.name }}-full
- name: Test
# Remove the following line after fix Windows tests
working-directory: ${{ matrix.build-dir }}
- name: Run Main Test Suite
run: cmake --build --preset=${{ matrix.name }}-full --target tests
- name: Get build dir
id: build-info
run: |
# main test suite
cmake --build . --target check
# skeleton tests (if enabled)
if [[ "x${{ matrix.skeleton }}" == "xtrue" ]] ; then
python run_tests.py --skeleton
if echo "${{ matrix.name }}" | grep -q "ootree"; then
BUILD_DIR="$(pwd)/.build/${{ matrix.name }}-full"
else
BUILD_DIR="$(pwd)"
fi
echo "::set-output name=BUILD_DIR::$BUILD_DIR"
- name: Run Skeleton Tests
if: "contains(matrix.name, 'skeleton')"
# Remove the following line after fix Windows tests
working-directory: ${{ steps.build-info.outputs.BUILD_DIR }}
run: python run_tests.py --skeleton
- name: Archive test artifacts
# Run this step only if tests failed (otherwise there is nothing to upload).
if: failure()
@@ -270,7 +154,7 @@ jobs:
# use a different name per job.
name: test-${{ matrix.name }}
# Tests run in a temporary directory that has a name test_<timestamp>.
path: ${{ matrix.build-dir }}/test_[0-9]*
path: ${{ steps.build-info.outputs.BUILD_DIR }}/test_[0-9]*
# Artifacts are retained for 90 days by default.
# In fact we don't need such long period.
# In fact, we don't need such long period.
retention-days: 30
+3
View File
@@ -12,6 +12,9 @@
/test_*
/.build*
# Ignore user defined CMake presets
CMakeUserPresets.json
# The files autogenerated by automake.
Makefile.in
aclocal.m4
+471
View File
@@ -0,0 +1,471 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 1
},
"configurePresets": [
{
"name": "default-full-configuration",
"hidden": true,
"installDir": "${sourceDir}/install-2",
"cacheVariables": {
"RE2C_BUILD_RE2GO": true,
"RE2C_BUILD_LIBS": true,
"RE2C_REBUILD_LEXERS": true,
"RE2C_REBUILD_DOCS": true,
"RE2C_FOR_BUILD": "${sourceDir}/install/bin/re2c"
}
},
{
"name": "linux-config-base",
"hidden": true,
"generator": "Unix Makefiles",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++"
}
},
{
"name": "linux-gcc-ubsan-ootree-fast",
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (fast)",
"inherits": "linux-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Ubsan",
"CMAKE_C_FLAGS": "-O2",
"CMAKE_CXX_FLAGS": "-O2",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "linux-gcc-ubsan-ootree-full",
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (full)",
"inherits": ["default-full-configuration", "linux-gcc-ubsan-ootree-fast"]
},
{
"name": "linux-gcc-asan-ootree-fast",
"displayName": "Linux x86_64: GCC / Asan / Out of tree (fast)",
"inherits": "linux-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Asan",
"CMAKE_C_FLAGS": "-O2",
"CMAKE_CXX_FLAGS": "-O2",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "linux-gcc-asan-ootree-full",
"displayName": "Linux x86_64: GCC / Asan / Out of tree (full)",
"inherits": ["default-full-configuration", "linux-gcc-asan-ootree-fast"]
},
{
"name": "linux-gcc-debug-ootree-skeleton-fast",
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (fast)",
"inherits": "linux-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_FLAGS": "-O2",
"CMAKE_CXX_FLAGS": "-O2",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "linux-gcc-debug-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (full)",
"inherits": ["default-full-configuration", "linux-gcc-debug-ootree-skeleton-fast"]
},
{
"name": "linux-gcc-debug-intree-m32-skeleton-fast",
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (fast)",
"inherits": "linux-config-base",
"binaryDir": "${sourceDir}",
"installDir": "${sourceDir}/install",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_FLAGS": "-O2 -m32",
"CMAKE_CXX_FLAGS": "-O2 -m32",
"CMAKE_EXE_LINKER_FLAGS": "-m32",
"CMAKE_SHARED_LINKER_FLAGS": "-m32",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "linux-gcc-debug-intree-m32-skeleton-full",
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (full)",
"inherits": ["default-full-configuration", "linux-gcc-debug-intree-m32-skeleton-fast"]
},
{
"name": "linux-gcc-release-ootree-skeleton-fast",
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (fast)",
"inherits": "linux-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "linux-gcc-release-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (full)",
"inherits": ["default-full-configuration", "linux-gcc-release-ootree-skeleton-fast"]
},
{
"name": "macos-config-base",
"hidden": true,
"generator": "Unix Makefiles",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++"
}
},
{
"name": "macos-clang-debug-ootree-skeleton-fast",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (fast)",
"inherits": "macos-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_FLAGS": "-O2",
"CMAKE_CXX_FLAGS": "-O2",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "macos-clang-debug-ootree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (full)",
"inherits": ["default-full-configuration", "macos-clang-debug-ootree-skeleton-fast"]
},
{
"name": "macos-clang-debug-intree-skeleton-fast",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (fast)",
"inherits": "macos-config-base",
"binaryDir": "${sourceDir}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_FLAGS": "-O2",
"CMAKE_CXX_FLAGS": "-O2",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "macos-clang-debug-intree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (full)",
"inherits": ["default-full-configuration", "macos-clang-debug-intree-skeleton-fast"]
},
{
"name": "macos-clang-release-ootree-skeleton-fast",
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (fast)",
"inherits": "macos-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "macos-clang-release-ootree-skeleton-full",
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (full)",
"inherits": ["default-full-configuration", "macos-clang-release-ootree-skeleton-fast"]
},
{
"name": "windows-config-base",
"hidden": true,
"generator": "Visual Studio 16 2019",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_C_COMPILER": "cl",
"CMAKE_CXX_COMPILER": "cl"
}
},
{
"name": "windows-msvc-debug-ootree-fast",
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (fast)",
"inherits": "windows-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "windows-msvc-debug-ootree-full",
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (full)",
"inherits": ["default-full-configuration", "windows-msvc-debug-ootree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
},
{
"name": "windows-msvc-debug-intree-fast",
"displayName": "Windows x86_64: MSVC / Debug / In tree (fast)",
"inherits": "windows-config-base",
"binaryDir": "${sourceDir}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "windows-msvc-debug-intree-full",
"displayName": "Windows x86_64: MSVC / Debug / In tree (full)",
"inherits": ["default-full-configuration", "windows-msvc-debug-intree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
},
{
"name": "windows-msvc-release-ootree-fast",
"displayName": "Windows x86_64: MSVC / Release / Out of tree (fast)",
"inherits": "windows-config-base",
"binaryDir": "${sourceDir}/.build/${presetName}",
"installDir": "${sourceDir}/install",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"RE2C_BUILD_RE2GO": false
}
},
{
"name": "windows-msvc-release-ootree-full",
"displayName": "Windows x86_64: MSVC / Release / Out of tree (full)",
"inherits": ["default-full-configuration", "windows-msvc-release-ootree-fast"],
"cacheVariables": {
"RE2C_REBUILD_DOCS": false
}
}
],
"buildPresets": [
{
"name": "linux-build-base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux-gcc-ubsan-ootree-fast",
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (fast)",
"configurePreset": "linux-gcc-ubsan-ootree-fast",
"inherits": "linux-build-base",
"configuration": "Ubsan"
},
{
"name": "linux-gcc-ubsan-ootree-full",
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (full)",
"configurePreset": "linux-gcc-ubsan-ootree-full",
"inherits": "linux-build-base",
"configuration": "Ubsan"
},
{
"name": "linux-gcc-asan-ootree-fast",
"displayName": "Linux x86_64: GCC / Asan / Out of tree (fast)",
"configurePreset": "linux-gcc-asan-ootree-fast",
"inherits": "linux-build-base",
"configuration": "Asan"
},
{
"name": "linux-gcc-asan-ootree-full",
"displayName": "Linux x86_64: GCC / Asan / Out of tree (full)",
"configurePreset": "linux-gcc-asan-ootree-full",
"inherits": "linux-build-base",
"configuration": "Asan"
},
{
"name": "linux-gcc-debug-ootree-skeleton-fast",
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (fast)",
"configurePreset": "linux-gcc-debug-ootree-skeleton-fast",
"inherits": "linux-build-base",
"configuration": "Debug"
},
{
"name": "linux-gcc-debug-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (full)",
"configurePreset": "linux-gcc-debug-ootree-skeleton-full",
"inherits": "linux-build-base",
"configuration": "Debug"
},
{
"name": "linux-gcc-debug-intree-m32-skeleton-fast",
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (fast)",
"configurePreset": "linux-gcc-debug-intree-m32-skeleton-fast",
"inherits": "linux-build-base",
"configuration": "Debug"
},
{
"name": "linux-gcc-debug-intree-m32-skeleton-full",
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (full)",
"configurePreset": "linux-gcc-debug-intree-m32-skeleton-full",
"inherits": "linux-build-base",
"configuration": "Debug"
},
{
"name": "linux-gcc-release-ootree-skeleton-fast",
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (fast)",
"configurePreset": "linux-gcc-release-ootree-skeleton-fast",
"inherits": "linux-build-base",
"configuration": "Release"
},
{
"name": "linux-gcc-release-ootree-skeleton-full",
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (full)",
"configurePreset": "linux-gcc-release-ootree-skeleton-full",
"inherits": "linux-build-base",
"configuration": "Release"
},
{
"name": "macos-build-base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "macos-clang-debug-ootree-skeleton-fast",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (fast)",
"configurePreset": "macos-clang-debug-ootree-skeleton-fast",
"inherits": "macos-build-base",
"configuration": "Debug"
},
{
"name": "macos-clang-debug-ootree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (full)",
"configurePreset": "macos-clang-debug-ootree-skeleton-full",
"inherits": "macos-build-base",
"configuration": "Debug"
},
{
"name": "macos-clang-debug-intree-skeleton-fast",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (fast)",
"configurePreset": "macos-clang-debug-intree-skeleton-fast",
"inherits": "macos-build-base",
"configuration": "Debug"
},
{
"name": "macos-clang-debug-intree-skeleton-full",
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (full)",
"configurePreset": "macos-clang-debug-intree-skeleton-full",
"inherits": "macos-build-base",
"configuration": "Debug"
},
{
"name": "macos-clang-release-ootree-skeleton-fast",
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (fast)",
"configurePreset": "macos-clang-release-ootree-skeleton-fast",
"inherits": "macos-build-base",
"configuration": "Release"
},
{
"name": "macos-clang-release-ootree-skeleton-full",
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (full)",
"configurePreset": "macos-clang-release-ootree-skeleton-full",
"inherits": "macos-build-base",
"configuration": "Release"
},
{
"name": "windows-build-base",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows-msvc-debug-ootree-fast",
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (fast)",
"configurePreset": "windows-msvc-debug-ootree-fast",
"inherits": "windows-build-base",
"configuration": "Debug"
},
{
"name": "windows-msvc-debug-ootree-full",
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (full)",
"configurePreset": "windows-msvc-debug-ootree-full",
"inherits": "windows-build-base",
"configuration": "Debug"
},
{
"name": "windows-msvc-debug-intree-fast",
"displayName": "Windows x86_64: MSVC / Debug / In tree (fast)",
"configurePreset": "windows-msvc-debug-intree-fast",
"inherits": "windows-build-base",
"configuration": "Debug"
},
{
"name": "windows-msvc-debug-intree-full",
"displayName": "Windows x86_64: MSVC / Debug / In tree (full)",
"configurePreset": "windows-msvc-debug-intree-full",
"inherits": "windows-build-base",
"configuration": "Debug"
},
{
"name": "windows-msvc-release-ootree-fast",
"displayName": "Windows x86_64: MSVC / Release / Out of tree (fast)",
"configurePreset": "windows-msvc-release-ootree-fast",
"inherits": "windows-build-base",
"configuration": "Release"
},
{
"name": "windows-msvc-release-ootree-full",
"displayName": "Windows x86_64: MSVC / Release / Out of tree (full)",
"configurePreset": "windows-msvc-release-ootree-full",
"inherits": "windows-build-base",
"configuration": "Release"
}
]
}
+1 -1
View File
@@ -1,5 +1,5 @@
bench_submatch_dfa_jit_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11 -O3 -I $(top_srcdir)
bench_submatch_dfa_jit_LDFLAGS = $(LDFLAGS_RE2) -lbenchmark
bench_submatch_dfa_jit_LDFLAGS = $(LDFLAGS_RE2) -lbenchmark -lpthread
bench_submatch_dfa_jit_LDADD = $(top_builddir)/libre2c.la
noinst_PROGRAMS = bench_submatch_dfa_jit
+1 -1
View File
@@ -1,5 +1,5 @@
bench_submatch_nfa_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11 -O3 -I $(top_srcdir)
bench_submatch_nfa_LDFLAGS = $(LDFLAGS_RE2) -lbenchmark
bench_submatch_nfa_LDFLAGS = $(LDFLAGS_RE2) -lbenchmark -lpthread
bench_submatch_nfa_LDADD = $(top_builddir)/libre2c.la
noinst_PROGRAMS = bench_submatch_nfa
+70 -51
View File
@@ -1,8 +1,5 @@
.\" Man page generated from reStructuredText.
.
.TH RE2C 1 "" "" ""
.SH NAME
re2c \- generate fast lexical analyzers for C/C++ and Go
.
.nr rst2man-indent-level 0
.
@@ -30,6 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "RE2C" 1 "" "" ""
.SH NAME
re2c \- generate fast lexical analyzers for C/C++ and Go
.SH SYNOPSIS
.sp
\fBre2c [OPTIONS] INPUT [\-o OUTPUT]\fP
@@ -822,11 +822,16 @@ exists for backward compatibility.
.UNINDENT
.SH INTERFACE PRIMITIVES
.sp
Here is a list of API primitives that may be used by the generated code in order to interface with the outer program.
Here is a list of API primitives that may be used by the generated
code in order to interface with the outer program.
Which primitives are needed depends on multiple factors, including
the complexity of regular expressions, input representation, buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form of macros, functions, variables, free\-form pieces of code or any other suitable form.
Re2c does not (and cannot) check the definitions, so if anything is missing or defined incorrectly the generated code will not compile.
the complexity of regular expressions, input representation,
buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form
of macros, functions, variables, free\-form pieces of code, or any other
suitable form.
Re2c does not (and cannot) check the definitions, so if anything is
missing or defined incorrectly the generated code will not compile.
.INDENT 0.0
.TP
.B \fBYYCTYPE\fP
@@ -839,22 +844,23 @@ should be 4\-byte unsigned integer.
A pointer\-like l\-value that stores the current input position (usually a
pointer of type \fBYYCTYPE*\fP). Initially \fBYYCURSOR\fP should point to the
first input character. It is advanced by the generated code.
When a rule matches, \fBYYCURSOR\fP points to the one after the last matched
character. It is used only in the default C API.
When a rule matches, \fBYYCURSOR\fP points to the position after the
last matched character. It is used only in the default C API.
.TP
.B \fBYYLIMIT\fP
A pointer\-like r\-value that stores the end of input position (usually a
pointer of type \fBYYCTYPE*\fP). Initially \fBYYLIMIT\fP should point to the
one after the last available input character. It is not changed by the
generated code. Lexer compares \fBYYCURSOR\fP to \fBYYLIMIT\fP
in order to determine if there is enough input characters left.
position after the last available input character. It is not changed by the
generated code. The lexer compares \fBYYCURSOR\fP to \fBYYLIMIT\fP
in order to determine if there are enough input characters left.
\fBYYLIMIT\fP is used only in the default C API.
.TP
.B \fBYYMARKER\fP
A pointer\-like l\-value (usually a pointer of type \fBYYCTYPE*\fP) that stores
the position of the latest matched rule. It is used to restores \fBYYCURSOR\fP
position if the longer match fails and lexer needs to rollback.
Initialization is not needed. \fBYYMARKER\fP is used only in the default C API.
A pointer\-like l\-value (usually a pointer of type \fBYYCTYPE*\fP)
that stores the position of the latest matched rule. It is used to
restore the \fBYYCURSOR\fP position if the longer match fails and
the lexer needs to rollback. Initialization is not
needed. \fBYYMARKER\fP is used only in the default C API.
.TP
.B \fBYYCTXMARKER\fP
A pointer\-like l\-value that stores the position of the trailing context
@@ -863,24 +869,28 @@ It is used only in the default C API, and only with the lookahead operator
\fB/\fP\&.
.TP
.B \fBYYFILL\fP
API primitive with one argument \fBlen\fP\&.
The meaning of \fBYYFILL\fP is to provide at least \fBlen\fP more input
characters or fail. If EOF rule is used, \fBYYFILL\fP should always return to
the calling function; the return value should be zero on success and non\-zero
on failure. If EOF rule is not used, \fBYYFILL\fP return value is ignored and
it should not return on failure. Maximal value of \fBlen\fP is \fBYYMAXFILL\fP,
which can be generated with \fB/*!max:re2c*/\fP directive. The definition of
\fBYYFILL\fP can be either function\-like or free\-form depending on the API
style (see \fBre2c:api:style\fP and \fBre2c:define:YYFILL:naked\fP).
A generic API primitive with one argument \fBlen\fP\&. \fBYYFILL\fP requests
that the program provide at least \fBlen\fP more input characters or
fail.
If an EOF rule is used, \fBYYFILL\fP should always return to the
calling function; the return value should be zero on success and
non\-zero on failure.
If an EOF rule is not used, the \fBYYFILL\fP return value is ignored
and it should not return on failure.
The maximal value of \fBlen\fP is \fBYYMAXFILL\fP, which can be
generated with the \fB/*!max:re2c*/\fP directive. The definition of
\fBYYFILL\fP can be either function\-like or free\-form depending on
the API style (see \fBre2c:api:style\fP and
\fBre2c:define:YYFILL:naked\fP).
.TP
.B \fBYYMAXFILL\fP
An integral constant equal to the maximal value of \fBYYFILL\fP argument.
It can be generated with \fB/*!max:re2c*/\fP directive.
An integral constant equal to the maximal value of the argument to
\fBYYFILL\fP\&. It can be generated with \fB/*!max:re2c*/\fP directive.
.TP
.B \fBYYLESSTHAN\fP
A generic API primitive with one argument \fBlen\fP\&.
It should be defined as an r\-value of boolean type that equals \fBtrue\fP if
and only if there is less than \fBlen\fP input characters left.
and only if there are less than \fBlen\fP input characters left.
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
@@ -888,93 +898,101 @@ API style (see \fBre2c:api:style\fP).
A generic API primitive with no arguments.
It should be defined as an r\-value of type \fBYYCTYPE\fP that is equal to the
character at the current input position. The definition can be either
function\-like or free\-form depending on the API style (see \fBre2c:api:style\fP).
function\-like or free\-form depending on the API style
(see \fBre2c:api:style\fP).
.TP
.B \fBYYSKIP\fP
A generic API primitive with no arguments.
The meaning of \fBYYSKIP\fP is to advance the current input position by one
\fBYYSKIP\fP should advance the current input position by one
character. The definition can be either function\-like or free\-form
depending on the API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYBACKUP\fP
A generic API primitive with no arguments.
The meaning of \fBYYBACKUP\fP is to save the current input position, which is
\fBYYBACKUP\fP should save the current input position, which is
later restored with \fBYYRESTORE\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORE\fP
A generic API primitive with no arguments.
The meaning of \fBYYRESTORE\fP is to restore the current input position to the
\fBYYRESTORE\fP should restore the current input position to the
value saved by \fBYYBACKUP\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYBACKUPCTX\fP
A generic API primitive with zero arguments.
The meaning of \fBYYBACKUPCTX\fP is to save the current input position as the
position of the trailing context, which is later restored by \fBYYRESTORECTX\fP\&.
\fBYYBACKUPCTX\fP should save the current input position as the
position of the trailing context, which is later restored by
\fBYYRESTORECTX\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORECTX\fP
A generic API primitive with no arguments.
The meaning of \fBYYRESTORECTX\fP is to restore the trailing context position
\fBYYRESTORECTX\fP should restore the trailing context position
saved with \fBYYBACKUPCTX\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORETAG\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYRESTORETAG\fP is to restore the trailing context position
\fBYYRESTORETAG\fP should restore the trailing context position
to the value of \fBtag\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSTAGP\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYSTAGP\fP is to set \fBtag\fP value to the current input
(\fBtag\fP should represent a pointer or an offset, see the section
on submatch extraction for details.)
\fBYYSTAGP\fP should set the \fBtag\fP value to the current input
position.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSTAGN\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYSTAGN\fP is to set \fBtag\fP value to null (or some default
value). The definition should be either function\-like or free\-form depending
on the API style (see \fBre2c:api:style\fP).
(\fBtag\fP should represent a pointer or an offset, see the section
on submatch extraction for details.)
\fBYYSTAGN\fP should to set the \fBtag\fP value to null (or
some default value).
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYMTAGP\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYMTAGP\fP is to append the current position to the history
of \fBtag\fP\&.
\fBYYMTAGP\fP should append the current position to the history
of \fBtag\fP\&. (See the section on submatch extraction for details.)
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYMTAGN\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYMTAGN\fP is to append null (or some other default) value
\fBYYMTAGN\fP should append null (or some other default) value
to the history of \fBtag\fP\&.
(See the section on submatch extraction for details.)
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFT\fP
A generic API primitive with one argument \fBshift\fP\&.
The meaning of \fBYYSHIFT\fP is to shift the current input position by
\fBYYSHIFT\fP should shift the current input position by
\fBshift\fP characters (the shift value may be negative). The definition
can be either function\-like or free\-form depending on the API style
(see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFTSTAG\fP
A generic API primitive with two arguments, \fBtag\fP and \fBshift\fP\&.
The meaning of \fBYYSHIFTSTAG\fP is to shift \fBtag\fP by \fBshift\fP characters
\fBYYSHIFTSTAG\fP should shift \fBtag\fP by \fBshift\fP characters
(the shift value may be negative).
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFTMTAG\fP
A generic API primitive with two arguments, \fBtag\fP and \fBshift\fP\&.
The meaning of \fBYYSHIFTMTAG\fP is to shift the latest value in the history
\fBYYSHIFTMTAG\fP should shift the latest value in the history
of \fBtag\fP by \fBshift\fP characters (the shift value may be negative).
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
@@ -985,8 +1003,8 @@ in a rule. It is generated with \fB/*!maxnmatch:re2c*/\fP directive.
.TP
.B \fBYYCONDTYPE\fP
The type of the condition enum.
It should be generated either with \fB/*!types:re2c*/\fP directive or \fB\-t\fP
\fB\-\-type\-header\fP option.
It should be generated either with the \fB/*!types:re2c*/\fP
directive or the \fB\-t\fP \fB\-\-type\-header\fP option.
.TP
.B \fBYYGETCONDITION\fP
An API primitive with zero arguments.
@@ -997,8 +1015,8 @@ or free\-form depending on the API style (see \fBre2c:api:style\fP and
.TP
.B \fBYYSETCONDITION\fP
An API primitive with one argument \fBcond\fP\&.
The meaning of \fBYYSETCONDITION\fP is to set the current condition identifier
to \fBcond\fP\&.
The meaning of \fBYYSETCONDITION\fP is to set the current condition
identifier to \fBcond\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP and \fBre2c:define:YYSETCONDITION@cond\fP).
.TP
@@ -1011,7 +1029,8 @@ either function\-like or free\-form depending on the API style (see
.TP
.B \fBYYSETSTATE\fP
An API primitive with one argument \fBstate\fP\&.
The meaning of \fBYYSETSTATE\fP is to set the current lexer state to \fBstate\fP\&.
The meaning of \fBYYSETSTATE\fP is to set the current lexer state to
\fBstate\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP and \fBre2c:define:YYSETSTATE@state\fP).
.TP
+70 -51
View File
@@ -1,8 +1,5 @@
.\" Man page generated from reStructuredText.
.
.TH RE2C 1 "" "" ""
.SH NAME
re2c \- generate fast lexical analyzers for C/C++ and Go
.
.nr rst2man-indent-level 0
.
@@ -30,6 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "RE2C" 1 "" "" ""
.SH NAME
re2c \- generate fast lexical analyzers for C/C++ and Go
.SH SYNOPSIS
.sp
\fBre2c [OPTIONS] INPUT [\-o OUTPUT]\fP
@@ -834,11 +834,16 @@ exists for backward compatibility.
.UNINDENT
.SH INTERFACE PRIMITIVES
.sp
Here is a list of API primitives that may be used by the generated code in order to interface with the outer program.
Here is a list of API primitives that may be used by the generated
code in order to interface with the outer program.
Which primitives are needed depends on multiple factors, including
the complexity of regular expressions, input representation, buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form of macros, functions, variables, free\-form pieces of code or any other suitable form.
Re2c does not (and cannot) check the definitions, so if anything is missing or defined incorrectly the generated code will not compile.
the complexity of regular expressions, input representation,
buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form
of macros, functions, variables, free\-form pieces of code, or any other
suitable form.
Re2c does not (and cannot) check the definitions, so if anything is
missing or defined incorrectly the generated code will not compile.
.INDENT 0.0
.TP
.B \fBYYCTYPE\fP
@@ -851,22 +856,23 @@ should be 4\-byte unsigned integer.
A pointer\-like l\-value that stores the current input position (usually a
pointer of type \fBYYCTYPE*\fP). Initially \fBYYCURSOR\fP should point to the
first input character. It is advanced by the generated code.
When a rule matches, \fBYYCURSOR\fP points to the one after the last matched
character. It is used only in the default C API.
When a rule matches, \fBYYCURSOR\fP points to the position after the
last matched character. It is used only in the default C API.
.TP
.B \fBYYLIMIT\fP
A pointer\-like r\-value that stores the end of input position (usually a
pointer of type \fBYYCTYPE*\fP). Initially \fBYYLIMIT\fP should point to the
one after the last available input character. It is not changed by the
generated code. Lexer compares \fBYYCURSOR\fP to \fBYYLIMIT\fP
in order to determine if there is enough input characters left.
position after the last available input character. It is not changed by the
generated code. The lexer compares \fBYYCURSOR\fP to \fBYYLIMIT\fP
in order to determine if there are enough input characters left.
\fBYYLIMIT\fP is used only in the default C API.
.TP
.B \fBYYMARKER\fP
A pointer\-like l\-value (usually a pointer of type \fBYYCTYPE*\fP) that stores
the position of the latest matched rule. It is used to restores \fBYYCURSOR\fP
position if the longer match fails and lexer needs to rollback.
Initialization is not needed. \fBYYMARKER\fP is used only in the default C API.
A pointer\-like l\-value (usually a pointer of type \fBYYCTYPE*\fP)
that stores the position of the latest matched rule. It is used to
restore the \fBYYCURSOR\fP position if the longer match fails and
the lexer needs to rollback. Initialization is not
needed. \fBYYMARKER\fP is used only in the default C API.
.TP
.B \fBYYCTXMARKER\fP
A pointer\-like l\-value that stores the position of the trailing context
@@ -875,24 +881,28 @@ It is used only in the default C API, and only with the lookahead operator
\fB/\fP\&.
.TP
.B \fBYYFILL\fP
API primitive with one argument \fBlen\fP\&.
The meaning of \fBYYFILL\fP is to provide at least \fBlen\fP more input
characters or fail. If EOF rule is used, \fBYYFILL\fP should always return to
the calling function; the return value should be zero on success and non\-zero
on failure. If EOF rule is not used, \fBYYFILL\fP return value is ignored and
it should not return on failure. Maximal value of \fBlen\fP is \fBYYMAXFILL\fP,
which can be generated with \fB/*!max:re2c*/\fP directive. The definition of
\fBYYFILL\fP can be either function\-like or free\-form depending on the API
style (see \fBre2c:api:style\fP and \fBre2c:define:YYFILL:naked\fP).
A generic API primitive with one argument \fBlen\fP\&. \fBYYFILL\fP requests
that the program provide at least \fBlen\fP more input characters or
fail.
If an EOF rule is used, \fBYYFILL\fP should always return to the
calling function; the return value should be zero on success and
non\-zero on failure.
If an EOF rule is not used, the \fBYYFILL\fP return value is ignored
and it should not return on failure.
The maximal value of \fBlen\fP is \fBYYMAXFILL\fP, which can be
generated with the \fB/*!max:re2c*/\fP directive. The definition of
\fBYYFILL\fP can be either function\-like or free\-form depending on
the API style (see \fBre2c:api:style\fP and
\fBre2c:define:YYFILL:naked\fP).
.TP
.B \fBYYMAXFILL\fP
An integral constant equal to the maximal value of \fBYYFILL\fP argument.
It can be generated with \fB/*!max:re2c*/\fP directive.
An integral constant equal to the maximal value of the argument to
\fBYYFILL\fP\&. It can be generated with \fB/*!max:re2c*/\fP directive.
.TP
.B \fBYYLESSTHAN\fP
A generic API primitive with one argument \fBlen\fP\&.
It should be defined as an r\-value of boolean type that equals \fBtrue\fP if
and only if there is less than \fBlen\fP input characters left.
and only if there are less than \fBlen\fP input characters left.
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
@@ -900,93 +910,101 @@ API style (see \fBre2c:api:style\fP).
A generic API primitive with no arguments.
It should be defined as an r\-value of type \fBYYCTYPE\fP that is equal to the
character at the current input position. The definition can be either
function\-like or free\-form depending on the API style (see \fBre2c:api:style\fP).
function\-like or free\-form depending on the API style
(see \fBre2c:api:style\fP).
.TP
.B \fBYYSKIP\fP
A generic API primitive with no arguments.
The meaning of \fBYYSKIP\fP is to advance the current input position by one
\fBYYSKIP\fP should advance the current input position by one
character. The definition can be either function\-like or free\-form
depending on the API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYBACKUP\fP
A generic API primitive with no arguments.
The meaning of \fBYYBACKUP\fP is to save the current input position, which is
\fBYYBACKUP\fP should save the current input position, which is
later restored with \fBYYRESTORE\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORE\fP
A generic API primitive with no arguments.
The meaning of \fBYYRESTORE\fP is to restore the current input position to the
\fBYYRESTORE\fP should restore the current input position to the
value saved by \fBYYBACKUP\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYBACKUPCTX\fP
A generic API primitive with zero arguments.
The meaning of \fBYYBACKUPCTX\fP is to save the current input position as the
position of the trailing context, which is later restored by \fBYYRESTORECTX\fP\&.
\fBYYBACKUPCTX\fP should save the current input position as the
position of the trailing context, which is later restored by
\fBYYRESTORECTX\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORECTX\fP
A generic API primitive with no arguments.
The meaning of \fBYYRESTORECTX\fP is to restore the trailing context position
\fBYYRESTORECTX\fP should restore the trailing context position
saved with \fBYYBACKUPCTX\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYRESTORETAG\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYRESTORETAG\fP is to restore the trailing context position
\fBYYRESTORETAG\fP should restore the trailing context position
to the value of \fBtag\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSTAGP\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYSTAGP\fP is to set \fBtag\fP value to the current input
(\fBtag\fP should represent a pointer or an offset, see the section
on submatch extraction for details.)
\fBYYSTAGP\fP should set the \fBtag\fP value to the current input
position.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSTAGN\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYSTAGN\fP is to set \fBtag\fP value to null (or some default
value). The definition should be either function\-like or free\-form depending
on the API style (see \fBre2c:api:style\fP).
(\fBtag\fP should represent a pointer or an offset, see the section
on submatch extraction for details.)
\fBYYSTAGN\fP should to set the \fBtag\fP value to null (or
some default value).
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYMTAGP\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYMTAGP\fP is to append the current position to the history
of \fBtag\fP\&.
\fBYYMTAGP\fP should append the current position to the history
of \fBtag\fP\&. (See the section on submatch extraction for details.)
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYMTAGN\fP
A generic API primitive with one argument \fBtag\fP\&.
The meaning of \fBYYMTAGN\fP is to append null (or some other default) value
\fBYYMTAGN\fP should append null (or some other default) value
to the history of \fBtag\fP\&.
(See the section on submatch extraction for details.)
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFT\fP
A generic API primitive with one argument \fBshift\fP\&.
The meaning of \fBYYSHIFT\fP is to shift the current input position by
\fBYYSHIFT\fP should shift the current input position by
\fBshift\fP characters (the shift value may be negative). The definition
can be either function\-like or free\-form depending on the API style
(see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFTSTAG\fP
A generic API primitive with two arguments, \fBtag\fP and \fBshift\fP\&.
The meaning of \fBYYSHIFTSTAG\fP is to shift \fBtag\fP by \fBshift\fP characters
\fBYYSHIFTSTAG\fP should shift \fBtag\fP by \fBshift\fP characters
(the shift value may be negative).
The definition can be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
.TP
.B \fBYYSHIFTMTAG\fP
A generic API primitive with two arguments, \fBtag\fP and \fBshift\fP\&.
The meaning of \fBYYSHIFTMTAG\fP is to shift the latest value in the history
\fBYYSHIFTMTAG\fP should shift the latest value in the history
of \fBtag\fP by \fBshift\fP characters (the shift value may be negative).
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP).
@@ -997,8 +1015,8 @@ in a rule. It is generated with \fB/*!maxnmatch:re2c*/\fP directive.
.TP
.B \fBYYCONDTYPE\fP
The type of the condition enum.
It should be generated either with \fB/*!types:re2c*/\fP directive or \fB\-t\fP
\fB\-\-type\-header\fP option.
It should be generated either with the \fB/*!types:re2c*/\fP
directive or the \fB\-t\fP \fB\-\-type\-header\fP option.
.TP
.B \fBYYGETCONDITION\fP
An API primitive with zero arguments.
@@ -1009,8 +1027,8 @@ or free\-form depending on the API style (see \fBre2c:api:style\fP and
.TP
.B \fBYYSETCONDITION\fP
An API primitive with one argument \fBcond\fP\&.
The meaning of \fBYYSETCONDITION\fP is to set the current condition identifier
to \fBcond\fP\&.
The meaning of \fBYYSETCONDITION\fP is to set the current condition
identifier to \fBcond\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP and \fBre2c:define:YYSETCONDITION@cond\fP).
.TP
@@ -1023,7 +1041,8 @@ either function\-like or free\-form depending on the API style (see
.TP
.B \fBYYSETSTATE\fP
An API primitive with one argument \fBstate\fP\&.
The meaning of \fBYYSETSTATE\fP is to set the current lexer state to \fBstate\fP\&.
The meaning of \fBYYSETSTATE\fP is to set the current lexer state to
\fBstate\fP\&.
The definition should be either function\-like or free\-form depending on the
API style (see \fBre2c:api:style\fP and \fBre2c:define:YYSETSTATE@state\fP).
.TP
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
+21 -10
View File
@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
set -e
@@ -14,22 +14,33 @@ autoreconf -i -W all
# try to be portable on various MAKEs
for make_prog in make bmake
do
rm -rf $builddir
mkdir $builddir
cd $builddir
rm -rf "$builddir"
mkdir "$builddir"
cd "$builddir"
# stage 1
mkdir stage1
../configure --prefix "$(pwd)/stage1" \
&& $make_prog \
&& $make_prog install
# stage 2
# 'make' implies 'make docs'; running both in parallel may cause data races
# configure without --enable-debug, this is the release binary
../configure --enable-docs --enable-libs \
&& $make_prog bootstrap -j$(nproc) \
&& $make_prog distcheck -j$(nproc)
../configure \
--enable-docs \
--enable-libs \
--enable-lexers RE2C_FOR_BUILD="$(pwd)/stage1/bin/re2c" \
&& $make_prog bootstrap -j"$(nproc)" \
&& $make_prog distcheck -j"$(nproc)"
cd ..
done
# test that the release tarball builds with CMake
cd $builddir
cd "$builddir"
tarball=$(find . -name 're2c-*.tar.xz')
test -f "$tarball" && tar -xf "$tarball" \
|| { echo "*** failed to find tarball ***" && exit 1; }
cd "${tarball%.tar.xz}" && cmake . && make -j$(nproc) && cd .. \
|| { echo "*** failed to extract tarball ***" && exit 1; }
cd "${tarball%.tar.xz}" && cmake . && make -j"$(nproc)" && cd .. \
|| { echo "*** failed to build with cmake ***" && exit 1; }
cd ..
+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}"
+67 -48
View File
@@ -1,8 +1,13 @@
Here is a list of API primitives that may be used by the generated code in order to interface with the outer program.
Here is a list of API primitives that may be used by the generated
code in order to interface with the outer program.
Which primitives are needed depends on multiple factors, including
the complexity of regular expressions, input representation, buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form of macros, functions, variables, free-form pieces of code or any other suitable form.
Re2c does not (and cannot) check the definitions, so if anything is missing or defined incorrectly the generated code will not compile.
the complexity of regular expressions, input representation,
buffering, the use of various features and so on.
All the necessary primitives should be defined by the user in the form
of macros, functions, variables, free-form pieces of code, or any other
suitable form.
Re2c does not (and cannot) check the definitions, so if anything is
missing or defined incorrectly the generated code will not compile.
``YYCTYPE``
The type of the input characters (code units).
@@ -14,22 +19,23 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
A pointer-like l-value that stores the current input position (usually a
pointer of type ``YYCTYPE*``). Initially ``YYCURSOR`` should point to the
first input character. It is advanced by the generated code.
When a rule matches, ``YYCURSOR`` points to the one after the last matched
character. It is used only in the default C API.
When a rule matches, ``YYCURSOR`` points to the position after the
last matched character. It is used only in the default C API.
``YYLIMIT``
A pointer-like r-value that stores the end of input position (usually a
pointer of type ``YYCTYPE*``). Initially ``YYLIMIT`` should point to the
one after the last available input character. It is not changed by the
generated code. Lexer compares ``YYCURSOR`` to ``YYLIMIT``
in order to determine if there is enough input characters left.
position after the last available input character. It is not changed by the
generated code. The lexer compares ``YYCURSOR`` to ``YYLIMIT``
in order to determine if there are enough input characters left.
``YYLIMIT`` is used only in the default C API.
``YYMARKER``
A pointer-like l-value (usually a pointer of type ``YYCTYPE*``) that stores
the position of the latest matched rule. It is used to restores ``YYCURSOR``
position if the longer match fails and lexer needs to rollback.
Initialization is not needed. ``YYMARKER`` is used only in the default C API.
A pointer-like l-value (usually a pointer of type ``YYCTYPE*``)
that stores the position of the latest matched rule. It is used to
restore the ``YYCURSOR`` position if the longer match fails and
the lexer needs to rollback. Initialization is not
needed. ``YYMARKER`` is used only in the default C API.
``YYCTXMARKER``
A pointer-like l-value that stores the position of the trailing context
@@ -38,24 +44,28 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
``/``.
``YYFILL``
API primitive with one argument ``len``.
The meaning of ``YYFILL`` is to provide at least ``len`` more input
characters or fail. If EOF rule is used, ``YYFILL`` should always return to
the calling function; the return value should be zero on success and non-zero
on failure. If EOF rule is not used, ``YYFILL`` return value is ignored and
it should not return on failure. Maximal value of ``len`` is ``YYMAXFILL``,
which can be generated with ``/*!max:re2c*/`` directive. The definition of
``YYFILL`` can be either function-like or free-form depending on the API
style (see ``re2c:api:style`` and ``re2c:define:YYFILL:naked``).
A generic API primitive with one argument ``len``. ``YYFILL`` requests
that the program provide at least ``len`` more input characters or
fail.
If an EOF rule is used, ``YYFILL`` should always return to the
calling function; the return value should be zero on success and
non-zero on failure.
If an EOF rule is not used, the ``YYFILL`` return value is ignored
and it should not return on failure.
The maximal value of ``len`` is ``YYMAXFILL``, which can be
generated with the ``/*!max:re2c*/`` directive. The definition of
``YYFILL`` can be either function-like or free-form depending on
the API style (see ``re2c:api:style`` and
``re2c:define:YYFILL:naked``).
``YYMAXFILL``
An integral constant equal to the maximal value of ``YYFILL`` argument.
It can be generated with ``/*!max:re2c*/`` directive.
An integral constant equal to the maximal value of the argument to
``YYFILL``. It can be generated with ``/*!max:re2c*/`` directive.
``YYLESSTHAN``
A generic API primitive with one argument ``len``.
It should be defined as an r-value of boolean type that equals ``true`` if
and only if there is less than ``len`` input characters left.
and only if there are less than ``len`` input characters left.
The definition can be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
@@ -63,93 +73,101 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
A generic API primitive with no arguments.
It should be defined as an r-value of type ``YYCTYPE`` that is equal to the
character at the current input position. The definition can be either
function-like or free-form depending on the API style (see ``re2c:api:style``).
function-like or free-form depending on the API style
(see ``re2c:api:style``).
``YYSKIP``
A generic API primitive with no arguments.
The meaning of ``YYSKIP`` is to advance the current input position by one
``YYSKIP`` should advance the current input position by one
character. The definition can be either function-like or free-form
depending on the API style (see ``re2c:api:style``).
``YYBACKUP``
A generic API primitive with no arguments.
The meaning of ``YYBACKUP`` is to save the current input position, which is
``YYBACKUP`` should save the current input position, which is
later restored with ``YYRESTORE``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYRESTORE``
A generic API primitive with no arguments.
The meaning of ``YYRESTORE`` is to restore the current input position to the
``YYRESTORE`` should restore the current input position to the
value saved by ``YYBACKUP``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYBACKUPCTX``
A generic API primitive with zero arguments.
The meaning of ``YYBACKUPCTX`` is to save the current input position as the
position of the trailing context, which is later restored by ``YYRESTORECTX``.
``YYBACKUPCTX`` should save the current input position as the
position of the trailing context, which is later restored by
``YYRESTORECTX``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYRESTORECTX``
A generic API primitive with no arguments.
The meaning of ``YYRESTORECTX`` is to restore the trailing context position
``YYRESTORECTX`` should restore the trailing context position
saved with ``YYBACKUPCTX``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYRESTORETAG``
A generic API primitive with one argument ``tag``.
The meaning of ``YYRESTORETAG`` is to restore the trailing context position
``YYRESTORETAG`` should restore the trailing context position
to the value of ``tag``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYSTAGP``
A generic API primitive with one argument ``tag``.
The meaning of ``YYSTAGP`` is to set ``tag`` value to the current input
(``tag`` should represent a pointer or an offset, see the section
on submatch extraction for details.)
``YYSTAGP`` should set the ``tag`` value to the current input
position.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYSTAGN``
A generic API primitive with one argument ``tag``.
The meaning of ``YYSTAGN`` is to set ``tag`` value to null (or some default
value). The definition should be either function-like or free-form depending
on the API style (see ``re2c:api:style``).
(``tag`` should represent a pointer or an offset, see the section
on submatch extraction for details.)
``YYSTAGN`` should to set the ``tag`` value to null (or
some default value).
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYMTAGP``
A generic API primitive with one argument ``tag``.
The meaning of ``YYMTAGP`` is to append the current position to the history
of ``tag``.
``YYMTAGP`` should append the current position to the history
of ``tag``. (See the section on submatch extraction for details.)
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYMTAGN``
A generic API primitive with one argument ``tag``.
The meaning of ``YYMTAGN`` is to append null (or some other default) value
``YYMTAGN`` should append null (or some other default) value
to the history of ``tag``.
(See the section on submatch extraction for details.)
The definition can be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYSHIFT``
A generic API primitive with one argument ``shift``.
The meaning of ``YYSHIFT`` is to shift the current input position by
``YYSHIFT`` should shift the current input position by
``shift`` characters (the shift value may be negative). The definition
can be either function-like or free-form depending on the API style
(see ``re2c:api:style``).
``YYSHIFTSTAG``
A generic API primitive with two arguments, ``tag`` and ``shift``.
The meaning of ``YYSHIFTSTAG`` is to shift ``tag`` by ``shift`` characters
``YYSHIFTSTAG`` should shift ``tag`` by ``shift`` characters
(the shift value may be negative).
The definition can be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
``YYSHIFTMTAG``
A generic API primitive with two arguments, ``tag`` and ``shift``.
The meaning of ``YYSHIFTMTAG`` is to shift the latest value in the history
``YYSHIFTMTAG`` should shift the latest value in the history
of ``tag`` by ``shift`` characters (the shift value may be negative).
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style``).
@@ -160,8 +178,8 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
``YYCONDTYPE``
The type of the condition enum.
It should be generated either with ``/*!types:re2c*/`` directive or ``-t``
``--type-header`` option.
It should be generated either with the ``/*!types:re2c*/``
directive or the ``-t`` ``--type-header`` option.
``YYGETCONDITION``
An API primitive with zero arguments.
@@ -172,8 +190,8 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
``YYSETCONDITION``
An API primitive with one argument ``cond``.
The meaning of ``YYSETCONDITION`` is to set the current condition identifier
to ``cond``.
The meaning of ``YYSETCONDITION`` is to set the current condition
identifier to ``cond``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style`` and ``re2c:define:YYSETCONDITION@cond``).
@@ -186,7 +204,8 @@ Re2c does not (and cannot) check the definitions, so if anything is missing or d
``YYSETSTATE``
An API primitive with one argument ``state``.
The meaning of ``YYSETSTATE`` is to set the current lexer state to ``state``.
The meaning of ``YYSETSTATE`` is to set the current lexer state to
``state``.
The definition should be either function-like or free-form depending on the
API style (see ``re2c:api:style`` and ``re2c:define:YYSETSTATE@state``).
-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"
+2 -2
View File
@@ -394,7 +394,7 @@ def run_test(test):
# Disable Wine warnings, or else they are dumped into .stderr files for
# random tests, so the test results don't match and the tests fail.
if _ctx.wine != None:
if _ctx.wine:
os.environ['WINEDEBUG'] = '-all'
# run re2c
@@ -431,7 +431,7 @@ def run_test(test):
pass # if there is an error, some files may be missing
# On windows we need to patch output to match expected test results.
if os.name == 'nt' or _ctx.wine != None:
if os.name == 'nt' or _ctx.wine:
patch_newlines_and_slashes(outy, stem)
# compare results
-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:
+3
View File
@@ -63,6 +63,9 @@ void interfere(const tcmd_t *cmd, const bool *live, bool *interf,
memcpy(buf, live, nver * sizeof(bool));
cfg_t::live_through_bblock(p->next, buf);
// a register does not interfere with itself
buf[l] = false;
// if copy command, exclude RHS
if (tcmd_t::iscopy(p)) buf[r] = false;
+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);
+116 -68
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
@@ -677,7 +723,7 @@ void gen_fintags(Output &output, CodeList *stmts, const DFA &dfa, const Rule &ru
CodeList *varops = code_list(alc);
CodeList *fixops = code_list(alc);
CodeList *fixtrailops = code_list(alc);
CodeList *trailops = code_list(alc);
CodeList *fixpostops = code_list(alc);
std::string negtag;
@@ -695,9 +741,9 @@ void gen_fintags(Output &output, CodeList *stmts, const DFA &dfa, const Rule &ru
if (trailing(tag)) {
const bool notag = dfa.oldstyle_ctxmarker;
if (generic) {
gen_restorectx(output, varops, notag ? "" : expr);
gen_restorectx(output, trailops, notag ? "" : expr);
} else {
gen_assign(output, varops, opts->yycursor,
gen_assign(output, trailops, opts->yycursor,
notag ? opts->yyctxmarker : expr);
}
} else {
@@ -716,9 +762,9 @@ void gen_fintags(Output &output, CodeList *stmts, const DFA &dfa, const Rule &ru
DASSERT(tag.toplevel);
if (generic) {
if (!fixed_on_cursor) {
gen_restorectx(output, fixtrailops, base);
gen_restorectx(output, trailops, base);
}
gen_shift(output, fixtrailops, -dist, "", false /* unused */);
gen_shift(output, trailops, -dist, "", false /* unused */);
} else {
if (!fixed_on_cursor) {
o.str(opts->yycursor).cstr(" = ").str(base);
@@ -726,7 +772,7 @@ void gen_fintags(Output &output, CodeList *stmts, const DFA &dfa, const Rule &ru
} else if (dist > 0) {
o.str(opts->yycursor).cstr(" -= ").i32(dist);
}
append(fixtrailops, code_stmt(alc, o.flush()));
append(trailops, code_stmt(alc, o.flush()));
}
} else {
DASSERT(!fintags.empty());
@@ -778,9 +824,11 @@ void gen_fintags(Output &output, CodeList *stmts, const DFA &dfa, const Rule &ru
}
}
// Variable tags must be set before fixed tags which depend on them. Trailing context
// is updated after all tags, because fixed tags may depend on the rightmost position.
append(stmts, varops);
append(stmts, fixtrailops);
append(stmts, fixops);
append(stmts, trailops);
if (!negtag.empty()) {
// With generic API there is no explicit negative NULL value, so it is
+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,
+1 -1
View File
@@ -219,7 +219,7 @@ void generate_versions(ctx_t &ctx)
const tagver_t v = history(tag) ? vs[t] : TAGVER_ZERO;
newver_t x = {t, v, h};
const tagver_t
n = (maxver + 1) * (h0 == TAGVER_BOTTOM ? -1 : 1),
n = maxver + 1,
m = newvers.insert(std::make_pair(x, n)).first->second;
if (n == m) ++maxver;
+7 -14
View File
@@ -485,14 +485,9 @@ void reserve_buffers(ctx_t &ctx)
kbufs.indegree = alc.alloct<uint32_t>(n);
if (!stadfa) {
const size_t m = 2 * n + 1;
// point to the center (zero index) of each buffer
// indexes in range [-N .. N] must be valid, where N is capacity
kbufs.x2y = alc.alloct<tagver_t>(m) + n;
kbufs.y2x = alc.alloct<tagver_t>(m) + n;
kbufs.x2t = alc.alloct<size_t>(m) + n;
kbufs.x2y = alc.alloct<tagver_t>(n);
kbufs.y2x = alc.alloct<tagver_t>(n);
kbufs.x2t = alc.alloct<size_t>(n);
kbufs.backup_actions = alc.alloct<tcmd_t>(n);
}
}
@@ -649,8 +644,8 @@ bool kernel_map_t<ctx_t, regless>::operator()(const kernel_t *x, const kernel_t
// map tag versions of one kernel to that of another
// and check that lookahead versions (if any) coincide
std::fill(x2y - max, x2y + max, TAGVER_ZERO);
std::fill(y2x - max, y2x + max, TAGVER_ZERO);
std::fill(x2y, x2y + max, TAGVER_ZERO);
std::fill(y2x, y2x + max, TAGVER_ZERO);
for (size_t i = 0; i < n; ++i) {
const tagver_t
*xvs = ctx.dc_tagvertbl[x->tvers[i]],
@@ -691,15 +686,13 @@ bool kernel_map_t<ctx_t, regless>::operator()(const kernel_t *x, const kernel_t
// fix LHS of 'save' commands to reuse old version
// see note [save(X), copy(Y,X) optimization]
for (a = *pacts; a; a = a->next) {
const tagver_t
yv = a->lhs * (a->history[0] == TAGVER_BOTTOM ? -1 : 1),
xv = y2x[yv];
const tagver_t yv = a->lhs, xv = y2x[yv];
a->lhs = abs(xv);
y2x[yv] = x2y[xv] = TAGVER_ZERO;
}
// create 'copy' commands
for (tagver_t xv = -max; xv < max; ++xv) {
for (tagver_t xv = 0; xv < max; ++xv) {
const tagver_t yv = x2y[xv], axv = abs(xv), ayv = abs(yv);
if (yv != TAGVER_ZERO && xv != yv && !fixed(tags[x2t[xv]])) {
DASSERT(axv != ayv);
+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;
+2 -4
View File
@@ -163,10 +163,8 @@ static void find_fixed_tags(RESpec &spec, std::vector<StackItem> &stack,
l.tag = re->tag.idx;
l.dist_to_tag = 0;
}
if (trailing(tag)) {
l.dist_to_tag = 0;
}
// Trailing context is not special: tags are fixed on the rightmost cursor
// position, and trailing context is just another tag.
}
}
+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
+2 -2
View File
@@ -343,8 +343,8 @@ static void write_keys(const path_t &path, Skeleton &skel, size_t width)
tval = tags[tver];
if (tval != Skeleton::DEFTAG) tval -= tag.dist;
} else {
// fixed-length tag based on cursor
tval = matched - tag.dist;
// fixed-length tag based on the rightmost position
tval = f - tag.dist;
}
*k++ = to_le(static_cast<key_t>(tval));
}
+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;
}
+4
View File
@@ -1,3 +1,7 @@
#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE 1 // for fdopen
#endif
#include "config.h"
#include <time.h>
#include "src/util/temp_file.h"
-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")
}
}

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