Merge branch 'master' into vec4-stack

This was a challenging merge...
Conflicts:
	elab_scope.cc
	synth2.cc
	tgt-vvp/eval_expr.c
	vvp/vthread.cc
This commit is contained in:
Stephen Williams 2014-11-13 16:26:15 -08:00
commit 2aeb3871ed
14 changed files with 273 additions and 85 deletions

View File

@ -267,7 +267,7 @@ lexor.cc: $(srcdir)/lexor.lex
lexor_keyword.o: lexor_keyword.cc parse.h
lexor_keyword.cc: $(srcdir)/lexor_keyword.gperf
gperf -o -i 7 -C -k 1-4,6,9,$$ -L ANSI-C -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false)
gperf -o -i 7 -C -k 1-4,6,9,$$ -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false)
iverilog-vpi.man: $(srcdir)/iverilog-vpi.man.in version.exe
./version.exe `head -1 $(srcdir)/iverilog-vpi.man.in`'\n' > $@

View File

@ -10,7 +10,7 @@ echo "Autoconf in root..."
autoconf -f
echo "Precompiling lexor_keyword.gperf"
gperf -o -i 7 -C -k 1-4,6,9,\$ -L ANSI-C -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc
gperf -o -i 7 -C -k 1-4,6,9,\$ -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc
echo "Precompiling vhdlpp/lexor_keyword.gperf"
(cd vhdlpp ; gperf -o -i 7 --ignore-case -C -k 1-4,6,9,\$ -L ANSI-C -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc )
(cd vhdlpp ; gperf -o -i 7 --ignore-case -C -k 1-4,6,9,\$ -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf > lexor_keyword.cc )

View File

