mirror of
https://github.com/boostorg/build.git
synced 2026-01-19 04:02:14 +00:00
Switch to string_view for some internal char handling.
Add some debug validation in Jam interpreter.
This commit is contained in:
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -54,7 +54,7 @@
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/.build/gcc-12/debug/cxxstd-11-iso/b2",
|
||||
"args": ["-na"],
|
||||
"args": ["--grep"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
|
||||
25
.vscode/tasks.json
vendored
25
.vscode/tasks.json
vendored
@@ -98,31 +98,6 @@
|
||||
}
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "B2 Engine Infer",
|
||||
"type": "shell",
|
||||
"osx": {
|
||||
"command": "CXX='infer run -- clang++' ./build.sh clang"
|
||||
},
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/src/engine"
|
||||
},
|
||||
"problemMatcher": {
|
||||
"owner": "cpptools",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}/src/engine"
|
||||
],
|
||||
"pattern": {
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -45,6 +45,7 @@ project b2
|
||||
<cxxstd>11
|
||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
||||
<toolset>msvc:<define>_CRT_NONSTDC_NO_DEPRECATE=1
|
||||
<variant>debug:<define>B2_DEBUG=1
|
||||
;
|
||||
|
||||
#|
|
||||
|
||||
@@ -980,7 +980,7 @@ LIST * builtin_subst( FRAME * frame, int flags )
|
||||
if ( *in >= '0' && *in <= '9' )
|
||||
{
|
||||
unsigned int const n = *in - '0';
|
||||
size_t const srclen = re_i[n].size;
|
||||
size_t const srclen = re_i[n].size();
|
||||
size_t const remaining = buf + BUFLEN - out;
|
||||
size_t const len = srclen < remaining
|
||||
? srclen
|
||||
@@ -1030,7 +1030,7 @@ LIST * builtin_match( FRAME * frame, int flags )
|
||||
{
|
||||
/* Find highest parameter */
|
||||
int top = NSUBEXP-1;
|
||||
while ( re_i[top].end() == nullptr ) top -= 1;
|
||||
while ( re_i[top].empty() ) top -= 1;
|
||||
/* And add all parameters up to highest onto list. */
|
||||
/* Must have parameters to have results! */
|
||||
for ( int i = 1; i <= top ; ++i )
|
||||
|
||||
@@ -80,7 +80,7 @@ CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell )
|
||||
string_new( cmd->buf );
|
||||
|
||||
frame_init( frame );
|
||||
frame->module = rule->module;
|
||||
frame->module = b2::ensure_valid(rule->module);
|
||||
lol_init( frame->args );
|
||||
lol_add( frame->args, list_copy( targets ) );
|
||||
lol_add( frame->args, list_copy( sources ) );
|
||||
|
||||
@@ -90,7 +90,7 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame )
|
||||
if ( rule->procedure && rule->module != prev_module )
|
||||
{
|
||||
/* Propagate current module to nested rule invocations. */
|
||||
frame->module = rule->module;
|
||||
frame->module = b2::ensure_valid(rule->module);
|
||||
}
|
||||
|
||||
/* Record current rule name in frame. */
|
||||
@@ -180,7 +180,7 @@ LIST * call_rule( OBJECT * rulename, FRAME * caller_frame, LOL * args )
|
||||
inner->prev_user = caller_frame->module->user_module
|
||||
? caller_frame
|
||||
: caller_frame->prev_user;
|
||||
inner->module = caller_frame->module;
|
||||
inner->module = b2::ensure_valid(caller_frame->module);
|
||||
|
||||
for ( int32_t a = 0; a < args->count; ++a)
|
||||
{
|
||||
@@ -259,7 +259,7 @@ LIST * call_member_rule(
|
||||
inner->prev = caller_frame;
|
||||
inner->prev_user = caller_frame->module->user_module
|
||||
? caller_frame : caller_frame->prev_user;
|
||||
inner->module = caller_frame->module;
|
||||
inner->module = b2::ensure_valid(caller_frame->module);
|
||||
|
||||
args.swap( inner->args[0] );
|
||||
|
||||
|
||||
@@ -75,5 +75,27 @@ typedef long int32_t;
|
||||
#define B2_USE_STD_THREADS 1
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace b2 {
|
||||
|
||||
inline bool ensure(bool v)
|
||||
{
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
assert(v);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T * ensure_valid(T * v)
|
||||
{
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
assert(v != nullptr);
|
||||
#endif
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace b2
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@ void frame_init( FRAME * frame )
|
||||
frame->prev = 0;
|
||||
frame->prev_user = 0;
|
||||
lol_init( frame->args );
|
||||
frame->module = root_module();
|
||||
frame->module = b2::ensure_valid(root_module());
|
||||
frame->rulename = "module scope";
|
||||
frame->file = 0;
|
||||
frame->line = -1;
|
||||
|
||||
@@ -66,13 +66,13 @@ namespace b2 { namespace jam {
|
||||
struct module_scope
|
||||
{
|
||||
inline module_scope(FRAME * frame_, const char * module_name_)
|
||||
: module_name(module_name_)
|
||||
, module_frame(frame_)
|
||||
: module_frame(frame_)
|
||||
{
|
||||
if (module_frame)
|
||||
{
|
||||
module_saved = module_frame->module;
|
||||
module_frame->module = bindmodule(module_name);
|
||||
module_frame->module
|
||||
= b2::ensure_valid(bindmodule(value_ref(module_name_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,20 +80,31 @@ struct module_scope
|
||||
{
|
||||
if (module_frame && module_saved)
|
||||
{
|
||||
module_frame->module = module_saved;
|
||||
module_frame->module = b2::ensure_valid(module_saved);
|
||||
}
|
||||
}
|
||||
|
||||
inline const value_ref & name() const { return module_name; }
|
||||
inline value_ref name() const { return module_frame->module->name; }
|
||||
inline FRAME * frame() const { return module_frame; }
|
||||
inline operator module_ptr() const { return module_frame->module; }
|
||||
|
||||
private:
|
||||
value_ref module_name;
|
||||
FRAME * module_frame = nullptr;
|
||||
module_t * module_saved = nullptr;
|
||||
};
|
||||
|
||||
struct module_scope_in_function
|
||||
{
|
||||
module_scope_in_function(FRAME * frame_, const char * module_name_);
|
||||
~module_scope_in_function();
|
||||
inline value_ref name() const { return module_frame->module->name; }
|
||||
inline FRAME * frame() const { return module_frame; }
|
||||
inline operator module_ptr() const { return module_frame->module; }
|
||||
|
||||
private:
|
||||
FRAME * module_frame = nullptr;
|
||||
};
|
||||
|
||||
}} // namespace b2::jam
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "variable.h"
|
||||
#include "output.h"
|
||||
#include "startup.h"
|
||||
#include "mod_sysinfo.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -267,9 +268,14 @@ struct _stack
|
||||
|
||||
_stack()
|
||||
{
|
||||
// The LIFO stack goes from start end (high/right) to (low/left).
|
||||
// Byte size of the function stack.
|
||||
int32_t const size = 1 << 21;
|
||||
// The beginning of the stack memory.
|
||||
start = BJAM_MALLOC( size );
|
||||
// The current past-end/tail of the stack memory.
|
||||
end = (char *)start + size;
|
||||
// The next available stack location.
|
||||
data = end;
|
||||
}
|
||||
|
||||
@@ -277,10 +283,12 @@ struct _stack
|
||||
{
|
||||
if ( cleanups_size > cleanups_t::size_type(0) )
|
||||
{
|
||||
// Call any left over cleanup functions to deallocate items that
|
||||
// reference resources.
|
||||
while ( cleanups_size > 0 )
|
||||
{
|
||||
cleanups_size -= 1;
|
||||
cleanups[cleanups_size]( this );
|
||||
cleanups[cleanups_size].clean( this );
|
||||
}
|
||||
}
|
||||
BJAM_FREE( start );
|
||||
@@ -305,28 +313,39 @@ struct _stack
|
||||
|
||||
// Move "v" to a new slot in ther stack. Returns a reference to the new item.
|
||||
template <class T>
|
||||
remove_cref_t<T> & push( T v );
|
||||
remove_cref_t<T> & push( T v, FRAME * frame = nullptr );
|
||||
|
||||
// Copy "v" into "n" new items at the top of the stack. Returns a pointer
|
||||
// to the first, i.e. top most, new item.
|
||||
// to the first, i.e. top/front most, new item.
|
||||
template <class T>
|
||||
remove_cref_t<T> * push( T v, int32_t n );
|
||||
remove_cref_t<T> * push( T v, int32_t n, FRAME * frame = nullptr );
|
||||
|
||||
// Removes the top most "T" item from the stack and returns a copy of it.
|
||||
// Removes the top/left most "T" item from the stack and returns a copy of
|
||||
// it.
|
||||
template <class T>
|
||||
remove_cref_t<T> pop()
|
||||
{
|
||||
using U = remove_cref_t<T>;
|
||||
// Copy the top/left item.
|
||||
U result = top<U>();
|
||||
// Remove the obsolete top item.
|
||||
pop<T>( 1 );
|
||||
return result;
|
||||
}
|
||||
|
||||
// Removes the top "n" "T" items from the stack.
|
||||
// Removes the top/left "n" "T" items from the stack.
|
||||
template <class T>
|
||||
void pop( int32_t n )
|
||||
{
|
||||
// Debugging validation.
|
||||
using U = remove_cref_t<T>;
|
||||
for (auto c = 0; c < n; ++c)
|
||||
cleanups[cleanups_size-c-1].check<U>().~cleanup_t();
|
||||
// The removal of the n items happens by skipping the stack pointer
|
||||
// past the n items.
|
||||
set_data( nth<T>( n ) );
|
||||
// We also "skip" the cleanups. As the caller has dealt with that in
|
||||
// some form.
|
||||
cleanups_size -= n;
|
||||
}
|
||||
|
||||
@@ -336,7 +355,42 @@ struct _stack
|
||||
void * end = nullptr;
|
||||
void * data = nullptr;
|
||||
using cleanup_f = void(*)( _stack* );
|
||||
using cleanups_t = std::array<cleanup_f, (1<<21)/sizeof(void*)>;
|
||||
struct cleanup_t
|
||||
{
|
||||
cleanup_f clean = nullptr;
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
const char * type_name = nullptr;
|
||||
std::string native_stack;
|
||||
std::string jam_stack;
|
||||
#endif
|
||||
inline cleanup_t() = default;
|
||||
template <class T>
|
||||
inline cleanup_t(cleanup_f f, FRAME * frame, T*_) : clean(f)
|
||||
{
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
type_name = typeid(T).name();
|
||||
native_stack = b2::stacktrace::to_string();
|
||||
jam_stack = b2::jam::backtrace::to_string(frame);
|
||||
#endif
|
||||
}
|
||||
inline ~cleanup_t()
|
||||
{
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
clean = nullptr;
|
||||
type_name = nullptr;
|
||||
#endif
|
||||
}
|
||||
template <class T>
|
||||
inline cleanup_t & check()
|
||||
{
|
||||
#if defined(B2_DEBUG) && B2_DEBUG
|
||||
static const char * type_name_c = typeid(T).name();
|
||||
b2::ensure(type_name_c == this->type_name);
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
using cleanups_t = std::array<cleanup_t, (1 << 21)/sizeof(void*)>;
|
||||
cleanups_t cleanups;
|
||||
cleanups_t::size_type cleanups_size = 0;
|
||||
|
||||
@@ -359,6 +413,7 @@ struct _stack
|
||||
return data;
|
||||
}
|
||||
|
||||
// Get a pointer from the current top/left onward to the nth item.
|
||||
template <typename T>
|
||||
remove_cref_t<T> * nth( int32_t n )
|
||||
{
|
||||
@@ -405,7 +460,7 @@ struct _stack
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void cleanup_push(int32_t n, T*_ = nullptr);
|
||||
void cleanup_push(int32_t n, FRAME * frame, T*_ = nullptr);
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -416,25 +471,27 @@ void _stack::cleanup_item<LIST*>(_stack * s, LIST**)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
remove_cref_t<T> & _stack::push( T v )
|
||||
remove_cref_t<T> & _stack::push( T v, FRAME * frame )
|
||||
{
|
||||
return *push<T>( v, 1 );
|
||||
return *push<T>( v, 1, frame );
|
||||
}
|
||||
|
||||
template <class T>
|
||||
remove_cref_t<T> * _stack::push( T v, int32_t n )
|
||||
remove_cref_t<T> * _stack::push( T v, int32_t n, FRAME * frame )
|
||||
{
|
||||
using U = remove_cref_t<T>;
|
||||
set_data( nth<T>( -n ) );
|
||||
std::uninitialized_fill_n( nth<U>( 0 ), n, v );
|
||||
cleanup_push<U>( n );
|
||||
cleanup_push<U>( n, frame );
|
||||
return nth<U>( 0 );
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _stack::cleanup_push( int32_t n, T*_ )
|
||||
void _stack::cleanup_push( int32_t n, FRAME * frame, T*_ )
|
||||
{
|
||||
std::uninitialized_fill_n( &cleanups[cleanups_size], n, (cleanup_f)&_stack::cleanup_item<T> );
|
||||
using U = remove_cref_t<T>;
|
||||
cleanup_t c((cleanup_f)&_stack::cleanup_item<T>, frame, (U*)(_));
|
||||
std::uninitialized_fill_n( &cleanups[cleanups_size], n, c );
|
||||
cleanups_size += n;
|
||||
}
|
||||
|
||||
@@ -958,7 +1015,7 @@ static int32_t expand_modifiers( STACK * s, int32_t n )
|
||||
iter[ i ] = list_begin( args[ i ] );
|
||||
}
|
||||
}
|
||||
s->pop<LIST*>( n );
|
||||
s->pop<LISTITER>( n );
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -3416,12 +3473,12 @@ void argument_list_push( struct arg_list * formal, int32_t formal_count,
|
||||
{
|
||||
LIST * * const old = &frame->module->fixed_variables[
|
||||
formal_arg->index ];
|
||||
s->push( *old );
|
||||
s->push( *old, frame );
|
||||
*old = value.release();
|
||||
}
|
||||
else
|
||||
s->push( var_swap( frame->module, formal_arg->arg_name,
|
||||
value.release() ) );
|
||||
value.release() ), frame );
|
||||
}
|
||||
|
||||
if ( actual_iter != actual_end )
|
||||
@@ -4562,7 +4619,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame )
|
||||
PROFILE_ENTER_LOCAL(function_run_INSTR_PUSH_LOCAL);
|
||||
LIST * value = s->pop<LIST *>();
|
||||
s->push( function_swap_variable( function, frame, code->arg,
|
||||
value ) );
|
||||
value ), frame );
|
||||
PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_LOCAL);
|
||||
break;
|
||||
}
|
||||
@@ -5149,10 +5206,10 @@ LIST * function_run( FUNCTION * function_, FRAME * frame )
|
||||
LIST * const module_name = s->pop<LIST *>();
|
||||
module_t * outer_module = frame->module;
|
||||
frame->module = !list_empty( module_name )
|
||||
? bindmodule( list_front( module_name ) )
|
||||
: root_module();
|
||||
? b2::ensure_valid(bindmodule( list_front( module_name ) ))
|
||||
: b2::ensure_valid(root_module());
|
||||
list_free( module_name );
|
||||
s->push( outer_module );
|
||||
s->push( b2::ensure_valid(outer_module) );
|
||||
PROFILE_EXIT_LOCAL(function_run_INSTR_PUSH_MODULE);
|
||||
break;
|
||||
}
|
||||
@@ -5160,7 +5217,7 @@ LIST * function_run( FUNCTION * function_, FRAME * frame )
|
||||
case INSTR_POP_MODULE:
|
||||
{
|
||||
PROFILE_ENTER_LOCAL(function_run_INSTR_POP_MODULE);
|
||||
frame->module = s->pop<module_t *>();
|
||||
frame->module = b2::ensure_valid(s->pop<module_t *>());
|
||||
PROFILE_EXIT_LOCAL(function_run_INSTR_POP_MODULE);
|
||||
break;
|
||||
}
|
||||
@@ -5173,10 +5230,13 @@ LIST * function_run( FUNCTION * function_, FRAME * frame )
|
||||
OBJECT * class_module = make_class_module( name, bases );
|
||||
|
||||
module_t * outer_module = frame->module;
|
||||
frame->module = bindmodule( class_module );
|
||||
// out_printf("function_run: OUTER-CLASS = %s, %s\n",
|
||||
// outer_module->name ? outer_module->name->str() : "<>",
|
||||
// class_module->str());
|
||||
frame->module = b2::ensure_valid(bindmodule( class_module ));
|
||||
object_free( class_module );
|
||||
|
||||
s->push( outer_module );
|
||||
s->push( b2::ensure_valid(outer_module) );
|
||||
PROFILE_EXIT_LOCAL(function_run_INSTR_CLASS);
|
||||
break;
|
||||
}
|
||||
@@ -5254,3 +5314,51 @@ void function_done( void )
|
||||
{
|
||||
stack_global()->done();
|
||||
}
|
||||
|
||||
b2::jam::module_scope_in_function::module_scope_in_function(
|
||||
FRAME * frame_, const char * module_name_)
|
||||
: module_frame(frame_)
|
||||
{
|
||||
auto stack = stack_global();
|
||||
stack->push(b2::ensure_valid(module_frame->module));
|
||||
module_frame->module = module_name_ == nullptr
|
||||
? b2::ensure_valid(root_module())
|
||||
: b2::ensure_valid(bindmodule(value_ref(module_name_)));
|
||||
}
|
||||
|
||||
b2::jam::module_scope_in_function::~module_scope_in_function()
|
||||
{
|
||||
auto stack = stack_global();
|
||||
module_frame->module = b2::ensure_valid(stack->pop<module_t *>());
|
||||
}
|
||||
|
||||
std::string b2::jam::backtrace::to_string(frame * f)
|
||||
{
|
||||
std::string result;
|
||||
for (auto i : backtrace::to_list(f))
|
||||
{
|
||||
if (!result.empty()) result += "\n";
|
||||
result += i->str();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
b2::list_ref b2::jam::backtrace::to_list(frame * f)
|
||||
{
|
||||
b2::list_ref result;
|
||||
for (; f != nullptr; f = f->prev)
|
||||
{
|
||||
std::string line;
|
||||
if (f->module->name != nullptr)
|
||||
{
|
||||
line += f->module->name->str();
|
||||
line += ".";
|
||||
}
|
||||
line += f->rulename == nullptr ? "???" : f->rulename;
|
||||
line += " at ";
|
||||
line += f->file == nullptr ? "(builtin)" : f->file->str();
|
||||
line += ":" + std::to_string(f->line);
|
||||
result.push_back(line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,45 +1,60 @@
|
||||
/*
|
||||
* Copyright 2022 René Ferdinand Rivera Morell
|
||||
* Copyright 2022-2023 René Ferdinand Rivera Morell
|
||||
* Copyright 2011 Steven Watanabe
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
|
||||
* (See accompanying file LICENSE.txt or
|
||||
* https://www.bfgroup.xyz/b2/LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef FUNCTION_SW20111123_H
|
||||
#define FUNCTION_SW20111123_H
|
||||
#ifndef B2_FUNCTION_H
|
||||
#define B2_FUNCTION_H
|
||||
|
||||
#include "config.h"
|
||||
#include "object.h"
|
||||
#include "frames.h"
|
||||
#include "lists.h"
|
||||
#include "parse.h"
|
||||
#include "jam_strings.h"
|
||||
#include "lists.h"
|
||||
#include "object.h"
|
||||
#include "parse.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
typedef struct _function FUNCTION;
|
||||
|
||||
typedef std::function<LIST* (FRAME *, int32_t flags)> function_builtin_t;
|
||||
typedef std::function<LIST *(FRAME *, int32_t flags)> function_builtin_t;
|
||||
|
||||
typedef FUNCTION* function_ptr;
|
||||
typedef FUNCTION * function_ptr;
|
||||
|
||||
FUNCTION * function_compile( PARSE * parse );
|
||||
FUNCTION * function_builtin( function_builtin_t func, int32_t flags, const char * * args );
|
||||
void function_refer( FUNCTION * );
|
||||
void function_free( FUNCTION * );
|
||||
OBJECT * function_rulename( FUNCTION * );
|
||||
void function_set_rulename( FUNCTION *, OBJECT * );
|
||||
void function_location( FUNCTION *, OBJECT * *, int32_t * );
|
||||
LIST * function_run( FUNCTION * function, FRAME * frame );
|
||||
FUNCTION * function_compile(PARSE * parse);
|
||||
FUNCTION * function_builtin(
|
||||
function_builtin_t func, int32_t flags, const char ** args);
|
||||
void function_refer(FUNCTION *);
|
||||
void function_free(FUNCTION *);
|
||||
OBJECT * function_rulename(FUNCTION *);
|
||||
void function_set_rulename(FUNCTION *, OBJECT *);
|
||||
void function_location(FUNCTION *, OBJECT **, int32_t *);
|
||||
LIST * function_run(FUNCTION * function, FRAME * frame);
|
||||
|
||||
FUNCTION * function_compile_actions( const char * actions, OBJECT * file, int32_t line );
|
||||
void function_run_actions( FUNCTION * function, FRAME * frame, string * out );
|
||||
FUNCTION * function_compile_actions(
|
||||
const char * actions, OBJECT * file, int32_t line);
|
||||
void function_run_actions(FUNCTION * function, FRAME * frame, string * out);
|
||||
|
||||
FUNCTION * function_bind_variables( FUNCTION * f, module_t * module, int32_t * counter );
|
||||
FUNCTION * function_unbind_variables( FUNCTION * f );
|
||||
FUNCTION * function_bind_variables(
|
||||
FUNCTION * f, module_t * module, int32_t * counter);
|
||||
FUNCTION * function_unbind_variables(FUNCTION * f);
|
||||
|
||||
LIST * function_get_variables( FUNCTION * f );
|
||||
LIST * function_get_variables(FUNCTION * f);
|
||||
|
||||
void function_done( void );
|
||||
void function_done(void);
|
||||
|
||||
namespace b2 { namespace jam {
|
||||
|
||||
struct backtrace
|
||||
{
|
||||
static std::string to_string(frame * f);
|
||||
static list_ref to_list(frame * f);
|
||||
};
|
||||
|
||||
}} // namespace b2::jam
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,6 +18,8 @@ Distributed under the Boost Software License, Version 1.0.
|
||||
|
||||
#include "mod_path.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace b2 { namespace jam { namespace modules {
|
||||
|
||||
list_ref binding(std::string module_name)
|
||||
@@ -194,15 +196,18 @@ void load(value_ref module_name,
|
||||
// Set the binding rule.
|
||||
module_target_r.on_set("BINDRULE", "modules.record-binding");
|
||||
// Do the load, in the module.
|
||||
module_scope load_scope(context.frame, module_name->str());
|
||||
parse_include(module_target, load_scope.frame());
|
||||
// Allow the module to see its own names with full
|
||||
// qualification.
|
||||
list_ref to_import(module_rules(load_scope), true);
|
||||
for (auto rule : to_import)
|
||||
{
|
||||
import_rule(find_rule(rule, load_scope), load_scope,
|
||||
module_name + "." + rule);
|
||||
module_scope_in_function in_module(
|
||||
context.frame, module_name->str());
|
||||
parse_include(module_target, in_module.frame());
|
||||
// Allow the module to see its own names with full
|
||||
// qualification.
|
||||
list_ref to_import(module_rules(in_module), true);
|
||||
for (auto rule : to_import)
|
||||
{
|
||||
import_rule(find_rule(rule, in_module), in_module,
|
||||
module_name + "." + rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Did the include succeed?
|
||||
@@ -251,6 +256,14 @@ void load(value_ref module_name,
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
value_ref caller_module(FRAME * frame, int levels = 0)
|
||||
{
|
||||
for (; levels >= 0; --levels) frame = frame->prev ? frame->prev : frame;
|
||||
return frame->module == root_module() ? nullptr : frame->module->name;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
void import(list_cref module_names,
|
||||
list_cref rules_opt,
|
||||
list_cref rename_opt,
|
||||
@@ -278,11 +291,7 @@ void import(list_cref module_names,
|
||||
+ "When loading multiple modules, no specific rules or renaming is allowed.");
|
||||
}
|
||||
|
||||
FRAME * caller_frame
|
||||
= context.frame->prev ? context.frame->prev : context.frame;
|
||||
value_ref caller_module = caller_frame->module == root_module()
|
||||
? nullptr
|
||||
: caller_frame->module->name;
|
||||
value_ref caller_module = detail::caller_module(context.frame);
|
||||
|
||||
// Import each specified module
|
||||
for (value_ref m : module_names)
|
||||
@@ -324,7 +333,19 @@ void import(list_cref module_names,
|
||||
}
|
||||
|
||||
if (!(*loaded_v).contains(module_basename))
|
||||
{
|
||||
auto grand_caller = detail::caller_module(context.frame, 1);
|
||||
out_printf(
|
||||
"b2::jam::modules::import: GRAND-CALLER-CURRENT-NEXT modules = %s, %s, %s, %s\n",
|
||||
grand_caller.has_value() ? grand_caller->str() : "<>",
|
||||
caller_module.has_value() ? caller_module->str() : "<>",
|
||||
context.frame->module->name ? context.frame->module->name->str()
|
||||
: "<root>",
|
||||
module_basename.c_str());
|
||||
load(module_basename, value_ref(), list_cref(*search), context_ref);
|
||||
// run_rule(context.frame, "modules.load",
|
||||
// list_ref(module_basename));
|
||||
}
|
||||
else if (!(*tested_v).contains(m))
|
||||
{
|
||||
// Native modules are pre-loaded but can be untested. In that case
|
||||
|
||||
@@ -304,8 +304,7 @@ struct regex_grep_task
|
||||
{
|
||||
if (text_grep_result_expressions.get(i))
|
||||
{
|
||||
std::string m(grep_i[i].str, grep_i[i].size);
|
||||
result->push_back(m);
|
||||
result->push_back(grep_i[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ LIST * sequence_transform( FRAME * frame, int flags )
|
||||
frame_init( inner );
|
||||
inner->prev = frame;
|
||||
inner->prev_user = frame->prev_user;
|
||||
inner->module = frame->prev->module;
|
||||
inner->module = b2::ensure_valid(frame->prev->module);
|
||||
|
||||
lol_add( inner->args, list_push_back( list_copy_range( function, args_begin, args_end ), object_copy( list_item( iter ) ) ) );
|
||||
result = list_append( result, evaluate_rule( rule, function_name, inner ) );
|
||||
|
||||
@@ -11,23 +11,28 @@ Distributed under the Boost Software License, Version 1.0.
|
||||
#include <thread>
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
# include <sys/sysctl.h>
|
||||
# include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if !defined(OS_NT)
|
||||
# include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
# include <windows.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) || defined(__GLIBC__)
|
||||
// Need to define this in case it's not as that's the only way to get the
|
||||
// sched_* APIs.
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
# endif
|
||||
# include <sched.h>
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) && defined(__GLIBC__)
|
||||
// For backtrace_symbols() call.
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
b2::system_info::system_info() {}
|
||||
@@ -164,3 +169,24 @@ unsigned int b2::system_info::cpu_thread_count()
|
||||
}
|
||||
return cpu_thread_count_;
|
||||
}
|
||||
|
||||
std::string b2::stacktrace::to_string()
|
||||
{
|
||||
std::string result;
|
||||
|
||||
#if defined(OS_LINUX) && defined(__GLIBC__)
|
||||
;
|
||||
void * frame_buffer[2048];
|
||||
auto frame_count = ::backtrace(frame_buffer, 2048);
|
||||
if (auto frame_strings = ::backtrace_symbols(frame_buffer, frame_count))
|
||||
{
|
||||
for (decltype(frame_count) i = 0; i < frame_count; ++i)
|
||||
{
|
||||
if (i > 0) result += "\n";
|
||||
result += frame_strings[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -12,6 +12,8 @@ Distributed under the Boost Software License, Version 1.0.
|
||||
#include "bind.h"
|
||||
#include "value.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace b2 {
|
||||
|
||||
/*
|
||||
@@ -44,6 +46,11 @@ class system_info : public object
|
||||
unsigned int cpu_thread_count_ = 0;
|
||||
};
|
||||
|
||||
struct stacktrace
|
||||
{
|
||||
static std::string to_string();
|
||||
};
|
||||
|
||||
struct sysinfo_module : b2::bind::module_<sysinfo_module>
|
||||
{
|
||||
const char * module_name = "sysinfo";
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "regexp.h"
|
||||
#include "jam.h"
|
||||
#include "output.h"
|
||||
#include "strview.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
@@ -73,7 +74,7 @@ void regerror(char const * s);
|
||||
* The "internal use only" fields in regexp.h are present to pass info from
|
||||
* compile to execute that permits the execute phase to run lots faster on
|
||||
* simple cases. They are:
|
||||
:
|
||||
*
|
||||
* regstart char that must begin a match; '\0' if none obvious.
|
||||
* reganch is the match anchored (at beginning-of-line only)?
|
||||
* regmust string (pointer into program) that match must include, or NULL.
|
||||
@@ -186,10 +187,6 @@ return (NULL); \
|
||||
#define SPSTART 04 /* Starts with * or +. */
|
||||
#define WORST 0 /* Worst case. */
|
||||
|
||||
#ifndef STATIC
|
||||
#define STATIC static
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
char regdummy = 0;
|
||||
/*
|
||||
@@ -215,6 +212,7 @@ inline C * regnext(C * p)
|
||||
// The compiled regex program to match with.
|
||||
struct regex_prog
|
||||
{
|
||||
std::string regexpr; /* The not-compiled regex. */
|
||||
char regstart = 0; /* Internal use only. */
|
||||
char reganch = 0; /* Internal use only. */
|
||||
const char * regmust = nullptr; /* Internal use only. */
|
||||
@@ -281,8 +279,10 @@ struct compiler
|
||||
|
||||
/* Allocate space. */
|
||||
r = (regex_prog *)BJAM_MALLOC(sizeof(regex_prog) + regsize);
|
||||
r->progsize = regsize;
|
||||
if (r == NULL) FAIL("out of space");
|
||||
b2::jam::ctor_ptr<regex_prog>(r);
|
||||
r->regexpr = exp;
|
||||
r->progsize = regsize;
|
||||
|
||||
/* Second pass: emit code. */
|
||||
regparse = (char *)exp;
|
||||
@@ -814,114 +814,72 @@ struct executor
|
||||
/*
|
||||
* Global work variables for regexec().
|
||||
*/
|
||||
const char * reginput; /* String-input pointer. */
|
||||
const char * regbol; /* Beginning of input, for ^ check. */
|
||||
const char ** regstartp; /* Pointer to startp array. */
|
||||
const char ** regendp; /* Ditto for endp. */
|
||||
|
||||
#ifdef DEBUG
|
||||
int32_t regnarrate = 0;
|
||||
void regdump();
|
||||
STATIC char * regprop();
|
||||
#endif
|
||||
string_view reg_in; /* String-input. */
|
||||
const char * reg_bol; /* Beginning of input, for ^ check. */
|
||||
|
||||
/*
|
||||
- regexec - match a regexp against a string
|
||||
*/
|
||||
int32_t regexec(
|
||||
const regex_prog * prog, regex_expr * expr, const char * string)
|
||||
bool regexec(
|
||||
const regex_prog & prog, regex_expr & expr, const string_view & string)
|
||||
{
|
||||
char * s;
|
||||
|
||||
/* Be paranoid... */
|
||||
if (prog == NULL)
|
||||
{
|
||||
regerror("NULL parameter");
|
||||
return (0);
|
||||
}
|
||||
if (string == NULL)
|
||||
{
|
||||
regerror("NULL string");
|
||||
return (0);
|
||||
}
|
||||
|
||||
string_view s;
|
||||
/* Check validity of program. */
|
||||
if (UCHARAT(prog->program) != MAGIC)
|
||||
if (UCHARAT(prog.program) != MAGIC)
|
||||
{
|
||||
regerror("corrupted program");
|
||||
return (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If there is a "must appear" string, look for it. */
|
||||
if (prog->regmust != NULL)
|
||||
{
|
||||
s = (char *)string;
|
||||
while ((s = strchr(s, prog->regmust[0])) != NULL)
|
||||
{
|
||||
if (!strncmp(s, prog->regmust, prog->regmlen))
|
||||
break; /* Found it. */
|
||||
++s;
|
||||
}
|
||||
if (s == NULL) /* Not present. */
|
||||
return 0;
|
||||
}
|
||||
if (prog.regmust != NULL
|
||||
&& string.find(prog.regmust, 0, prog.regmlen) == string_view::npos)
|
||||
return false; /* Not present. */
|
||||
|
||||
/* Mark beginning of line for ^ . */
|
||||
regbol = (char *)string;
|
||||
reg_bol = string.begin();
|
||||
|
||||
/* Simplest case: anchored match need be tried only once. */
|
||||
if (prog->reganch) return regtry(prog, expr, string);
|
||||
if (prog.reganch) return regtry(prog, expr, string);
|
||||
|
||||
/* Messy cases: unanchored match. */
|
||||
s = (char *)string;
|
||||
if (prog->regstart != '\0') /* We know what char it must start with. */
|
||||
while ((s = strchr(s, prog->regstart)) != NULL)
|
||||
s = string;
|
||||
if (prog.regstart != '\0') /* We know what char it must start with. */
|
||||
for (auto j = s.find(prog.regstart); j != string_view::npos;
|
||||
j = s.find(prog.regstart))
|
||||
{
|
||||
if (regtry(prog, expr, s)) return (1);
|
||||
s++;
|
||||
s = s.substr(j);
|
||||
if (regtry(prog, expr, s)) return true;
|
||||
s = s.substr(1);
|
||||
}
|
||||
else
|
||||
/* We do not -- general case. */
|
||||
do
|
||||
{
|
||||
if (regtry(prog, expr, s)) return (1);
|
||||
if (regtry(prog, expr, s)) return true;
|
||||
}
|
||||
while (*s++ != '\0');
|
||||
while (!(s = s.substr(1)).empty());
|
||||
|
||||
/* Failure. */
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* regtry() - try match at specific point.
|
||||
* success == true
|
||||
*/
|
||||
|
||||
int32_t /* 0 failure, 1 success */
|
||||
regtry(const regex_prog * prog, regex_expr * expr, const char * string)
|
||||
inline bool regtry(
|
||||
const regex_prog & prog, regex_expr & expr, const string_view & string)
|
||||
{
|
||||
int32_t i;
|
||||
const char ** sp;
|
||||
const char ** ep;
|
||||
|
||||
reginput = string;
|
||||
regstartp = expr->startp;
|
||||
regendp = expr->endp;
|
||||
|
||||
sp = expr->startp;
|
||||
ep = expr->endp;
|
||||
for (i = NSUBEXP; i > 0; --i)
|
||||
reg_in = string;
|
||||
expr = regex_expr {};
|
||||
if (regmatch(prog.program + 1, expr))
|
||||
{
|
||||
*sp++ = NULL;
|
||||
*ep++ = NULL;
|
||||
expr.sub[0] = string_view(string.begin(), reg_in.begin());
|
||||
return true;
|
||||
}
|
||||
if (regmatch(prog->program + 1))
|
||||
{
|
||||
expr->startp[0] = string;
|
||||
expr->endp[0] = reginput;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -934,47 +892,45 @@ struct executor
|
||||
* whether the rest of the match failed) by a loop instead of by recursion.
|
||||
*/
|
||||
|
||||
int32_t /* 0 failure, 1 success */
|
||||
regmatch(const char * prog)
|
||||
bool regmatch(const char * prog, regex_expr & expr)
|
||||
{
|
||||
const char * scan; /* Current node. */
|
||||
const char * next; /* Next node. */
|
||||
|
||||
scan = prog;
|
||||
#ifdef DEBUG
|
||||
if (scan != NULL && regnarrate) err_printf("%s(\n", regprop(scan));
|
||||
#endif
|
||||
while (scan != NULL)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (regnarrate) err_printf("%s...\n", regprop(scan));
|
||||
#endif
|
||||
next = regnext(scan);
|
||||
|
||||
switch (OP(scan))
|
||||
{
|
||||
case BOL:
|
||||
if (reginput != regbol) return (0);
|
||||
if (reg_in.begin() != reg_bol) return false;
|
||||
break;
|
||||
case EOL:
|
||||
if (*reginput != '\0') return (0);
|
||||
if (!reg_in.empty()) return false;
|
||||
break;
|
||||
case WORDA:
|
||||
/* Must be looking at a letter, digit, or _ */
|
||||
if ((!isalnum(*reginput)) && *reginput != '_') return (0);
|
||||
if (!reg_in.empty() && (!isalnum(reg_in[0]))
|
||||
&& reg_in[0] != '_')
|
||||
return false;
|
||||
/* Prev must be BOL or nonword */
|
||||
if (reginput > regbol
|
||||
&& (isalnum(reginput[-1]) || reginput[-1] == '_'))
|
||||
return (0);
|
||||
if (reg_in.begin() > reg_bol
|
||||
&& (isalnum(reg_in.begin()[-1])
|
||||
|| reg_in.begin()[-1] == '_'))
|
||||
return false;
|
||||
break;
|
||||
case WORDZ:
|
||||
/* Must be looking at non letter, digit, or _ */
|
||||
if (isalnum(*reginput) || *reginput == '_') return (0);
|
||||
if (reg_in.empty() || isalnum(reg_in[0])
|
||||
|| reg_in[0] == '_')
|
||||
return false;
|
||||
/* We don't care what the previous char was */
|
||||
break;
|
||||
case ANY:
|
||||
if (*reginput == '\0') return (0);
|
||||
reginput++;
|
||||
if (reg_in.empty()) return false;
|
||||
reg_in = reg_in.substr(1);
|
||||
break;
|
||||
case EXACTLY:
|
||||
{
|
||||
@@ -983,24 +939,24 @@ struct executor
|
||||
|
||||
opnd = OPERAND(scan);
|
||||
/* Inline the first character, for speed. */
|
||||
if (*opnd != *reginput) return (0);
|
||||
if (reg_in.empty() || *opnd != reg_in[0]) return false;
|
||||
len = strlen(opnd);
|
||||
if (len > 1 && strncmp(opnd, reginput, len) != 0)
|
||||
return (0);
|
||||
reginput += len;
|
||||
if (len > 1 && reg_in.compare(0, len, opnd) != 0)
|
||||
return false;
|
||||
reg_in = reg_in.substr(len);
|
||||
}
|
||||
break;
|
||||
case ANYOF:
|
||||
if (*reginput == '\0'
|
||||
|| strchr(OPERAND(scan), *reginput) == NULL)
|
||||
return (0);
|
||||
reginput++;
|
||||
if (reg_in.empty()
|
||||
|| strchr(OPERAND(scan), reg_in[0]) == NULL)
|
||||
return false;
|
||||
reg_in = reg_in.substr(1);
|
||||
break;
|
||||
case ANYBUT:
|
||||
if (*reginput == '\0'
|
||||
|| strchr(OPERAND(scan), *reginput) != NULL)
|
||||
return (0);
|
||||
reginput++;
|
||||
if (reg_in.empty()
|
||||
|| strchr(OPERAND(scan), reg_in[0]) != NULL)
|
||||
return false;
|
||||
reg_in = reg_in.substr(1);
|
||||
break;
|
||||
case NOTHING: break;
|
||||
case BACK: break;
|
||||
@@ -1015,23 +971,23 @@ struct executor
|
||||
case OPEN + 9:
|
||||
{
|
||||
int32_t no;
|
||||
const char * save;
|
||||
|
||||
no = OP(scan) - OPEN;
|
||||
save = reginput;
|
||||
auto save = reg_in;
|
||||
|
||||
if (regmatch(next))
|
||||
if (regmatch(next, expr))
|
||||
{
|
||||
/*
|
||||
* Don't set startp if some later
|
||||
* invocation of the same parentheses
|
||||
* already has.
|
||||
*/
|
||||
if (regstartp[no] == NULL) regstartp[no] = save;
|
||||
return (1);
|
||||
if (expr.sub[no].begin() == nullptr)
|
||||
expr.sub[no] = string_view(save.begin(), 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CLOSE + 1:
|
||||
@@ -1045,42 +1001,41 @@ struct executor
|
||||
case CLOSE + 9:
|
||||
{
|
||||
int32_t no;
|
||||
const char * save;
|
||||
|
||||
no = OP(scan) - CLOSE;
|
||||
save = reginput;
|
||||
auto save = reg_in;
|
||||
|
||||
if (regmatch(next))
|
||||
if (regmatch(next, expr))
|
||||
{
|
||||
/*
|
||||
* Don't set endp if some later
|
||||
* invocation of the same parentheses
|
||||
* already has.
|
||||
*/
|
||||
if (regendp[no] == NULL) regendp[no] = save;
|
||||
return (1);
|
||||
if (expr.sub[no].begin() && expr.sub[no].empty())
|
||||
expr.sub[no] = string_view(
|
||||
expr.sub[no].begin(), save.begin());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case BRANCH:
|
||||
{
|
||||
const char * save;
|
||||
|
||||
if (OP(next) != BRANCH) /* No choice. */
|
||||
next = OPERAND(scan); /* Avoid recursion. */
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
save = reginput;
|
||||
if (regmatch(OPERAND(scan))) return (1);
|
||||
reginput = save;
|
||||
auto save = reg_in;
|
||||
if (regmatch(OPERAND(scan), expr)) return true;
|
||||
reg_in = save;
|
||||
scan = regnext(scan);
|
||||
}
|
||||
while (scan != NULL && OP(scan) == BRANCH);
|
||||
return (0);
|
||||
return false;
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
@@ -1090,7 +1045,6 @@ struct executor
|
||||
{
|
||||
char nextch;
|
||||
int32_t no;
|
||||
const char * save;
|
||||
int32_t min;
|
||||
|
||||
/*
|
||||
@@ -1100,26 +1054,26 @@ struct executor
|
||||
nextch = '\0';
|
||||
if (OP(next) == EXACTLY) nextch = *OPERAND(next);
|
||||
min = (OP(scan) == STAR) ? 0 : 1;
|
||||
save = reginput;
|
||||
auto save = reg_in;
|
||||
no = regrepeat(OPERAND(scan));
|
||||
while (no >= min)
|
||||
{
|
||||
/* If it could work, try it. */
|
||||
if (nextch == '\0' || *reginput == nextch)
|
||||
if (regmatch(next)) return (1);
|
||||
if (nextch == '\0' || reg_in[0] == nextch)
|
||||
if (regmatch(next, expr)) return true;
|
||||
/* Couldn't or didn't -- back up. */
|
||||
no--;
|
||||
reginput = save + no;
|
||||
reg_in = save.substr(no);
|
||||
}
|
||||
return (0);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
return (1); /* Success! */
|
||||
return true; /* Success! */
|
||||
break;
|
||||
default:
|
||||
regerror("memory corruption");
|
||||
return (0);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1131,7 +1085,7 @@ struct executor
|
||||
* the terminating point.
|
||||
*/
|
||||
regerror("corrupted pointers");
|
||||
return (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1140,36 +1094,35 @@ struct executor
|
||||
int32_t regrepeat(const char * p)
|
||||
{
|
||||
int32_t count = 0;
|
||||
const char * scan;
|
||||
const char * opnd;
|
||||
|
||||
scan = reginput;
|
||||
auto scan = reg_in;
|
||||
opnd = OPERAND(p);
|
||||
switch (OP(p))
|
||||
{
|
||||
case ANY:
|
||||
count = int32_t(strlen(scan));
|
||||
scan += count;
|
||||
count = int32_t(scan.length());
|
||||
scan = scan.substr(count);
|
||||
break;
|
||||
case EXACTLY:
|
||||
while (*opnd == *scan)
|
||||
while (*opnd == scan[0])
|
||||
{
|
||||
count++;
|
||||
scan++;
|
||||
scan = scan.substr(1);
|
||||
}
|
||||
break;
|
||||
case ANYOF:
|
||||
while (*scan != '\0' && strchr(opnd, *scan) != NULL)
|
||||
while (!scan.empty() && strchr(opnd, scan[0]) != NULL)
|
||||
{
|
||||
count++;
|
||||
scan++;
|
||||
scan = scan.substr(1);
|
||||
}
|
||||
break;
|
||||
case ANYBUT:
|
||||
while (*scan != '\0' && strchr(opnd, *scan) == NULL)
|
||||
while (!scan.empty() && strchr(opnd, scan[0]) == NULL)
|
||||
{
|
||||
count++;
|
||||
scan++;
|
||||
scan = scan.substr(1);
|
||||
}
|
||||
break;
|
||||
default: /* Oh dear. Called inappropriately. */
|
||||
@@ -1177,152 +1130,21 @@ struct executor
|
||||
count = 0; /* Best compromise. */
|
||||
break;
|
||||
}
|
||||
reginput = scan;
|
||||
reg_in = scan;
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
}; // struct executor
|
||||
|
||||
int32_t regex_exec(const regex_prog & prog,
|
||||
regex_expr & expr,
|
||||
const char * start,
|
||||
const char * end)
|
||||
bool regex_exec(
|
||||
const regex_prog & prog, regex_expr & expr, const string_view & text)
|
||||
{
|
||||
executor e;
|
||||
return e.regexec(&prog, &expr, start);
|
||||
auto result = e.regexec(prog, expr, text);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
STATIC char * regprop();
|
||||
|
||||
/*
|
||||
- regdump - dump a regexp onto stdout in vaguely comprehensible form
|
||||
*/
|
||||
void regdump(regexp * r)
|
||||
{
|
||||
char * s;
|
||||
char op = EXACTLY; /* Arbitrary non-END op. */
|
||||
char * next;
|
||||
|
||||
s = r->program + 1;
|
||||
while (op != END)
|
||||
{ /* While that wasn't END last time... */
|
||||
op = OP(s);
|
||||
out_printf("%2d%s", s - r->program, regprop(s)); /* Where, what. */
|
||||
next = regnext(s);
|
||||
if (next == NULL) /* Next ptr. */
|
||||
out_printf("(0)");
|
||||
else
|
||||
out_printf("(%d)", (s - r->program) + (next - s));
|
||||
s += 3;
|
||||
if (op == ANYOF || op == ANYBUT || op == EXACTLY)
|
||||
{
|
||||
/* Literal string, where present. */
|
||||
while (*s != '\0')
|
||||
{
|
||||
out_putc(*s);
|
||||
s++;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
out_putc('\n');
|
||||
}
|
||||
|
||||
/* Header fields of interest. */
|
||||
if (r->regstart != '\0') out_printf("start `%c' ", r->regstart);
|
||||
if (r->reganch) out_printf("anchored ");
|
||||
if (r->regmust != NULL) out_printf("must have \"%s\"", r->regmust);
|
||||
out_printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
- regprop - printable representation of opcode
|
||||
*/
|
||||
static char * regprop(char * op)
|
||||
{
|
||||
char * p;
|
||||
static char buf[50];
|
||||
|
||||
(void)strcpy(buf, ":");
|
||||
|
||||
switch (OP(op))
|
||||
{
|
||||
case BOL: p = "BOL"; break;
|
||||
case EOL: p = "EOL"; break;
|
||||
case ANY: p = "ANY"; break;
|
||||
case ANYOF: p = "ANYOF"; break;
|
||||
case ANYBUT: p = "ANYBUT"; break;
|
||||
case BRANCH: p = "BRANCH"; break;
|
||||
case EXACTLY: p = "EXACTLY"; break;
|
||||
case NOTHING: p = "NOTHING"; break;
|
||||
case BACK: p = "BACK"; break;
|
||||
case END: p = "END"; break;
|
||||
case OPEN + 1:
|
||||
case OPEN + 2:
|
||||
case OPEN + 3:
|
||||
case OPEN + 4:
|
||||
case OPEN + 5:
|
||||
case OPEN + 6:
|
||||
case OPEN + 7:
|
||||
case OPEN + 8:
|
||||
case OPEN + 9:
|
||||
sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN);
|
||||
p = NULL;
|
||||
break;
|
||||
case CLOSE + 1:
|
||||
case CLOSE + 2:
|
||||
case CLOSE + 3:
|
||||
case CLOSE + 4:
|
||||
case CLOSE + 5:
|
||||
case CLOSE + 6:
|
||||
case CLOSE + 7:
|
||||
case CLOSE + 8:
|
||||
case CLOSE + 9:
|
||||
sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE);
|
||||
p = NULL;
|
||||
break;
|
||||
case STAR: p = "STAR"; break;
|
||||
case PLUS: p = "PLUS"; break;
|
||||
case WORDA: p = "WORDA"; break;
|
||||
case WORDZ: p = "WORDZ"; break;
|
||||
default: regerror("corrupted opcode"); break;
|
||||
}
|
||||
if (p != NULL) (void)strcat(buf, p);
|
||||
return (buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following is provided for those people who do not have strcspn() in
|
||||
* their C libraries. They should get off their butts and do something
|
||||
* about it; at least one public-domain implementation of those (highly
|
||||
* useful) string routines has been published on Usenet.
|
||||
*/
|
||||
#ifdef STRCSPN
|
||||
/*
|
||||
* strcspn - find length of initial segment of s1 consisting entirely
|
||||
* of characters not from s2
|
||||
*/
|
||||
|
||||
static int32_t strcspn(char * s1, char * s2)
|
||||
{
|
||||
char * scan1;
|
||||
char * scan2;
|
||||
int32_t count;
|
||||
|
||||
count = 0;
|
||||
for (scan1 = s1; *scan1 != '\0'; scan1++)
|
||||
{
|
||||
for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */
|
||||
if (*scan1 == *scan2++) return (count);
|
||||
count++;
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
#endif
|
||||
|
||||
void regerror(char const * s) { out_printf("re error %s\n", s); }
|
||||
|
||||
regex_prog & program::compile(const char * pattern)
|
||||
@@ -1340,10 +1162,9 @@ program::program(const char * pattern) { reset(pattern); }
|
||||
void program::reset(const char * pattern) { compiled = &compile(pattern); }
|
||||
|
||||
program::result_iterator::result_iterator(
|
||||
const regex_prog & c, const char * b, const char * e)
|
||||
const regex_prog & c, const string_view & s)
|
||||
: compiled(&c)
|
||||
, match { b, 0 }
|
||||
, rest { b, std::size_t(e - b) }
|
||||
, rest(s)
|
||||
{
|
||||
advance();
|
||||
}
|
||||
@@ -1351,20 +1172,11 @@ program::result_iterator::result_iterator(
|
||||
void program::result_iterator::advance()
|
||||
{
|
||||
// We start searching for a match at the end of the previous match.
|
||||
if (regex_exec(*compiled, expressions, rest.begin(), rest.end()) == 0)
|
||||
{
|
||||
// No next match, reset to end/nothing.
|
||||
match.str = nullptr;
|
||||
match.size = 0;
|
||||
}
|
||||
else
|
||||
if (regex_exec(*compiled, expressions, rest))
|
||||
{
|
||||
// A match means the subexpressions are filled in and the first entry
|
||||
// is the full match.
|
||||
match.str = expressions.startp[0];
|
||||
match.size = expressions.endp[0] - expressions.startp[0];
|
||||
rest.str = expressions.endp[0];
|
||||
rest.size = rest.end() - rest.begin();
|
||||
// is the full match. Advance `rest` to follow the match.
|
||||
rest = string_view(expressions.sub[0].end(), rest.end());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "mem.h"
|
||||
#include "strview.h"
|
||||
#include "types.h"
|
||||
#include "value.h"
|
||||
|
||||
@@ -24,8 +25,7 @@ namespace b2 { namespace regex {
|
||||
// And expressions [1,NSUBEXP] are the subexpressions matched.
|
||||
struct regex_expr
|
||||
{
|
||||
char const * startp[NSUBEXP] = { nullptr };
|
||||
char const * endp[NSUBEXP] = { nullptr };
|
||||
string_view sub[NSUBEXP];
|
||||
};
|
||||
|
||||
// The compiled regex program to match with.
|
||||
@@ -43,6 +43,7 @@ struct program
|
||||
|
||||
void reset(const char * pattern);
|
||||
|
||||
result_iterator search(const string_view & str);
|
||||
result_iterator search(const char * str_begin, const char * str_end);
|
||||
result_iterator search(const char * str_begin);
|
||||
|
||||
@@ -55,12 +56,12 @@ struct program
|
||||
struct program::result_iterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = value::str_view;
|
||||
using value_type = string_view;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
|
||||
result_iterator(const regex_prog & c, const char * b, const char * e);
|
||||
result_iterator(const regex_prog & c, const string_view & s);
|
||||
result_iterator(const result_iterator & o) = default;
|
||||
result_iterator(result_iterator && o) = default;
|
||||
|
||||
@@ -69,34 +70,37 @@ struct program::result_iterator
|
||||
advance();
|
||||
return *this;
|
||||
}
|
||||
inline reference operator*() const { return match; }
|
||||
inline pointer operator->() const { return &match; }
|
||||
explicit inline operator bool() const { return match.str != nullptr; }
|
||||
inline value_type operator[](std::size_t i) const
|
||||
inline reference operator*() const { return (*this)[0]; }
|
||||
inline pointer operator->() const { return &(*this)[0]; }
|
||||
explicit inline operator bool() const { return !(*this)[0].empty(); }
|
||||
inline reference operator[](std::size_t i) const
|
||||
{
|
||||
return i <= NSUBEXP ? value_type { expressions.startp[i],
|
||||
std::size_t(expressions.endp[i] - expressions.startp[i]) }
|
||||
: value_type { nullptr, 0 };
|
||||
static const value_type invalid { nullptr, 0 };
|
||||
return i <= NSUBEXP ? expressions.sub[i] : invalid;
|
||||
}
|
||||
|
||||
private:
|
||||
const regex_prog * compiled = nullptr;
|
||||
regex_expr expressions;
|
||||
value_type match;
|
||||
value_type rest;
|
||||
|
||||
void advance();
|
||||
};
|
||||
|
||||
inline program::result_iterator program::search(const string_view & str)
|
||||
{
|
||||
return result_iterator(*compiled, str);
|
||||
}
|
||||
|
||||
inline program::result_iterator program::search(
|
||||
const char * str_begin, const char * str_end)
|
||||
{
|
||||
return result_iterator(*compiled, str_begin, str_end);
|
||||
return this->search(string_view(str_begin, str_end));
|
||||
}
|
||||
|
||||
inline program::result_iterator program::search(const char * str_begin)
|
||||
{
|
||||
return this->search(str_begin, str_begin + std::strlen(str_begin));
|
||||
return this->search(string_view(str_begin, std::strlen(str_begin)));
|
||||
}
|
||||
|
||||
}} // namespace b2::regex
|
||||
|
||||
@@ -109,7 +109,7 @@ static rule_ptr enter_rule( b2::value_ptr rulename, module_ptr target_module )
|
||||
r->module = 0;
|
||||
r->actions = 0;
|
||||
r->exported = 0;
|
||||
r->module = target_module;
|
||||
r->module = b2::ensure_valid(target_module);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -131,7 +131,7 @@ static rule_ptr define_rule( module_ptr src_module, b2::value_ptr rulename,
|
||||
set_rule_body( r, 0 );
|
||||
set_rule_actions( r, 0 );
|
||||
/* r will be executed in the source module. */
|
||||
r->module = src_module;
|
||||
r->module = b2::ensure_valid(src_module);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
991
src/engine/strview.h
Normal file
991
src/engine/strview.h
Normal file
@@ -0,0 +1,991 @@
|
||||
#ifndef BOOST_CORE_STRING_VIEW_HPP_INCLUDED
|
||||
#define BOOST_CORE_STRING_VIEW_HPP_INCLUDED
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
// boost::core::basic_string_view<Ch>
|
||||
//
|
||||
// Copyright 2021 Peter Dimov
|
||||
// Copyright 2023 René Ferdinand Rivera Morell
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include <iosfwd>
|
||||
#include <ios>
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
namespace core
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class Ch> struct sv_to_uchar
|
||||
{
|
||||
typedef Ch type;
|
||||
};
|
||||
|
||||
template<> struct sv_to_uchar<char>
|
||||
{
|
||||
typedef unsigned char type;
|
||||
};
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
#endif
|
||||
|
||||
template<class Ch> constexpr std::size_t find_first_of( Ch const* p_, std::size_t n_, Ch const* s, std::size_t pos, std::size_t n ) noexcept
|
||||
{
|
||||
typedef typename sv_to_uchar<Ch>::type UCh;
|
||||
|
||||
unsigned char table[ 256 ] = {};
|
||||
|
||||
bool use_table = true;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
UCh ch = s[ j ];
|
||||
|
||||
if( ch >= 0 && ch < 256 )
|
||||
{
|
||||
table[ ch ] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_table = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( use_table )
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
UCh ch = p_[ i ];
|
||||
if( ch >= 0 && ch < 256 && table[ ch ] ) return i;
|
||||
}
|
||||
}
|
||||
else if( n >= 16 )
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
if( std::char_traits<Ch>::find( s, n, ch ) != 0 ) return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
if( s[ j ] == ch ) return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>( -1 );
|
||||
}
|
||||
|
||||
template<class Ch> constexpr std::size_t find_last_of( Ch const* p_, Ch const* s, std::size_t pos, std::size_t n ) noexcept
|
||||
{
|
||||
typedef typename sv_to_uchar<Ch>::type UCh;
|
||||
|
||||
unsigned char table[ 256 ] = {};
|
||||
|
||||
bool use_table = true;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
UCh ch = s[ j ];
|
||||
|
||||
if( ch >= 0 && ch < 256 )
|
||||
{
|
||||
table[ ch ] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_table = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t const npos = static_cast< std::size_t >( -1 );
|
||||
|
||||
std::size_t i = pos;
|
||||
|
||||
if( use_table )
|
||||
{
|
||||
do
|
||||
{
|
||||
UCh ch = p_[ i ];
|
||||
|
||||
if( ch >= 0 && ch < 256 && table[ ch ] ) return i;
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
else if( n >= 16 )
|
||||
{
|
||||
do
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
if( std::char_traits<Ch>::find( s, n, ch ) != 0 ) return i;
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
if( s[ j ] == ch ) return i;
|
||||
}
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
template<class Ch> constexpr std::size_t find_first_not_of( Ch const* p_, std::size_t n_, Ch const* s, std::size_t pos, std::size_t n ) noexcept
|
||||
{
|
||||
typedef typename sv_to_uchar<Ch>::type UCh;
|
||||
|
||||
unsigned char table[ 256 ] = {};
|
||||
|
||||
bool use_table = true;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
UCh ch = s[ j ];
|
||||
|
||||
if( ch >= 0 && ch < 256 )
|
||||
{
|
||||
table[ ch ] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_table = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( use_table )
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
UCh ch = p_[ i ];
|
||||
if( !( ch >= 0 && ch < 256 && table[ ch ] ) ) return i;
|
||||
}
|
||||
}
|
||||
else if( n >= 16 )
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
if( std::char_traits<Ch>::find( s, n, ch ) == 0 ) return i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
bool r = false;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
if( s[ j ] == ch )
|
||||
{
|
||||
r = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !r ) return i;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<std::size_t>( -1 );
|
||||
}
|
||||
|
||||
template<class Ch> constexpr std::size_t find_last_not_of( Ch const* p_, Ch const* s, std::size_t pos, std::size_t n ) noexcept
|
||||
{
|
||||
typedef typename sv_to_uchar<Ch>::type UCh;
|
||||
|
||||
unsigned char table[ 256 ] = {};
|
||||
|
||||
bool use_table = true;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
UCh ch = s[ j ];
|
||||
|
||||
if( ch >= 0 && ch < 256 )
|
||||
{
|
||||
table[ ch ] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
use_table = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t const npos = static_cast< std::size_t >( -1 );
|
||||
|
||||
std::size_t i = pos;
|
||||
|
||||
if( use_table )
|
||||
{
|
||||
do
|
||||
{
|
||||
UCh ch = p_[ i ];
|
||||
|
||||
if( !( ch >= 0 && ch < 256 && table[ ch ] ) ) return i;
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
else if( n >= 16 )
|
||||
{
|
||||
do
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
if( std::char_traits<Ch>::find( s, n, ch ) == 0 ) return i;
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
Ch ch = p_[ i ];
|
||||
|
||||
bool r = false;
|
||||
|
||||
for( std::size_t j = 0; j < n; ++j )
|
||||
{
|
||||
if( s[ j ] == ch )
|
||||
{
|
||||
r = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !r ) return i;
|
||||
|
||||
--i;
|
||||
}
|
||||
while( i != npos );
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ >= 406
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<class Ch> class basic_string_view
|
||||
{
|
||||
private:
|
||||
|
||||
Ch const* p_;
|
||||
mutable std::size_t n_; // mutable to allow mod within C++11 constexpr rules
|
||||
|
||||
public:
|
||||
|
||||
// types
|
||||
|
||||
typedef std::char_traits<Ch> traits_type;
|
||||
typedef Ch value_type;
|
||||
typedef Ch* pointer;
|
||||
typedef Ch const* const_pointer;
|
||||
typedef Ch& reference;
|
||||
typedef Ch const& const_reference;
|
||||
typedef Ch const* const_iterator;
|
||||
typedef const_iterator iterator;
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
typedef const_reverse_iterator reverse_iterator;
|
||||
typedef std::size_t size_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
// npos
|
||||
|
||||
static constexpr size_type npos = static_cast<size_type>( -1 );
|
||||
|
||||
public:
|
||||
|
||||
// construction and assignment
|
||||
|
||||
constexpr basic_string_view() noexcept: p_(), n_()
|
||||
{
|
||||
}
|
||||
|
||||
constexpr basic_string_view( Ch const* str ) noexcept: p_( str ), n_( traits_type::length( str ) )
|
||||
{
|
||||
}
|
||||
|
||||
constexpr basic_string_view( Ch const* str, size_type len ) noexcept: p_( str ), n_( len )
|
||||
{
|
||||
}
|
||||
|
||||
template<class End> constexpr basic_string_view( Ch const* first, End last,
|
||||
typename std::enable_if<std::is_same<End, Ch const*>::value, void >::type* = 0 ) noexcept: p_( first ), n_( last - first )
|
||||
{
|
||||
}
|
||||
|
||||
template<class A> basic_string_view( std::basic_string<Ch, std::char_traits<Ch>, A> const& str ) noexcept: p_( str.data() ), n_( str.size() )
|
||||
{
|
||||
}
|
||||
|
||||
basic_string_view( std::nullptr_t ) = delete;
|
||||
|
||||
// constexpr basic_string_view& operator=( basic_string_view const& ) noexcept & = default;
|
||||
|
||||
// conversions
|
||||
|
||||
template<class A> operator std::basic_string<Ch, std::char_traits<Ch>, A>() const
|
||||
{
|
||||
return std::basic_string<Ch, std::char_traits<Ch>, A>( data(), size() );
|
||||
}
|
||||
|
||||
// iterator support
|
||||
|
||||
constexpr const_iterator begin() const noexcept
|
||||
{
|
||||
return p_;
|
||||
}
|
||||
|
||||
constexpr const_iterator end() const noexcept
|
||||
{
|
||||
return p_ + n_;
|
||||
}
|
||||
|
||||
constexpr const_iterator cbegin() const noexcept
|
||||
{
|
||||
return p_;
|
||||
}
|
||||
|
||||
constexpr const_iterator cend() const noexcept
|
||||
{
|
||||
return p_ + n_;
|
||||
}
|
||||
|
||||
constexpr const_reverse_iterator rbegin() const noexcept
|
||||
{
|
||||
return const_reverse_iterator( end() );
|
||||
}
|
||||
|
||||
constexpr const_reverse_iterator rend() const noexcept
|
||||
{
|
||||
return const_reverse_iterator( begin() );
|
||||
}
|
||||
|
||||
constexpr const_reverse_iterator crbegin() const noexcept
|
||||
{
|
||||
return const_reverse_iterator( end() );
|
||||
}
|
||||
|
||||
constexpr const_reverse_iterator crend() const noexcept
|
||||
{
|
||||
return const_reverse_iterator( begin() );
|
||||
}
|
||||
|
||||
// capacity
|
||||
|
||||
constexpr size_type size() const noexcept
|
||||
{
|
||||
return n_;
|
||||
}
|
||||
|
||||
constexpr size_type length() const noexcept
|
||||
{
|
||||
return n_;
|
||||
}
|
||||
|
||||
constexpr size_type max_size() const noexcept
|
||||
{
|
||||
return npos / sizeof( Ch );
|
||||
}
|
||||
|
||||
constexpr bool empty() const noexcept
|
||||
{
|
||||
return n_ == 0;
|
||||
}
|
||||
|
||||
// element access
|
||||
|
||||
constexpr const_reference operator[]( size_type pos ) const noexcept
|
||||
{
|
||||
assert( pos < size() );
|
||||
return p_[ pos ];
|
||||
}
|
||||
|
||||
constexpr const_reference at( size_type pos ) const
|
||||
{
|
||||
if( pos >= size() )
|
||||
{
|
||||
throw std::out_of_range( "basic_string_view::at" );
|
||||
}
|
||||
|
||||
return p_[ pos ];
|
||||
}
|
||||
|
||||
constexpr const_reference front() const noexcept
|
||||
{
|
||||
assert( !empty() );
|
||||
return p_[ 0 ];
|
||||
}
|
||||
|
||||
constexpr const_reference back() const noexcept
|
||||
{
|
||||
assert( !empty() );
|
||||
return p_[ n_ - 1 ];
|
||||
}
|
||||
|
||||
constexpr const_pointer data() const noexcept
|
||||
{
|
||||
return p_;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
|
||||
constexpr void remove_prefix( size_type n ) noexcept
|
||||
{
|
||||
assert( n <= size() );
|
||||
|
||||
p_ += n;
|
||||
n_ -= n;
|
||||
}
|
||||
|
||||
constexpr void remove_suffix( size_type n ) noexcept
|
||||
{
|
||||
assert( n <= size() );
|
||||
|
||||
n_ -= n;
|
||||
}
|
||||
|
||||
constexpr void swap( basic_string_view& s ) noexcept
|
||||
{
|
||||
std::swap( p_, s.p_ );
|
||||
std::swap( n_, s.n_ );
|
||||
}
|
||||
|
||||
// string operations
|
||||
|
||||
constexpr size_type copy( Ch* s, size_type n, size_type pos = 0 ) const
|
||||
{
|
||||
if( pos > size() )
|
||||
{
|
||||
throw std::out_of_range( "basic_string_view::copy" );
|
||||
}
|
||||
|
||||
std::size_t rlen = (std::min)( n, size() - pos );
|
||||
|
||||
traits_type::copy( s, data() + pos, rlen );
|
||||
|
||||
return rlen;
|
||||
}
|
||||
|
||||
constexpr basic_string_view substr( size_type pos = 0, size_type n = npos ) const
|
||||
{
|
||||
if( pos > size() )
|
||||
{
|
||||
throw std::out_of_range( "basic_string_view::substr" );
|
||||
}
|
||||
|
||||
std::size_t rlen = (std::min)( n, size() - pos );
|
||||
|
||||
return basic_string_view( data() + pos, rlen );
|
||||
}
|
||||
|
||||
// compare
|
||||
|
||||
constexpr int compare( basic_string_view str ) const noexcept
|
||||
{
|
||||
std::size_t rlen = (std::min)( size(), str.size() );
|
||||
|
||||
int cmp = traits_type::compare( data(), str.data(), rlen );
|
||||
|
||||
if( cmp != 0 ) return cmp;
|
||||
|
||||
if( size() == str.size() ) return 0;
|
||||
|
||||
return size() < str.size()? -1: +1;
|
||||
}
|
||||
|
||||
constexpr int compare( size_type pos1, size_type n1, basic_string_view str ) const
|
||||
{
|
||||
return substr( pos1, n1 ).compare( str );
|
||||
}
|
||||
|
||||
constexpr int compare( size_type pos1, size_type n1, basic_string_view str, size_type pos2, size_type n2 ) const
|
||||
{
|
||||
return substr( pos1, n1 ).compare( str.substr( pos2, n2 ) );
|
||||
}
|
||||
|
||||
constexpr int compare( Ch const* s ) const noexcept
|
||||
{
|
||||
return compare( basic_string_view( s ) );
|
||||
}
|
||||
|
||||
constexpr int compare( size_type pos1, size_type n1, Ch const* s ) const
|
||||
{
|
||||
return substr( pos1, n1 ).compare( basic_string_view( s ) );
|
||||
}
|
||||
|
||||
constexpr int compare( size_type pos1, size_type n1, Ch const* s, size_type n2 ) const
|
||||
{
|
||||
return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
|
||||
}
|
||||
|
||||
// starts_with
|
||||
|
||||
constexpr bool starts_with( basic_string_view x ) const noexcept
|
||||
{
|
||||
return size() >= x.size() && traits_type::compare( data(), x.data(), x.size() ) == 0;
|
||||
}
|
||||
|
||||
constexpr bool starts_with( Ch x ) const noexcept
|
||||
{
|
||||
return !empty() && front() == x;
|
||||
}
|
||||
|
||||
constexpr bool starts_with( Ch const* x ) const noexcept
|
||||
{
|
||||
return starts_with( basic_string_view( x ) );
|
||||
}
|
||||
|
||||
// ends_with
|
||||
|
||||
constexpr bool ends_with( basic_string_view x ) const noexcept
|
||||
{
|
||||
return size() >= x.size() && traits_type::compare( data() + size() - x.size(), x.data(), x.size() ) == 0;
|
||||
}
|
||||
|
||||
constexpr bool ends_with( Ch x ) const noexcept
|
||||
{
|
||||
return !empty() && back() == x;
|
||||
}
|
||||
|
||||
constexpr bool ends_with( Ch const* x ) const noexcept
|
||||
{
|
||||
return ends_with( basic_string_view( x ) );
|
||||
}
|
||||
|
||||
// find
|
||||
|
||||
constexpr size_type find( basic_string_view str, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type find( Ch c, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
if( pos >= size() ) return npos;
|
||||
|
||||
Ch const* r = traits_type::find( data() + pos, size() - pos, c );
|
||||
|
||||
return r? r - data(): npos;
|
||||
}
|
||||
|
||||
constexpr size_type find( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( n == 1 ) return find( s[0], pos );
|
||||
|
||||
if( pos + n > size() ) return npos;
|
||||
if( n == 0 ) return pos;
|
||||
|
||||
Ch const* p = data() + pos;
|
||||
Ch const* last = data() + size() - n + 1;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
p = traits_type::find( p, last - p, s[0] );
|
||||
|
||||
if( p == 0 ) break;
|
||||
|
||||
if( traits_type::compare( p + 1, s + 1, n - 1 ) == 0 ) return p - data();
|
||||
|
||||
++p;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find( Ch const* s, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// rfind
|
||||
|
||||
constexpr size_type rfind( basic_string_view str, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return rfind( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type rfind( Ch c, size_type pos = npos ) const noexcept
|
||||
{
|
||||
size_type n = size();
|
||||
|
||||
if( n == 0 )
|
||||
{
|
||||
return npos;
|
||||
}
|
||||
|
||||
if( pos > n - 1 )
|
||||
{
|
||||
pos = n - 1;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if( p_[ pos ] == c ) return pos;
|
||||
--pos;
|
||||
}
|
||||
while( pos != npos );
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type rfind( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( n > size() ) return npos;
|
||||
|
||||
if( pos > size() - n )
|
||||
{
|
||||
pos = size() - n;
|
||||
}
|
||||
|
||||
if( n == 0 ) return pos;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
size_type xpos = rfind( s[0], pos );
|
||||
|
||||
if( xpos == npos ) return npos;
|
||||
|
||||
if( traits_type::compare( data() + xpos, s, n ) == 0 ) return xpos;
|
||||
|
||||
if( xpos == 0 ) return npos;
|
||||
|
||||
pos = xpos - 1;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_type rfind( Ch const* s, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return rfind( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// find_first_of
|
||||
|
||||
constexpr size_type find_first_of( basic_string_view str, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find_first_of( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type find_first_of( Ch c, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find( c, pos );
|
||||
}
|
||||
|
||||
constexpr size_type find_first_of( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( n == 0 || pos >= size() ) return npos;
|
||||
if( n == 1 ) return find( s[0], pos );
|
||||
|
||||
return detail::find_first_of( data(), size(), s, pos, n );
|
||||
}
|
||||
|
||||
constexpr size_type find_first_of( Ch const* s, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find_first_of( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// find_last_of
|
||||
|
||||
constexpr size_type find_last_of( basic_string_view str, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return find_last_of( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type find_last_of( Ch c, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return rfind( c, pos );
|
||||
}
|
||||
|
||||
constexpr size_type find_last_of( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( n == 1 )
|
||||
{
|
||||
return rfind( s[0], pos );
|
||||
}
|
||||
|
||||
size_type m = size();
|
||||
|
||||
if( m == 0 )
|
||||
{
|
||||
return npos;
|
||||
}
|
||||
|
||||
if( pos > m - 1 )
|
||||
{
|
||||
pos = m - 1;
|
||||
}
|
||||
|
||||
return detail::find_last_of( data(), s, pos, n );
|
||||
}
|
||||
|
||||
constexpr size_type find_last_of( Ch const* s, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return find_last_of( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// find_first_not_of
|
||||
|
||||
constexpr size_type find_first_not_of( basic_string_view str, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find_first_not_of( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type find_first_not_of( Ch c, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
for( std::size_t i = pos; i < n_; ++i )
|
||||
{
|
||||
if( p_[ i ] != c ) return i;
|
||||
}
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_first_not_of( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( pos >= size() ) return npos;
|
||||
if( n == 1 ) return find_first_not_of( s[0], pos );
|
||||
|
||||
return detail::find_first_not_of( data(), size(), s, pos, n );
|
||||
}
|
||||
|
||||
constexpr size_type find_first_not_of( Ch const* s, size_type pos = 0 ) const noexcept
|
||||
{
|
||||
return find_first_not_of( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// find_last_not_of
|
||||
|
||||
constexpr size_type find_last_not_of( basic_string_view str, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return find_last_not_of( str.data(), pos, str.size() );
|
||||
}
|
||||
|
||||
constexpr size_type find_last_not_of( Ch c, size_type pos = npos ) const noexcept
|
||||
{
|
||||
size_type m = size();
|
||||
|
||||
if( m == 0 )
|
||||
{
|
||||
return npos;
|
||||
}
|
||||
|
||||
if( pos > m - 1 )
|
||||
{
|
||||
pos = m - 1;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if( p_[ pos ] != c ) return pos;
|
||||
--pos;
|
||||
}
|
||||
while( pos != npos );
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
constexpr size_type find_last_not_of( Ch const* s, size_type pos, size_type n ) const noexcept
|
||||
{
|
||||
if( n == 1 )
|
||||
{
|
||||
return find_last_not_of( s[0], pos );
|
||||
}
|
||||
|
||||
size_type m = size();
|
||||
|
||||
if( m == 0 )
|
||||
{
|
||||
return npos;
|
||||
}
|
||||
|
||||
if( pos > m - 1 )
|
||||
{
|
||||
pos = m - 1;
|
||||
}
|
||||
|
||||
return detail::find_last_not_of( data(), s, pos, n );
|
||||
}
|
||||
|
||||
constexpr size_type find_last_not_of( Ch const* s, size_type pos = npos ) const noexcept
|
||||
{
|
||||
return find_last_not_of( s, pos, traits_type::length( s ) );
|
||||
}
|
||||
|
||||
// contains
|
||||
|
||||
constexpr bool contains( basic_string_view sv ) const noexcept
|
||||
{
|
||||
return find( sv ) != npos;
|
||||
}
|
||||
|
||||
constexpr bool contains( Ch c ) const noexcept
|
||||
{
|
||||
Ch const* p = data();
|
||||
size_type n = size();
|
||||
|
||||
if( n >= 16 )
|
||||
{
|
||||
return traits_type::find( p, n, c ) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_type i = 0; i < n; ++i )
|
||||
{
|
||||
if( p[ i ] == c ) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool contains( Ch const* s ) const noexcept
|
||||
{
|
||||
return find( s ) != npos;
|
||||
}
|
||||
|
||||
// relational operators
|
||||
|
||||
constexpr friend bool operator==( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return sv1.size() == sv2.size() && traits_type::compare( sv1.data(), sv2.data(), sv1.size() ) == 0;
|
||||
}
|
||||
|
||||
constexpr friend bool operator!=( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return !( sv1 == sv2 );
|
||||
}
|
||||
|
||||
constexpr friend bool operator<( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return sv1.compare( sv2 ) < 0;
|
||||
}
|
||||
|
||||
constexpr friend bool operator<=( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return sv1.compare( sv2 ) <= 0;
|
||||
}
|
||||
|
||||
constexpr friend bool operator>( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return sv1.compare( sv2 ) > 0;
|
||||
}
|
||||
|
||||
constexpr friend bool operator>=( basic_string_view sv1, basic_string_view sv2 ) noexcept
|
||||
{
|
||||
return sv1.compare( sv2 ) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
// stream inserter
|
||||
|
||||
template<class Ch> std::basic_ostream<Ch>& operator<<( std::basic_ostream<Ch>& os, basic_string_view<Ch> str )
|
||||
{
|
||||
Ch const* p = str.data();
|
||||
std::streamsize n = str.size();
|
||||
|
||||
std::streamsize m = os.width();
|
||||
|
||||
if( n >= m )
|
||||
{
|
||||
os.write( p, n );
|
||||
}
|
||||
else if( ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::left )
|
||||
{
|
||||
os.write( p, n );
|
||||
|
||||
os.width( m - n );
|
||||
os << "";
|
||||
}
|
||||
else
|
||||
{
|
||||
os.width( m - n );
|
||||
os << "";
|
||||
|
||||
os.write( p, n );
|
||||
}
|
||||
|
||||
os.width( 0 );
|
||||
return os;
|
||||
}
|
||||
|
||||
#if defined(BOOST_NO_CXX17_INLINE_VARIABLES)
|
||||
template<class Ch> constexpr_OR_CONST std::size_t basic_string_view<Ch>::npos;
|
||||
#endif
|
||||
|
||||
// typedef names
|
||||
|
||||
typedef basic_string_view<char> string_view;
|
||||
typedef basic_string_view<wchar_t> wstring_view;
|
||||
typedef basic_string_view<char16_t> u16string_view;
|
||||
typedef basic_string_view<char32_t> u32string_view;
|
||||
|
||||
#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L
|
||||
typedef basic_string_view<char8_t> u8string_view;
|
||||
#endif
|
||||
|
||||
} // namespace core
|
||||
} // namespace boost
|
||||
|
||||
namespace b2 {
|
||||
|
||||
using string_view = boost::core::string_view;
|
||||
|
||||
}
|
||||
|
||||
#endif // #ifndef BOOST_CORE_STRING_VIEW_HPP_INCLUDED
|
||||
Reference in New Issue
Block a user