Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
349a97c831 | ||
|
|
e07052b566 | ||
|
|
8a4772cee8 | ||
|
|
b031e27437 | ||
|
|
c643db456c | ||
|
|
dcbbd01055 | ||
|
|
d2cf8a37e9 | ||
|
|
c5179da5f8 | ||
|
|
4edb6ca2d9 | ||
|
|
8b93728f93 | ||
|
|
e551a4918b | ||
|
|
51f769133b | ||
|
|
df50c25fc3 | ||
|
|
ac672c9ce4 | ||
|
|
59edd763bf | ||
|
|
66c6b1cd96 | ||
|
|
e42b3db26a | ||
|
|
1def5f1874 | ||
|
|
29d6adcee1 | ||
|
|
81feecfbfe | ||
|
|
a03b3b4d54 | ||
|
|
10f84e73ed | ||
|
|
bddcb93fa9 | ||
|
|
1c8451883f | ||
|
|
e3e76b0fe4 |
+11
-11
@@ -53,7 +53,7 @@
|
||||
{
|
||||
"name": "linux-gcc-ubsan-ootree-full",
|
||||
"displayName": "Linux x86_64: GCC / Ubsan / Out of tree (full)",
|
||||
"inherits": ["linux-gcc-ubsan-ootree-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "linux-gcc-ubsan-ootree-fast"]
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-asan-ootree-fast",
|
||||
@@ -71,7 +71,7 @@
|
||||
{
|
||||
"name": "linux-gcc-asan-ootree-full",
|
||||
"displayName": "Linux x86_64: GCC / Asan / Out of tree (full)",
|
||||
"inherits": ["linux-gcc-asan-ootree-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "linux-gcc-asan-ootree-fast"]
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-debug-ootree-skeleton-fast",
|
||||
@@ -89,7 +89,7 @@
|
||||
{
|
||||
"name": "linux-gcc-debug-ootree-skeleton-full",
|
||||
"displayName": "Linux x86_64: GCC / Debug / Skeleton / Out of tree (full)",
|
||||
"inherits": ["linux-gcc-debug-ootree-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "linux-gcc-debug-ootree-skeleton-fast"]
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-debug-intree-m32-skeleton-fast",
|
||||
@@ -113,7 +113,7 @@
|
||||
{
|
||||
"name": "linux-gcc-debug-intree-m32-skeleton-full",
|
||||
"displayName": "Linux x86: GCC / Debug / Skeleton / In tree (full)",
|
||||
"inherits": ["linux-gcc-debug-intree-m32-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "linux-gcc-debug-intree-m32-skeleton-fast"]
|
||||
},
|
||||
{
|
||||
"name": "linux-gcc-release-ootree-skeleton-fast",
|
||||
@@ -129,7 +129,7 @@
|
||||
{
|
||||
"name": "linux-gcc-release-ootree-skeleton-full",
|
||||
"displayName": "Linux x86_64: GCC / Release / Skeleton / Out of tree (full)",
|
||||
"inherits": ["linux-gcc-release-ootree-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "linux-gcc-release-ootree-skeleton-fast"]
|
||||
},
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
{
|
||||
"name": "macos-clang-debug-ootree-skeleton-full",
|
||||
"displayName": "macOS x86_64: Clang / Debug / Skeleton / Out of tree (full)",
|
||||
"inherits": ["macos-clang-debug-ootree-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "macos-clang-debug-ootree-skeleton-fast"]
|
||||
},
|
||||
{
|
||||
"name": "macos-clang-debug-intree-skeleton-fast",
|
||||
@@ -185,7 +185,7 @@
|
||||
{
|
||||
"name": "macos-clang-debug-intree-skeleton-full",
|
||||
"displayName": "macOS x86_64: Clang / Debug / Skeleton / In tree (full)",
|
||||
"inherits": ["macos-clang-debug-intree-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "macos-clang-debug-intree-skeleton-fast"]
|
||||
},
|
||||
{
|
||||
"name": "macos-clang-release-ootree-skeleton-fast",
|
||||
@@ -201,7 +201,7 @@
|
||||
{
|
||||
"name": "macos-clang-release-ootree-skeleton-full",
|
||||
"displayName": "macOS x86_64: CLang / Release / Skeleton / Out of tree (full)",
|
||||
"inherits": ["macos-clang-release-ootree-skeleton-fast", "default-full-configuration"]
|
||||
"inherits": ["default-full-configuration", "macos-clang-release-ootree-skeleton-fast"]
|
||||
},
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@
|
||||
{
|
||||
"name": "windows-msvc-debug-ootree-full",
|
||||
"displayName": "Windows x86_64: MSVC / Debug / Out of tree (full)",
|
||||
"inherits": ["windows-msvc-debug-ootree-fast", "default-full-configuration"],
|
||||
"inherits": ["default-full-configuration", "windows-msvc-debug-ootree-fast"],
|
||||
"cacheVariables": {
|
||||
"RE2C_REBUILD_DOCS": false
|
||||
}
|
||||
@@ -256,7 +256,7 @@
|
||||
{
|
||||
"name": "windows-msvc-debug-intree-full",
|
||||
"displayName": "Windows x86_64: MSVC / Debug / In tree (full)",
|
||||
"inherits": ["windows-msvc-debug-intree-fast", "default-full-configuration"],
|
||||
"inherits": ["default-full-configuration", "windows-msvc-debug-intree-fast"],
|
||||
"cacheVariables": {
|
||||
"RE2C_REBUILD_DOCS": false
|
||||
}
|
||||
@@ -275,7 +275,7 @@
|
||||
{
|
||||
"name": "windows-msvc-release-ootree-full",
|
||||
"displayName": "Windows x86_64: MSVC / Release / Out of tree (full)",
|
||||
"inherits": ["windows-msvc-release-ootree-fast", "default-full-configuration"],
|
||||
"inherits": ["default-full-configuration", "windows-msvc-release-ootree-fast"],
|
||||
"cacheVariables": {
|
||||
"RE2C_REBUILD_DOCS": false
|
||||
}
|
||||
|
||||
+1548
-1506
File diff suppressed because it is too large
Load Diff
+415
-409
File diff suppressed because it is too large
Load Diff
@@ -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_
|
||||
|
||||
+719
-706
File diff suppressed because it is too large
Load Diff
@@ -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}"
|
||||
|
||||
@@ -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,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,4 +1,4 @@
|
||||
//go:generate re2go -c $INPUT -o $OUTPUT -i
|
||||
//go:generate re2go $INPUT -o $OUTPUT -i
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
//go:generate re2go -c $INPUT -o $OUTPUT -i
|
||||
//go:generate re2go $INPUT -o $OUTPUT -i
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
@@ -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
@@ -21,7 +21,7 @@
|
||||
namespace re2c {
|
||||
|
||||
class Msg;
|
||||
class Output;
|
||||
struct Output;
|
||||
struct dfa_t;
|
||||
struct opt_t;
|
||||
struct Label;
|
||||
@@ -130,7 +130,6 @@ struct DFA {
|
||||
void reorder();
|
||||
void prepare(const opt_t *opts);
|
||||
void calc_stats(OutputBlock &out);
|
||||
void emit_body(Output &output, CodeList *program) const;
|
||||
void emit_dot(Output &output, CodeList *program) const;
|
||||
|
||||
private:
|
||||
|
||||
+37
-17
@@ -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
@@ -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
@@ -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
@@ -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
@@ -14,7 +14,6 @@
|
||||
#include "src/util/file_utils.h"
|
||||
#include "src/util/string_utils.h"
|
||||
#include "src/util/temp_file.h"
|
||||
#include "src/util/uniq_vector.h"
|
||||
|
||||
|
||||
namespace re2c {
|
||||
@@ -59,8 +58,8 @@ OutputBlock::OutputBlock(InputBlockKind kind, const std::string &name,
|
||||
, max_fill(1)
|
||||
, max_nmatch(1)
|
||||
, start_label(NULL)
|
||||
, fill_index_start(0)
|
||||
, fill_index_end(0)
|
||||
, fill_index(0)
|
||||
, fill_state(0)
|
||||
, fill_goto()
|
||||
{}
|
||||
|
||||
@@ -75,7 +74,7 @@ Output::Output(Msg &msg)
|
||||
, pblocks(&cblocks)
|
||||
, label_counter(0)
|
||||
, state_goto(false)
|
||||
, cond_enum_in_hdr(false)
|
||||
, cond_enum_autogen(true)
|
||||
, cond_goto(false)
|
||||
, warn_condition_order(true)
|
||||
, need_header(false)
|
||||
@@ -169,8 +168,7 @@ void Output::new_block(Opt &opts, InputBlockKind kind, const std::string &name,
|
||||
|
||||
b->opts = opts.snapshot();
|
||||
|
||||
b->fill_index_start = total_fill_index;
|
||||
b->fill_index_end = total_fill_index;
|
||||
b->fill_index = total_fill_index;
|
||||
|
||||
pblocks->push_back(b);
|
||||
|
||||
@@ -183,7 +181,7 @@ void Output::gather_info_from_block()
|
||||
{
|
||||
DASSERT(!pblocks->empty());
|
||||
const OutputBlock *b = pblocks->back();
|
||||
total_fill_index = b->fill_index_end;
|
||||
total_fill_index = b->fill_index;
|
||||
}
|
||||
|
||||
static void fix_first_block_opts(const blocks_t &blocks)
|
||||
@@ -276,23 +274,6 @@ bool Output::emit_blocks(const std::string &fname, const CodegenCtxGlobal &globa
|
||||
return true;
|
||||
}
|
||||
|
||||
static void add_symbols(const OutputBlock &block, CodegenCtxGlobal &ctx)
|
||||
{
|
||||
DASSERT(!block.name.empty());
|
||||
|
||||
const std::string &condenumprefix = block.opts->condEnumPrefix;
|
||||
for (size_t j = 0; j < block.conds.size(); ++j) {
|
||||
ctx.conds.find_or_add(condenumprefix + block.conds[j]);
|
||||
}
|
||||
|
||||
const tagnames_t &st = block.stags, &mt = block.mtags;
|
||||
ctx.stags.insert(st.begin(), st.end());
|
||||
ctx.mtags.insert(mt.begin(), mt.end());
|
||||
|
||||
ctx.max_fill = std::max(ctx.max_fill, block.max_fill);
|
||||
ctx.max_nmatch = std::max(ctx.max_nmatch, block.max_nmatch);
|
||||
}
|
||||
|
||||
bool Output::emit()
|
||||
{
|
||||
if (msg.warn.error()) return false;
|
||||
@@ -302,42 +283,31 @@ bool Output::emit()
|
||||
, scratchbuf
|
||||
, msg
|
||||
, total_opts
|
||||
, cblocks
|
||||
, hblocks
|
||||
, /*pblocks*/ NULL
|
||||
, /*conditions*/ uniq_vector_t<std::string>()
|
||||
, /*stags*/ tagnames_t()
|
||||
, /*mtags*/ tagnames_t()
|
||||
, /*max_fill*/ 1
|
||||
, /*max_nmatch*/ 1
|
||||
, /*tmpblocks*/ blocks_t()
|
||||
, warn_condition_order
|
||||
};
|
||||
|
||||
// gather global data accumulated across all blocks and files
|
||||
for (uint32_t i = 0; i < cblocks.size(); ++i) {
|
||||
add_symbols (*cblocks[i], ctx);
|
||||
}
|
||||
for (uint32_t i = 0; i < hblocks.size(); ++i) {
|
||||
add_symbols (*hblocks[i], ctx);
|
||||
}
|
||||
|
||||
// global options are last block's options
|
||||
const opt_t *opts = block().opts;
|
||||
bool ok = true;
|
||||
|
||||
// emit .h file
|
||||
// Emit header file.
|
||||
if (!opts->header_file.empty() || need_header) {
|
||||
// old-style -t, --type-headers usage implies condition generation
|
||||
if (!ctx.conds.empty() && !this->cond_enum_in_hdr) {
|
||||
if (opts->cFlag && this->cond_enum_autogen) {
|
||||
// Old-style -t, --type-headers usage implies condition enum.
|
||||
header_mode(true);
|
||||
wdelay_stmt(0, code_newline(allocator));
|
||||
wdelay_stmt(0, code_fmt(allocator, CODE_COND_ENUM, NULL, NULL, NULL));
|
||||
header_mode(false);
|
||||
}
|
||||
|
||||
ctx.pblocks = &hblocks;
|
||||
ok &= emit_blocks(opts->header_file, ctx);
|
||||
}
|
||||
|
||||
// emit .c file
|
||||
// Emit output file.
|
||||
ctx.pblocks = &cblocks;
|
||||
ok &= emit_blocks(opts->output_file, ctx);
|
||||
|
||||
|
||||
+107
-61
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -6,7 +6,7 @@ namespace re2c {
|
||||
|
||||
class Scanner;
|
||||
struct Opt;
|
||||
class Output;
|
||||
struct Output;
|
||||
|
||||
void compile(Scanner &input, Output &output, Opt &opts);
|
||||
|
||||
|
||||
@@ -97,8 +97,10 @@ enum CodeKind {
|
||||
CODE_MAXNMATCH,
|
||||
CODE_YYCH,
|
||||
CODE_YYACCEPT,
|
||||
CODE_YYSTATE,
|
||||
CODE_VAR,
|
||||
CODE_STMT,
|
||||
CODE_LOOP,
|
||||
CODE_TEXT,
|
||||
CODE_TEXT_RAW,
|
||||
CODE_RAW,
|
||||
|
||||
+35
-4
@@ -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
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -21,7 +21,7 @@
|
||||
namespace re2c {
|
||||
|
||||
class Msg;
|
||||
class Output;
|
||||
struct Output;
|
||||
struct AST;
|
||||
struct ASTChar;
|
||||
struct BlockNameList;
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ namespace re2c {
|
||||
|
||||
struct AST;
|
||||
struct ASTRule;
|
||||
struct Msg;
|
||||
class Msg;
|
||||
struct Rule;
|
||||
struct opt_t;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,25 +6,25 @@
|
||||
|
||||
namespace re2c {
|
||||
|
||||
template<class _Ty>
|
||||
class free_list: protected std::set<_Ty>
|
||||
template<class Ty>
|
||||
class free_list: protected std::set<Ty>
|
||||
{
|
||||
public:
|
||||
typedef typename std::set<_Ty>::iterator iterator;
|
||||
typedef typename std::set<_Ty>::size_type size_type;
|
||||
typedef typename std::set<_Ty>::key_type key_type;
|
||||
typedef typename std::set<Ty>::iterator iterator;
|
||||
typedef typename std::set<Ty>::size_type size_type;
|
||||
typedef typename std::set<Ty>::key_type key_type;
|
||||
|
||||
free_list(): in_clear(false)
|
||||
{
|
||||
}
|
||||
|
||||
using std::set<_Ty>::insert;
|
||||
using std::set<Ty>::insert;
|
||||
|
||||
size_type erase(const key_type& key)
|
||||
{
|
||||
if (!in_clear)
|
||||
{
|
||||
return std::set<_Ty>::erase(key);
|
||||
return std::set<Ty>::erase(key);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
std::set<_Ty>::clear();
|
||||
std::set<Ty>::clear();
|
||||
|
||||
in_clear = false;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#line 1 "bug152.re"
|
||||
// re2c $INPUT -o $OUTPUT -c
|
||||
#line 5 "bug152.c"
|
||||
enum YYCONDTYPE {
|
||||
};
|
||||
#line 2 "bug152.re"
|
||||
|
||||
|
||||
|
||||
@@ -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"); //
|
||||
} //
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 }
|
||||
$ { $ }
|
||||
* { * }
|
||||
*/
|
||||
@@ -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") //
|
||||
} //
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
// Code generated by re2c, DO NOT EDIT.
|
||||
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// expect a null-terminated string
|
||||
func lex(str string) int {
|
||||
var cursor int
|
||||
count := 0
|
||||
loop:
|
||||
|
||||
{
|
||||
var yych byte
|
||||
yystate := 0
|
||||
for {
|
||||
switch (yystate) {
|
||||
case 0:
|
||||
yych = str[cursor]
|
||||
cursor += 1
|
||||
switch (yych) {
|
||||
case 0x00:
|
||||
yystate = 1
|
||||
continue
|
||||
case ' ':
|
||||
yystate = 5
|
||||
continue
|
||||
case 'a':
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
case 'c':
|
||||
fallthrough
|
||||
case 'd':
|
||||
fallthrough
|
||||
case 'e':
|
||||
fallthrough
|
||||
case 'f':
|
||||
fallthrough
|
||||
case 'g':
|
||||
fallthrough
|
||||
case 'h':
|
||||
fallthrough
|
||||
case 'i':
|
||||
fallthrough
|
||||
case 'j':
|
||||
fallthrough
|
||||
case 'k':
|
||||
fallthrough
|
||||
case 'l':
|
||||
fallthrough
|
||||
case 'm':
|
||||
fallthrough
|
||||
case 'n':
|
||||
fallthrough
|
||||
case 'o':
|
||||
fallthrough
|
||||
case 'p':
|
||||
fallthrough
|
||||
case 'q':
|
||||
fallthrough
|
||||
case 'r':
|
||||
fallthrough
|
||||
case 's':
|
||||
fallthrough
|
||||
case 't':
|
||||
fallthrough
|
||||
case 'u':
|
||||
fallthrough
|
||||
case 'v':
|
||||
fallthrough
|
||||
case 'w':
|
||||
fallthrough
|
||||
case 'x':
|
||||
fallthrough
|
||||
case 'y':
|
||||
fallthrough
|
||||
case 'z':
|
||||
yystate = 8
|
||||
continue
|
||||
default:
|
||||
yystate = 3
|
||||
continue
|
||||
}
|
||||
case 1:
|
||||
yystate = 2
|
||||
continue
|
||||
case 2:
|
||||
{ return count }
|
||||
case 3:
|
||||
yystate = 4
|
||||
continue
|
||||
case 4:
|
||||
{ return -1 }
|
||||
case 5:
|
||||
yych = str[cursor]
|
||||
yystate = 6
|
||||
continue
|
||||
case 6:
|
||||
switch (yych) {
|
||||
case ' ':
|
||||
cursor += 1
|
||||
yystate = 5
|
||||
continue
|
||||
default:
|
||||
yystate = 7
|
||||
continue
|
||||
}
|
||||
case 7:
|
||||
{ goto loop }
|
||||
case 8:
|
||||
yych = str[cursor]
|
||||
yystate = 9
|
||||
continue
|
||||
case 9:
|
||||
switch (yych) {
|
||||
case 'a':
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
case 'c':
|
||||
fallthrough
|
||||
case 'd':
|
||||
fallthrough
|
||||
case 'e':
|
||||
fallthrough
|
||||
case 'f':
|
||||
fallthrough
|
||||
case 'g':
|
||||
fallthrough
|
||||
case 'h':
|
||||
fallthrough
|
||||
case 'i':
|
||||
fallthrough
|
||||
case 'j':
|
||||
fallthrough
|
||||
case 'k':
|
||||
fallthrough
|
||||
case 'l':
|
||||
fallthrough
|
||||
case 'm':
|
||||
fallthrough
|
||||
case 'n':
|
||||
fallthrough
|
||||
case 'o':
|
||||
fallthrough
|
||||
case 'p':
|
||||
fallthrough
|
||||
case 'q':
|
||||
fallthrough
|
||||
case 'r':
|
||||
fallthrough
|
||||
case 's':
|
||||
fallthrough
|
||||
case 't':
|
||||
fallthrough
|
||||
case 'u':
|
||||
fallthrough
|
||||
case 'v':
|
||||
fallthrough
|
||||
case 'w':
|
||||
fallthrough
|
||||
case 'x':
|
||||
fallthrough
|
||||
case 'y':
|
||||
fallthrough
|
||||
case 'z':
|
||||
cursor += 1
|
||||
yystate = 8
|
||||
continue
|
||||
default:
|
||||
yystate = 10
|
||||
continue
|
||||
}
|
||||
case 10:
|
||||
{ count += 1; goto loop }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLex(t *testing.T) {
|
||||
var tests = []struct {
|
||||
res int
|
||||
str string
|
||||
}{
|
||||
{0, "\000"},
|
||||
{3, "one two three\000"},
|
||||
{-1, "f0ur\000"},
|
||||
}
|
||||
|
||||
for _, x := range tests {
|
||||
t.Run(x.str, func(t *testing.T) {
|
||||
res := lex(x.str)
|
||||
if res != x.res {
|
||||
t.Errorf("got %d, want %d", res, x.res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//go:generate re2go $INPUT -o $OUTPUT --loop-switch -i
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// expect a null-terminated string
|
||||
func lex(str string) int {
|
||||
var cursor int
|
||||
count := 0
|
||||
loop:
|
||||
/*!re2c
|
||||
re2c:yyfill:enable = 0;
|
||||
re2c:define:YYCTYPE = byte;
|
||||
re2c:define:YYPEEK = "str[cursor]";
|
||||
re2c:define:YYSKIP = "cursor += 1";
|
||||
|
||||
* { return -1 }
|
||||
[\x00] { return count }
|
||||
[a-z]+ { count += 1; goto loop }
|
||||
[ ]+ { goto loop }
|
||||
*/
|
||||
}
|
||||
|
||||
func TestLex(t *testing.T) {
|
||||
var tests = []struct {
|
||||
res int
|
||||
str string
|
||||
}{
|
||||
{0, "\000"},
|
||||
{3, "one two three\000"},
|
||||
{-1, "f0ur\000"},
|
||||
}
|
||||
|
||||
for _, x := range tests {
|
||||
t.Run(x.str, func(t *testing.T) {
|
||||
res := lex(x.str)
|
||||
if res != x.res {
|
||||
t.Errorf("got %d, want %d", res, x.res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
// Code generated by re2c, DO NOT EDIT.
|
||||
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Intentionally small to trigger buffer refill.
|
||||
const SIZE int = 16
|
||||
|
||||
type Input struct {
|
||||
file *os.File
|
||||
data []byte
|
||||
cursor int
|
||||
marker int
|
||||
token int
|
||||
limit int
|
||||
state int
|
||||
yyaccept int
|
||||
}
|
||||
|
||||
const (
|
||||
lexEnd = iota
|
||||
lexReady
|
||||
lexWaitingForInput
|
||||
lexPacketBroken
|
||||
lexPacketTooBig
|
||||
lexCountMismatch
|
||||
)
|
||||
|
||||
func fill(in *Input) int {
|
||||
if in.token == 0 {
|
||||
// Error: no space can be freed.
|
||||
// In real life can reallocate a larger buffer.
|
||||
return lexPacketTooBig
|
||||
}
|
||||
|
||||
// Discard everything up to the start of the current lexeme,
|
||||
// shift buffer contents and adjust offsets.
|
||||
copy(in.data[0:], in.data[in.token:in.limit])
|
||||
in.cursor -= in.token
|
||||
in.marker -= in.token
|
||||
in.limit -= in.token
|
||||
in.token = 0
|
||||
|
||||
// Read new data (as much as possible to fill the buffer).
|
||||
n, _ := in.file.Read(in.data[in.limit:SIZE])
|
||||
in.limit += n
|
||||
in.data[in.limit] = 0 // append sentinel symbol
|
||||
|
||||
return lexReady
|
||||
}
|
||||
|
||||
func lex(in *Input, recv *int) int {
|
||||
var yych byte
|
||||
loop:
|
||||
|
||||
|
||||
yystate := in.state
|
||||
for {
|
||||
switch (yystate) {
|
||||
case -1:
|
||||
fallthrough
|
||||
case 0:
|
||||
yych = in.data[in.cursor]
|
||||
switch (yych) {
|
||||
case 'a':
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
case 'c':
|
||||
fallthrough
|
||||
case 'd':
|
||||
fallthrough
|
||||
case 'e':
|
||||
fallthrough
|
||||
case 'f':
|
||||
fallthrough
|
||||
case 'g':
|
||||
fallthrough
|
||||
case 'h':
|
||||
fallthrough
|
||||
case 'i':
|
||||
fallthrough
|
||||
case 'j':
|
||||
fallthrough
|
||||
case 'k':
|
||||
fallthrough
|
||||
case 'l':
|
||||
fallthrough
|
||||
case 'm':
|
||||
fallthrough
|
||||
case 'n':
|
||||
fallthrough
|
||||
case 'o':
|
||||
fallthrough
|
||||
case 'p':
|
||||
fallthrough
|
||||
case 'q':
|
||||
fallthrough
|
||||
case 'r':
|
||||
fallthrough
|
||||
case 's':
|
||||
fallthrough
|
||||
case 't':
|
||||
fallthrough
|
||||
case 'u':
|
||||
fallthrough
|
||||
case 'v':
|
||||
fallthrough
|
||||
case 'w':
|
||||
fallthrough
|
||||
case 'x':
|
||||
fallthrough
|
||||
case 'y':
|
||||
fallthrough
|
||||
case 'z':
|
||||
in.cursor += 1
|
||||
yystate = 3
|
||||
continue
|
||||
default:
|
||||
if (in.limit <= in.cursor) {
|
||||
in.state = 10
|
||||
return lexWaitingForInput
|
||||
}
|
||||
in.cursor += 1
|
||||
yystate = 1
|
||||
continue
|
||||
}
|
||||
case 1:
|
||||
yystate = 2
|
||||
continue
|
||||
case 2:
|
||||
in.state = 0
|
||||
{ return lexPacketBroken }
|
||||
case 3:
|
||||
in.marker = in.cursor
|
||||
yych = in.data[in.cursor]
|
||||
switch (yych) {
|
||||
case ';':
|
||||
in.cursor += 1
|
||||
yystate = 4
|
||||
continue
|
||||
case 'a':
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
case 'c':
|
||||
fallthrough
|
||||
case 'd':
|
||||
fallthrough
|
||||
case 'e':
|
||||
fallthrough
|
||||
case 'f':
|
||||
fallthrough
|
||||
case 'g':
|
||||
fallthrough
|
||||
case 'h':
|
||||
fallthrough
|
||||
case 'i':
|
||||
fallthrough
|
||||
case 'j':
|
||||
fallthrough
|
||||
case 'k':
|
||||
fallthrough
|
||||
case 'l':
|
||||
fallthrough
|
||||
case 'm':
|
||||
fallthrough
|
||||
case 'n':
|
||||
fallthrough
|
||||
case 'o':
|
||||
fallthrough
|
||||
case 'p':
|
||||
fallthrough
|
||||
case 'q':
|
||||
fallthrough
|
||||
case 'r':
|
||||
fallthrough
|
||||
case 's':
|
||||
fallthrough
|
||||
case 't':
|
||||
fallthrough
|
||||
case 'u':
|
||||
fallthrough
|
||||
case 'v':
|
||||
fallthrough
|
||||
case 'w':
|
||||
fallthrough
|
||||
case 'x':
|
||||
fallthrough
|
||||
case 'y':
|
||||
fallthrough
|
||||
case 'z':
|
||||
in.cursor += 1
|
||||
yystate = 6
|
||||
continue
|
||||
default:
|
||||
if (in.limit <= in.cursor) {
|
||||
in.state = 11
|
||||
return lexWaitingForInput
|
||||
}
|
||||
yystate = 2
|
||||
continue
|
||||
}
|
||||
case 4:
|
||||
yystate = 5
|
||||
continue
|
||||
case 5:
|
||||
in.state = 0
|
||||
{ *recv = *recv + 1; in.token = in.cursor; goto loop }
|
||||
case 6:
|
||||
yych = in.data[in.cursor]
|
||||
yystate = 7
|
||||
continue
|
||||
case 7:
|
||||
switch (yych) {
|
||||
case ';':
|
||||
in.cursor += 1
|
||||
yystate = 4
|
||||
continue
|
||||
case 'a':
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
case 'c':
|
||||
fallthrough
|
||||
case 'd':
|
||||
fallthrough
|
||||
case 'e':
|
||||
fallthrough
|
||||
case 'f':
|
||||
fallthrough
|
||||
case 'g':
|
||||
fallthrough
|
||||
case 'h':
|
||||
fallthrough
|
||||
case 'i':
|
||||
fallthrough
|
||||
case 'j':
|
||||
fallthrough
|
||||
case 'k':
|
||||
fallthrough
|
||||
case 'l':
|
||||
fallthrough
|
||||
case 'm':
|
||||
fallthrough
|
||||
case 'n':
|
||||
fallthrough
|
||||
case 'o':
|
||||
fallthrough
|
||||
case 'p':
|
||||
fallthrough
|
||||
case 'q':
|
||||
fallthrough
|
||||
case 'r':
|
||||
fallthrough
|
||||
case 's':
|
||||
fallthrough
|
||||
case 't':
|
||||
fallthrough
|
||||
case 'u':
|
||||
fallthrough
|
||||
case 'v':
|
||||
fallthrough
|
||||
case 'w':
|
||||
fallthrough
|
||||
case 'x':
|
||||
fallthrough
|
||||
case 'y':
|
||||
fallthrough
|
||||
case 'z':
|
||||
in.cursor += 1
|
||||
yystate = 6
|
||||
continue
|
||||
default:
|
||||
if (in.limit <= in.cursor) {
|
||||
in.state = 12
|
||||
return lexWaitingForInput
|
||||
}
|
||||
yystate = 8
|
||||
continue
|
||||
}
|
||||
case 8:
|
||||
in.cursor = in.marker
|
||||
yystate = 2
|
||||
continue
|
||||
case 9:
|
||||
in.state = 0
|
||||
{ return lexEnd }
|
||||
case 10:
|
||||
if (in.limit <= in.cursor) {
|
||||
yystate = 9
|
||||
continue
|
||||
}
|
||||
yystate = 0
|
||||
continue
|
||||
case 11:
|
||||
if (in.limit <= in.cursor) {
|
||||
yystate = 2
|
||||
continue
|
||||
}
|
||||
yystate = 3
|
||||
continue
|
||||
case 12:
|
||||
if (in.limit <= in.cursor) {
|
||||
yystate = 8
|
||||
continue
|
||||
}
|
||||
yystate = 6
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func test(packets []string) int {
|
||||
fname := "pipe"
|
||||
fw, _ := os.Create(fname);
|
||||
fr, _ := os.Open(fname);
|
||||
|
||||
in := &Input{
|
||||
file: fr,
|
||||
data: make([]byte, SIZE+1),
|
||||
cursor: SIZE,
|
||||
marker: SIZE,
|
||||
token: SIZE,
|
||||
limit: SIZE,
|
||||
state: -1,
|
||||
}
|
||||
// data is zero-initialized, no need to write sentinel
|
||||
|
||||
var status int
|
||||
send := 0
|
||||
recv := 0
|
||||
loop:
|
||||
for {
|
||||
status = lex(in, &recv)
|
||||
if status == lexEnd {
|
||||
if send != recv {
|
||||
status = lexCountMismatch
|
||||
}
|
||||
break loop
|
||||
} else if status == lexWaitingForInput {
|
||||
if send < len(packets) {
|
||||
fw.WriteString(packets[send])
|
||||
send += 1
|
||||
}
|
||||
status = fill(in)
|
||||
if status != lexReady {
|
||||
break loop
|
||||
}
|
||||
} else if status == lexPacketBroken {
|
||||
break loop
|
||||
} else {
|
||||
panic("unexpected status")
|
||||
}
|
||||
}
|
||||
|
||||
fr.Close()
|
||||
fw.Close()
|
||||
os.Remove(fname)
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func TestLex(t *testing.T) {
|
||||
var tests = []struct {
|
||||
status int
|
||||
packets []string
|
||||
}{
|
||||
{lexEnd, []string{}},
|
||||
{lexEnd, []string{"zero;", "one;", "two;", "three;", "four;"}},
|
||||
{lexPacketBroken, []string{"??;"}},
|
||||
{lexPacketTooBig, []string{"looooooooooooong;"}},
|
||||
}
|
||||
|
||||
for i, x := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
status := test(x.packets)
|
||||
if status != x.status {
|
||||
t.Errorf("got %d, want %d", status, x.status)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Intentionally small to trigger buffer refill.
|
||||
const SIZE int = 16
|
||||
|
||||
type Input struct {
|
||||
file *os.File
|
||||
data []byte
|
||||
cursor int
|
||||
marker int
|
||||
token int
|
||||
limit int
|
||||
state int
|
||||
yyaccept int
|
||||
}
|
||||
|
||||
const (
|
||||
lexEnd = iota
|
||||
lexReady
|
||||
lexWaitingForInput
|
||||
lexPacketBroken
|
||||
lexPacketTooBig
|
||||
lexCountMismatch
|
||||
)
|
||||
|
||||
func fill(in *Input) int {
|
||||
if in.token == 0 {
|
||||
// Error: no space can be freed.
|
||||
// In real life can reallocate a larger buffer.
|
||||
return lexPacketTooBig
|
||||
}
|
||||
|
||||
// Discard everything up to the start of the current lexeme,
|
||||
// shift buffer contents and adjust offsets.
|
||||
copy(in.data[0:], in.data[in.token:in.limit])
|
||||
in.cursor -= in.token
|
||||
in.marker -= in.token
|
||||
in.limit -= in.token
|
||||
in.token = 0
|
||||
|
||||
// Read new data (as much as possible to fill the buffer).
|
||||
n, _ := in.file.Read(in.data[in.limit:SIZE])
|
||||
in.limit += n
|
||||
in.data[in.limit] = 0 // append sentinel symbol
|
||||
|
||||
return lexReady
|
||||
}
|
||||
|
||||
func lex(in *Input, recv *int) int {
|
||||
var yych byte
|
||||
loop:
|
||||
/*!re2c
|
||||
re2c:eof = 0;
|
||||
re2c:define:YYPEEK = "in.data[in.cursor]";
|
||||
re2c:define:YYSKIP = "in.cursor += 1";
|
||||
re2c:define:YYBACKUP = "in.marker = in.cursor";
|
||||
re2c:define:YYRESTORE = "in.cursor = in.marker";
|
||||
re2c:define:YYLESSTHAN = "in.limit <= in.cursor";
|
||||
re2c:define:YYFILL = "return lexWaitingForInput";
|
||||
re2c:define:YYGETSTATE = "in.state";
|
||||
re2c:define:YYSETSTATE = "in.state = @@{state}";
|
||||
|
||||
packet = [a-z]+[;];
|
||||
|
||||
* { return lexPacketBroken }
|
||||
$ { return lexEnd }
|
||||
packet { *recv = *recv + 1; in.token = in.cursor; goto loop }
|
||||
*/
|
||||
}
|
||||
|
||||
func test(packets []string) int {
|
||||
fname := "pipe"
|
||||
fw, _ := os.Create(fname);
|
||||
fr, _ := os.Open(fname);
|
||||
|
||||
in := &Input{
|
||||
file: fr,
|
||||
data: make([]byte, SIZE+1),
|
||||
cursor: SIZE,
|
||||
marker: SIZE,
|
||||
token: SIZE,
|
||||
limit: SIZE,
|
||||
state: -1,
|
||||
}
|
||||
// data is zero-initialized, no need to write sentinel
|
||||
|
||||
var status int
|
||||
send := 0
|
||||
recv := 0
|
||||
loop:
|
||||
for {
|
||||
status = lex(in, &recv)
|
||||
if status == lexEnd {
|
||||
if send != recv {
|
||||
status = lexCountMismatch
|
||||
}
|
||||
break loop
|
||||
} else if status == lexWaitingForInput {
|
||||
if send < len(packets) {
|
||||
fw.WriteString(packets[send])
|
||||
send += 1
|
||||
}
|
||||
status = fill(in)
|
||||
if status != lexReady {
|
||||
break loop
|
||||
}
|
||||
} else if status == lexPacketBroken {
|
||||
break loop
|
||||
} else {
|
||||
panic("unexpected status")
|
||||
}
|
||||
}
|
||||
|
||||
fr.Close()
|
||||
fw.Close()
|
||||
os.Remove(fname)
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func TestLex(t *testing.T) {
|
||||
var tests = []struct {
|
||||
status int
|
||||
packets []string
|
||||
}{
|
||||
{lexEnd, []string{}},
|
||||
{lexEnd, []string{"zero;", "one;", "two;", "three;", "four;"}},
|
||||
{lexPacketBroken, []string{"??;"}},
|
||||
{lexPacketTooBig, []string{"looooooooooooong;"}},
|
||||
}
|
||||
|
||||
for i, x := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
status := test(x.packets)
|
||||
if status != x.status {
|
||||
t.Errorf("got %d, want %d", status, x.status)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
codegen/loop_switch/go/storable_state_error_getstate.re:3:16: error: `getstate:re2c` is incompatible with the --loop-switch option, as it requires cross-block transitions that are unsupported without the `goto` statement
|
||||
@@ -0,0 +1,3 @@
|
||||
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
|
||||
|
||||
/*!getstate:re2c*/
|
||||
@@ -0,0 +1,92 @@
|
||||
// Code generated by re2c, DO NOT EDIT.
|
||||
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
|
||||
|
||||
|
||||
|
||||
yystate := YYGETSTATE
|
||||
for {
|
||||
switch (yystate) {
|
||||
case -1:
|
||||
fallthrough
|
||||
case 0:
|
||||
YYSETSTATE
|
||||
if (YYLESSTHAN) {
|
||||
YYFILL
|
||||
}
|
||||
yych = YYPEEK
|
||||
YYSKIP
|
||||
switch (yych) {
|
||||
case 'a':
|
||||
yystate = 3
|
||||
continue
|
||||
default:
|
||||
yystate = 1
|
||||
continue
|
||||
}
|
||||
case 1:
|
||||
yystate = 2
|
||||
continue
|
||||
case 2:
|
||||
YYSETSTATE
|
||||
{ * }
|
||||
case 3:
|
||||
yystate = 4
|
||||
continue
|
||||
case 4:
|
||||
YYSETSTATE
|
||||
{ a }
|
||||
case 5:
|
||||
yystate = 0
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
yystate := YYGETSTATE
|
||||
for {
|
||||
switch (yystate) {
|
||||
case -1:
|
||||
fallthrough
|
||||
case 0:
|
||||
yych = YYPEEK
|
||||
switch (yych) {
|
||||
case 'b':
|
||||
YYSKIP
|
||||
yystate = 3
|
||||
continue
|
||||
default:
|
||||
if (YYLESSTHAN) {
|
||||
YYSETSTATE
|
||||
YYFILL
|
||||
}
|
||||
YYSKIP
|
||||
yystate = 1
|
||||
continue
|
||||
}
|
||||
case 1:
|
||||
yystate = 2
|
||||
continue
|
||||
case 2:
|
||||
YYSETSTATE
|
||||
{ * }
|
||||
case 3:
|
||||
yystate = 4
|
||||
continue
|
||||
case 4:
|
||||
YYSETSTATE
|
||||
{ b }
|
||||
case 5:
|
||||
YYSETSTATE
|
||||
{ $ }
|
||||
case 6:
|
||||
if (YYLESSTHAN) {
|
||||
yystate = 5
|
||||
continue
|
||||
}
|
||||
yystate = 0
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//go:generate re2go -f $INPUT -o $OUTPUT --loop-switch -i
|
||||
|
||||
/*!re2c
|
||||
[a] { a }
|
||||
* { * }
|
||||
*/
|
||||
|
||||
/*!re2c
|
||||
re2c:eof = 0;
|
||||
[b] { b }
|
||||
$ { $ }
|
||||
* { * }
|
||||
*/
|
||||
@@ -1,7 +0,0 @@
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_01.h
|
||||
/*!header:re2c:on */
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:off */
|
||||
/*!re2c
|
||||
<a> * {}
|
||||
*/
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Generated by re2c */
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_02.h
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
|
||||
|
||||
{
|
||||
YYCTYPE yych;
|
||||
switch (YYGETCONDITION()) {
|
||||
case yyca:
|
||||
goto yyc_a;
|
||||
}
|
||||
/* *********************************** */
|
||||
yyc_a:
|
||||
if (YYLIMIT <= YYCURSOR) YYFILL(1);
|
||||
yych = *YYCURSOR++;
|
||||
{}
|
||||
}
|
||||
|
||||
/* Generated by re2c */
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_02.h
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:on */
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:off */
|
||||
/*!re2c
|
||||
<a> * {}
|
||||
*/
|
||||
@@ -1,31 +0,0 @@
|
||||
/* Generated by re2c */
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_03.h
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
|
||||
|
||||
{
|
||||
YYCTYPE yych;
|
||||
switch (YYGETCONDITION()) {
|
||||
case yyca:
|
||||
goto yyc_a;
|
||||
}
|
||||
/* *********************************** */
|
||||
yyc_a:
|
||||
if (YYLIMIT <= YYCURSOR) YYFILL(1);
|
||||
yych = *YYCURSOR++;
|
||||
{}
|
||||
}
|
||||
|
||||
/* Generated by re2c */
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_03.h
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:on */
|
||||
/*!types:re2c */
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:off */
|
||||
/*!re2c
|
||||
<a> * {}
|
||||
*/
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Generated by re2c */
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_04.h
|
||||
|
||||
|
||||
{
|
||||
YYCTYPE yych;
|
||||
switch (YYGETCONDITION()) {
|
||||
case yyca:
|
||||
goto yyc_a;
|
||||
}
|
||||
/* *********************************** */
|
||||
yyc_a:
|
||||
if (YYLIMIT <= YYCURSOR) YYFILL(1);
|
||||
yych = *YYCURSOR++;
|
||||
{}
|
||||
}
|
||||
|
||||
/* Generated by re2c */
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_04.h
|
||||
/*!header:re2c:on */
|
||||
/*!types:re2c */
|
||||
/*!types:re2c */
|
||||
/*!header:re2c:off */
|
||||
/*!re2c
|
||||
<a> * {}
|
||||
*/
|
||||
@@ -1,25 +0,0 @@
|
||||
/* Generated by re2c */
|
||||
// re2c $INPUT -o $OUTPUT -ci --type-header cond_enum_05.h
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
|
||||
|
||||
{
|
||||
YYCTYPE yych;
|
||||
switch (YYGETCONDITION()) {
|
||||
case yyca:
|
||||
goto yyc_a;
|
||||
}
|
||||
/* *********************************** */
|
||||
yyc_a:
|
||||
if (YYLIMIT <= YYCURSOR) YYFILL(1);
|
||||
yych = *YYCURSOR++;
|
||||
{}
|
||||
}
|
||||
|
||||
/* Generated by re2c */
|
||||
|
||||
enum YYCONDTYPE {
|
||||
yyca,
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user