@ -168,7 +168,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
assert(enum_type->range->size() == 1);
pform_range_t&range = enum_type->range->front();
NetExpr*msb_ex = elab_and_eval(des, scope, range.first, -1);
NetExpr*lsb_ex = elab_and_eval(des, scope, range.second, -1);
NetExpr*lsb_ex = elab_and_eval(des, scope, range.second, -1);
long msb = 0;
rc_flag = eval_as_long(msb, msb_ex);
@ -180,7 +180,8 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
netenum_t*use_enum = new netenum_t(enum_type->base_type,
enum_type->signed_flag,
enum_type->integer_flag, msb, lsb,
enum_type->names->size());
enum_type->names->size(),
enum_type);
use_enum->set_line(enum_type->li);
if (scope)
@ -188,14 +189,18 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
else
des->add_enumeration_set(enum_type, use_enum);
verinum cur_value (0);
verinum one_value (1);
size_t name_idx = 0;
// Find the enumeration width.
long raw_width = use_enum->packed_width();
assert(raw_width > 0);
unsigned enum_width = (unsigned)raw_width;
// Find the minimum and maximum allowed enumeration values.
// Define the default start value and the increment value to be the
// correct type for this enumeration.
verinum cur_value ((uint64_t)0, enum_width);
cur_value.has_sign(enum_type->signed_flag);
verinum one_value ((uint64_t)1, enum_width);
one_value.has_sign(enum_type->signed_flag);
// Find the maximum allowed enumeration value.
verinum min_value (0);
verinum max_value (0);
if (enum_type->signed_flag) {
@ -206,10 +211,10 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
}
min_value.has_sign(true);
max_value.has_sign(enum_type->signed_flag);
// Process the enumeration definition.
for (list<named_pexpr_t>::const_iterator cur = enum_type->names->begin()
; cur != enum_type->names->end() ; ++ cur, name_idx += 1) {
// Check to see if the enumeration name has a value given.
if (cur->parm) {
// There is an explicit value. elaborate/evaluate
// the value and assign it to the enumeration name.
@ -217,26 +222,115 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
NetEConst*val_const = dynamic_cast<NetEConst*> (val);
if (val_const == 0) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration expression is not "
"constant." << endl;
<< ": error: Enumeration expression for "
<< cur->name <<" is not an integer constant."
<< endl;
des->errors += 1;
continue;
}
cur_value = val_const->value();
// A 2-state value can not have a constant with X/Z bits.
if (enum_type->base_type==IVL_VT_BOOL &&
! cur_value.is_defined()) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " cannot have an undefined value." << endl;
<< " can not have an undefined value." << endl;
des->errors += 1;
continue;
}
// If this is a literal constant and it has a defined
// width then the width must match the enumeration width.
if (PENumber *tmp = dynamic_cast<PENumber*>(cur->parm)) {
if (tmp->value().has_len() &&
(tmp->value().len() != enum_width)) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " has an incorrectly sized constant."
<< endl;
des->errors += 1;
}
}
// If we are padding/truncating a negative value for an
// unsigned enumeration that is an error or if the new
// value does not have a defined width.
if (((cur_value.len() != enum_width) ||
! cur_value.has_len()) &&
! enum_type->signed_flag && cur_value.is_negative()) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " has a negative value." << endl;
des->errors += 1;
}
// Narrower values need to be padded to the width of the
// enumeration and defined to have the specified width.
if (cur_value.len() < enum_width) {
cur_value = pad_to_width(cur_value, enum_width);
}
// Some wider values can be truncated.
if (cur_value.len() > enum_width) {
unsigned check_width = enum_width - 1;
// Check that the upper bits match the MSB
for (unsigned idx = enum_width;
idx < cur_value.len();
idx += 1) {
if (cur_value[idx] != cur_value[check_width]) {
// If this is an unsigned enumeration
// then zero padding is okay.
if (! enum_type->signed_flag &&
(idx == enum_width) &&
(cur_value[idx] == verinum::V0)) {
check_width += 1;
continue;
}
if (cur_value.is_defined()) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name "
<< cur->name
<< " has a value that is too "
<< ((cur_value > max_value) ?
"large" : "small")
<< " " << cur_value << "."
<< endl;
} else {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name "
<< cur->name
<< " has trimmed bits that do "
<< "not match the enumeration "
<< "MSB: " << cur_value << "."
<< endl;
}
des->errors += 1;
break;
}
}
// If this is an unsigned value then make sure
// The upper bits are not 1.
if (! cur_value.has_sign() &&
(cur_value[enum_width] == verinum::V1)) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name "
<< cur->name
<< " has a value that is too large: "
<< cur_value << "." << endl;
des->errors += 1;
break;
}
cur_value = verinum(cur_value, enum_width);
}
// At this point the value has the correct size and needs
// to have the correct sign attribute set.
cur_value.has_len(true);
cur_value.has_sign(enum_type->signed_flag);
} else if (! cur_value.is_defined()) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " cannot have an undefined inferred value." << endl;
<< " has an undefined inferred value." << endl;
des->errors += 1;
continue;
}
@ -258,10 +352,10 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
} else if ((two_state_value > max_value) ||
(cur_value.has_sign() && (two_state_value < min_value))) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " cannot have an out of range value " << cur_value
<< "." << endl;
<< " has an inferred value that overflowed." << endl;
des->errors += 1;
}
@ -275,38 +369,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
des->errors += 1;
}
// The values are explicitly sized to the width of the
// base type of the enumeration.
verinum tmp_val (0);
if (cur_value.len() < enum_width) {
// Pad the current value if it is narrower than the final
// width of the enum.
tmp_val = pad_to_width (cur_value, enum_width);
tmp_val.has_len(true);
} else {
// Truncate an oversized value. We report out of bound
// defined values above. Undefined values need to be
// checked here. This may create duplicates.
tmp_val = verinum(cur_value, enum_width);
// For an undefined value verify that all the trimmed bits
// match the MSB of the final enumeration value.
if (! cur_value.is_defined()) for (unsigned idx = enum_width;
idx < cur_value.len();
idx += 1) {
if (cur_value[idx] != tmp_val[enum_width-1]) {
cerr << use_enum->get_fileline()
<< ": error: Enumeration name " << cur->name
<< " cannot have trimmed bits that do not "
<< "match the enumeration MSB " << cur_value
<< "." << endl;
des->errors += 1;
break;
}
}
}
tmp_val.has_sign(enum_type->signed_flag);
rc_flag = use_enum->insert_name(name_idx, cur->name, tmp_val);
rc_flag = use_enum->insert_name(name_idx, cur->name, cur_value);
if (scope)
rc_flag &= scope->add_enumeration_name(use_enum, cur->name);
else
@ -321,8 +384,9 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
// In case the next name has an implicit value,
// increment the current value by one.
if (cur_value.is_defined())
if (cur_value.is_defined()) {
cur_value = cur_value + one_value;
}
}
use_enum->insert_name_close();
@ -852,7 +916,7 @@ bool Module::elaborate_scope(Design*des, NetScope*scope,
delete[]attr;
// Generate schemes need to have their scopes elaborated, but
// we cannot do that until defparams are run, so push it off
// we can not do that until defparams are run, so push it off
// into an elaborate work item.
if (debug_scopes)
cerr << get_fileline() << ": debug: "
@ -1594,7 +1658,7 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const
continue;
}
cerr << get_fileline() << ": error: You cannot instantiate "
cerr << get_fileline() << ": error: You can not instantiate "
<< "module " << mod->mod_name() << " within itself." << endl;
cerr << get_fileline() << ": : The offending instance is "
<< get_name() << " within " << scope_path(scn) << "." << endl;

View File

@ -38,6 +38,7 @@
# include "PPackage.h"
# include "PSpec.h"
# include "netlist.h"
# include "netenum.h"
# include "netvector.h"
# include "netdarray.h"
# include "netparray.h"
@ -2668,7 +2669,8 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const
return bl;
}
if (lv->enumeration() && (lv->enumeration() != rv->enumeration())) {
if (lv->enumeration() &&
! lv->enumeration()->matches(rv->enumeration())) {
cerr << get_fileline() << ": error: "
<< "Enumeration type mismatch in assignment." << endl;
des->errors += 1;
@ -3003,10 +3005,10 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
context_width = 1;
context_unsigned = false;
} else if (context_mode > PExpr::SIZED) {
} else if (context_mode >= PExpr::LOSSLESS) {
/* Expressions may choose a different size if they are
in an unsized context, so we need to run through the
in a lossless context, so we need to run through the
process again to get the final expression width. */
context_width = test_case_width(des, scope, expr_, context_mode);
@ -3069,7 +3071,7 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
/* Iterate over all the case items (guard/statement pairs)
elaborating them. If the guard has no expression, then this
is a "default" cause. Otherwise, the guard has one or more
is a "default" case. Otherwise, the guard has one or more
expressions, and each guard is a case. */
unsigned inum = 0;
for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) {
@ -3110,6 +3112,8 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const
}
}
res->prune();
return res;
}

View File

@ -3,9 +3,11 @@
* tokenType values are not initialized for the empty table entries.
*/
%define initializer-suffix ,0,0
%language=C++
%define class-name Lkwd
%{
/* Command-line: gperf -o -i 1 -C -k 1-3,$ -L C -H keyword_hash -N check_identifier -tT lexor_keyword.gperf */
/* Command-line: gperf -o -i 7 -C -k '1-4,6,9,$' -H keyword_hash -N check_identifier -t ./lexor_keyword.gperf */
#include "config.h"
#include "parse_misc.h"
@ -362,7 +364,7 @@ int lexor_keyword_mask = 0;
int lexor_keyword_code(const char*str, unsigned nstr)
{
const struct lexor_keyword*rc = check_identifier(str, nstr);
const struct lexor_keyword*rc = Lkwd::check_identifier(str, nstr);
if (rc == 0)
return IDENTIFIER;
else if ((rc->mask & lexor_keyword_mask) == 0)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com)
* Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
@ -19,8 +19,10 @@
# include "config.h"
# include "compiler.h"
# include "netlist.h"
# include <cassert>
# include "netmisc.h"
# include "ivl_assert.h"
NetBlock::NetBlock(Type t, NetScope*ss)
: type_(t), subscope_(ss), last_(0)
@ -73,7 +75,7 @@ const NetProc* NetBlock::proc_next(const NetProc*cur) const
NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt)
: type_(c), expr_(ex), items_(cnt)
{
assert(expr_);
ivl_assert(*this, expr_);
}
NetCase::~NetCase()
@ -92,11 +94,73 @@ NetCase::TYPE NetCase::type() const
void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p)
{
assert(idx < items_.size());
ivl_assert(*this, idx < items_.size());
items_[idx].guard = e;
items_[idx].statement = p;
}
void NetCase::prune()
{
// Test whether the case expression has been padded out
NetESelect*padded_expr = dynamic_cast<NetESelect*>(expr_);
if ((padded_expr == 0) || (padded_expr->select() != 0))
return;
// If so, run through the case item expressions to find
// the minimum number of bits needed to unambiguously
// select the correct case item.
const NetExpr*unpadded_expr = padded_expr->sub_expr();
unsigned padded_width = padded_expr->expr_width();
unsigned prune_width = unpadded_expr->expr_width();
for (unsigned idx = 0; idx < items_.size(); idx += 1) {
// If there is no guard expression, this is the default
// case, so skip it.
if (items_[idx].guard == 0)
continue;
// If the guard expression is not constant, assume
// all bits are needed, so no pruning can be done.
NetEConst*gc = dynamic_cast<NetEConst*>(items_[idx].guard);
if (gc == 0)
return;
unsigned sig_bits = gc->value().significant_bits();
if (sig_bits > prune_width)
prune_width = sig_bits;
// If all the padding bits are needed, no pruning
// can be done.
if (prune_width >= padded_width)
return;
}
ivl_assert(*this, prune_width < padded_width);
if (debug_elaborate) {
cerr << get_fileline() << ": debug: pruning case expressions to "
<< prune_width << " bits." << endl;
}
// Prune the case expression
expr_ = pad_to_width(unpadded_expr->dup_expr(), prune_width, *expr_);
delete padded_expr;
// Prune the case item expressions
for (unsigned idx = 0; idx < items_.size(); idx += 1) {
if (items_[idx].guard == 0)
continue;
NetEConst*gc = dynamic_cast<NetEConst*>(items_[idx].guard);
ivl_assert(*this, gc);
verinum value(gc->value(), prune_width);
NetEConst*tmp = new NetEConst(value);
tmp->set_line(*gc);
delete gc;
items_[idx].guard = tmp;
}
}
NetDisable::NetDisable(NetScope*tgt)
: target_(tgt)
{
@ -175,7 +239,7 @@ NetPDelay::~NetPDelay()
uint64_t NetPDelay::delay() const
{
assert(expr_ == 0);
ivl_assert(*this, expr_ == 0);
return delay_;
}

View File

@ -24,9 +24,11 @@
using namespace std;
netenum_t::netenum_t(ivl_variable_type_t btype, bool signed_flag,
bool integer_flag, long msb, long lsb, size_t name_count)
: base_type_(btype), signed_flag_(signed_flag), integer_flag_(integer_flag),
msb_(msb), lsb_(lsb), names_(name_count), bits_(name_count)
bool integer_flag, long msb, long lsb, size_t name_count,
enum_type_t*enum_type)
: base_type_(btype), enum_type_(enum_type), signed_flag_(signed_flag),
integer_flag_(integer_flag), msb_(msb), lsb_(lsb),
names_(name_count), bits_(name_count)
{
}
@ -161,3 +163,8 @@ perm_string netenum_t::bits_at(size_t idx) const
{
return bits_[idx];
}
bool netenum_t::matches(const netenum_t*other) const
{
return enum_type_ == other->enum_type_;
}

View File

@ -29,12 +29,14 @@
class NetScope;
struct enum_type_t;
class netenum_t : public LineInfo, public ivl_type_s {
public:
explicit netenum_t(ivl_variable_type_t base_type, bool signed_flag,
bool isint_flag, long msb, long lsb,
size_t name_count);
size_t name_count, enum_type_t*enum_type);
~netenum_t();
virtual ivl_variable_type_t base_type() const;
@ -67,8 +69,12 @@ class netenum_t : public LineInfo, public ivl_type_s {
perm_string name_at(size_t idx) const;
perm_string bits_at(size_t idx) const;
// Check if two enumerations have the same definition.
bool matches(const netenum_t*other) const;
private:
ivl_variable_type_t base_type_;
enum_type_t*enum_type_;
bool signed_flag_;
bool integer_flag_;
long msb_, lsb_;

View File

@ -2939,6 +2939,8 @@ class NetCase : public NetProc {
void set_case(unsigned idx, NetExpr*ex, NetProc*st);
void prune();
TYPE type() const;
const NetExpr*expr() const { return expr_; }
inline unsigned nitems() const { return items_.size(); }

View File

@ -502,16 +502,33 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
}
NetEConst*ge = dynamic_cast<NetEConst*>(items_[item].guard);
if (ge == 0) {
cerr << items_[item].guard->get_fileline() << ": sorry: "
<< "variable case item expressions with a variable "
<< "case select expression are not supported in "
<< "synthesis. " << endl;
des->errors += 1;
return false;
}
ivl_assert(*this, ge);
verinum gval = ge->value();
unsigned sel_idx = gval.as_ulong();
unsigned long sel_idx = gval.as_ulong();
assert(items_[item].statement);
statement_map[sel_idx] = items_[item].statement;
if (statement_map[sel_idx]) {
cerr << ge->get_fileline() << ": warning: duplicate case "
<< "value '" << sel_idx << "' detected. This case is "
<< "unreachable." << endl;
delete items_[item].statement;
items_[item].statement = 0;
continue;
}
if (sel_idx > max_guard_value)
max_guard_value = sel_idx;
ivl_assert(*this, items_[item].statement);
statement_map[sel_idx] = items_[item].statement;
}
// The mux_size is the number of inputs that are selected.
@ -544,6 +561,14 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
esig = mux_selector_reduce_width(des, scope, *this, esig, sel_need);
}
if (!statement_default && (statement_map.size() != ((size_t)1 << sel_width))) {
cerr << get_fileline() << ": sorry: Latch inferred from "
<< "incomplete case statement. This is not supported "
<< "in synthesis." << endl;
des->errors += 1;
return false;
}
/* If there is a default clause, synthesize it once and we'll
link it in wherever it is needed. */
NetBus default_bus (scope, nex_map.size());
@ -607,12 +632,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
continue;
}
if (stmt == 0) {
cerr << get_fileline() << ": error: case " << idx
<< " is not accounted for in asynchronous mux." << endl;
des->errors += 1;
continue;
}
ivl_assert(*this, stmt);
NetBus accumulated_tmp (scope, nex_map.size());
for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1)
@ -1911,9 +1931,9 @@ void synth2_f::process(Design*des, NetProcTop*top)
}
if (! top->synth_async(des)) {
cerr << top->get_fileline() << ": internal error: "
<< "is_asynchronous does not match "
<< "sync_async results." << endl;
cerr << top->get_fileline() << ": error: "
<< "failed to synthesize asynchronous "
<< "logic for this process." << endl;
des->errors += 1;
return;
}

View File

@ -587,6 +587,21 @@ bool verinum::is_negative() const
return (bits_[nbits_-1] == V1) && has_sign();
}
unsigned verinum::significant_bits() const
{
unsigned sbits = nbits_;
if (has_sign_) {
V sign_bit = bits_[sbits-1];
while ((sbits > 1) && (bits_[sbits-2] == sign_bit))
sbits -= 1;
} else {
while ((sbits > 1) && (bits_[sbits-1] == verinum::V0))
sbits -= 1;
}
return sbits;
}
void verinum::cast_to_int2()
{
for (unsigned idx = 0 ; idx < nbits_ ; idx += 1) {
@ -765,7 +780,7 @@ ostream& operator<< (ostream&o, const verinum&v)
/* If the number is fully defined (no x or z) then print it
out as a decimal number. */
if (v.is_defined() && v.len() < 8*sizeof(long)) {
if (v.is_defined() && v.len() <= 8*sizeof(long)) {
if (v.has_sign())
o << "'sd" << v.as_long();
else

View File

@ -59,7 +59,7 @@ class verinum {
~verinum();
verinum& operator= (const verinum&);
// Number of significant bits in this number.
// Number of stored bits in this number.
unsigned len() const { return nbits_; }
// A number "has a length" if the length was specified fixed
@ -86,6 +86,9 @@ class verinum {
// Comparison for use in sorting algorithms.
bool is_before(const verinum&that) const;
// Number of significant bits in this number.
unsigned significant_bits() const;
// Convert 4-state to 2-state
void cast_to_int2();

View File

@ -124,7 +124,7 @@ parse.h: parse.cc
lexor_keyword.o: lexor_keyword.cc parse.h
lexor_keyword.cc: $(srcdir)/lexor_keyword.gperf
gperf -o -i 7 --ignore-case -C -k 1-4,6,9,$$ -L ANSI-C -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false)
gperf -o -i 7 --ignore-case -C -k 1-4,6,9,$$ -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false)
install: all installdirs $(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@

View File

@ -3,9 +3,10 @@
* tokenType values are not initialized for the empty table entries.
*/
%define initializer-suffix ,0,0
%language=C++
%define class-name Lkwd
%{
/* Command-line: gperf -o -i 1 --ignore-case -C -k 1-3,$ -L C -H keyword_hash -N check_identifier -tT lexor_keyword.gperf */
/* Command-line: gperf -o -i 7 --ignore-case -C -k '1-4,6,9,$' -H keyword_hash -N check_identifier -t lexor_keyword.gperf */
#include "vhdlpp_config.h"
#include <cstring>
@ -134,7 +135,7 @@ int lexor_keyword_mask = GN_KEYWORD_2008;
int lexor_keyword_code(const char*str, unsigned nstr)
{
const struct lexor_keyword*rc = check_identifier(str, nstr);
const struct lexor_keyword*rc = Lkwd::check_identifier(str, nstr);
if (rc == 0)
return IDENTIFIER;
else if ((rc->mask & lexor_keyword_mask) == 0)