Parser - Type checking
This commit is contained in:
parent
65cbebd150
commit
e21e5cefb2
3 changed files with 157 additions and 113 deletions
17
opast.hh
17
opast.hh
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "odbg.hh"
|
#include "odbg.hh"
|
||||||
#include <ebcl/SRDData.hh>
|
#include <ebcl/SRDData.hh>
|
||||||
|
#include <ebcl/Sets.hh>
|
||||||
|
|
||||||
|
|
||||||
namespace opast {
|
namespace opast {
|
||||||
|
@ -1152,11 +1153,21 @@ class T_BinaryOperatorNode : public A_ExpressionNode
|
||||||
|
|
||||||
/*= PARSER ===================================================================*/
|
/*= PARSER ===================================================================*/
|
||||||
|
|
||||||
|
// Parser output. Consists in a root node as well as other details (table of
|
||||||
|
// constants, data types, etc...)
|
||||||
|
struct T_ParserOutput
|
||||||
|
{
|
||||||
|
T_RootNode root;
|
||||||
|
T_KeyValueTable< T_String , E_DataType > types;
|
||||||
|
T_Set< float > constants{ UseTag< IndexBacked< > >( ) };
|
||||||
|
};
|
||||||
|
|
||||||
|
// The actual parser
|
||||||
class T_Parser : public A_PrivateImplementation
|
class T_Parser : public A_PrivateImplementation
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
T_Array< T_SRDError > errors_;
|
T_Array< T_SRDError > errors_;
|
||||||
T_OwnPtr< T_RootNode > rootNode_;
|
T_OwnPtr< T_ParserOutput > output_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
T_Parser( ) noexcept;
|
T_Parser( ) noexcept;
|
||||||
|
@ -1165,8 +1176,8 @@ class T_Parser : public A_PrivateImplementation
|
||||||
|
|
||||||
T_Array< T_SRDError > const& errors( ) const noexcept
|
T_Array< T_SRDError > const& errors( ) const noexcept
|
||||||
{ return errors_; }
|
{ return errors_; }
|
||||||
T_OwnPtr< T_RootNode > result( ) noexcept
|
T_OwnPtr< T_ParserOutput > result( ) noexcept
|
||||||
{ return std::move( rootNode_ ); }
|
{ return std::move( output_ ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
186
opparser.cc
186
opparser.cc
|
@ -147,7 +147,7 @@ struct T_ParserImpl_
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
T_OwnPtr< T_RootNode >& root;
|
T_OwnPtr< T_ParserOutput >& output;
|
||||||
T_Array< T_SRDError >& errors;
|
T_Array< T_SRDError >& errors;
|
||||||
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
|
T_Visitor< A_Node > visitor{ opast::ASTVisitorBrowser };
|
||||||
T_MultiArray< uint32_t > calls;
|
T_MultiArray< uint32_t > calls;
|
||||||
|
@ -161,18 +161,26 @@ struct T_ParserImpl_
|
||||||
} };
|
} };
|
||||||
|
|
||||||
T_ParserImpl_( T_Array< T_SRDError >* errors ,
|
T_ParserImpl_( T_Array< T_SRDError >* errors ,
|
||||||
T_OwnPtr< T_RootNode >* root ) noexcept;
|
T_OwnPtr< T_ParserOutput >* root ) noexcept;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
void parseTopLevel( T_SRDList const& list ) noexcept;
|
void main( T_SRDList const& list ) noexcept;
|
||||||
|
|
||||||
|
bool checkRequiredBlocks( T_SRDList const& list ) noexcept;
|
||||||
|
bool checkCalls( ) noexcept;
|
||||||
|
bool checkInstructionRestrictions( ) noexcept;
|
||||||
|
bool collectGlobalTypes( ) noexcept;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool parseTopLevel( T_SRDList const& list ) noexcept;
|
||||||
|
|
||||||
void parseFunction( T_SRDList const& funcList ) noexcept;
|
void parseFunction( T_SRDList const& funcList ) noexcept;
|
||||||
void parseFunctionArguments(
|
void parseFunctionArguments(
|
||||||
T_FuncNode& function ,
|
T_FuncNode& function ,
|
||||||
T_SRDToken const& argsToken ) noexcept;
|
T_SRDToken const& argsToken ) noexcept;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
|
||||||
|
|
||||||
void parseInstructions(
|
void parseInstructions(
|
||||||
T_InstrListNode& instructions ,
|
T_InstrListNode& instructions ,
|
||||||
T_SRDList const& input ,
|
T_SRDList const& input ,
|
||||||
|
@ -263,48 +271,47 @@ struct T_ParserImpl_
|
||||||
|
|
||||||
inline T_ParserImpl_::T_ParserImpl_(
|
inline T_ParserImpl_::T_ParserImpl_(
|
||||||
T_Array< T_SRDError >* const errors ,
|
T_Array< T_SRDError >* const errors ,
|
||||||
T_OwnPtr< T_RootNode >* const root ) noexcept
|
T_OwnPtr< T_ParserOutput >* const output ) noexcept
|
||||||
: root( *root ) , errors( *errors )
|
: output( *output ) , errors( *errors )
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
void T_ParserImpl_::main(
|
||||||
|
T_SRDList const& input ) noexcept
|
||||||
|
{
|
||||||
|
parseTopLevel( input )
|
||||||
|
&& checkRequiredBlocks( input )
|
||||||
|
&& checkCalls( )
|
||||||
|
&& checkInstructionRestrictions( )
|
||||||
|
&& collectGlobalTypes( );
|
||||||
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
void T_ParserImpl_::parseTopLevel(
|
bool T_ParserImpl_::checkRequiredBlocks(
|
||||||
T_SRDList const& input ) noexcept
|
T_SRDList const& input ) noexcept
|
||||||
{
|
{
|
||||||
for ( auto const& t : input ) {
|
|
||||||
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
|
||||||
parseFunction( t.list( ) );
|
|
||||||
} else {
|
|
||||||
errors.addNew( "function, init or frame list expected" ,
|
|
||||||
t.location( ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !errors.empty( ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T_SRDLocation missingErrLoc( ([&input]() {
|
const T_SRDLocation missingErrLoc( ([&input]() {
|
||||||
if ( input.size( ) != 0 ) {
|
if ( input.size( ) != 0 ) {
|
||||||
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
|
return T_SRDLocation( input[ 0 ].location( ).source( ) , 1 , 1 );
|
||||||
}
|
}
|
||||||
return T_SRDLocation{};
|
return T_SRDLocation{};
|
||||||
})( ));
|
})( ));
|
||||||
if ( !root->hasInit( ) ) {
|
if ( !output->root.hasInit( ) ) {
|
||||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
errors.addNew( "no initialisation block" , missingErrLoc );
|
||||||
}
|
}
|
||||||
if ( !root->hasFrame( ) ) {
|
if ( !output->root.hasFrame( ) ) {
|
||||||
errors.addNew( "no initialisation block" , missingErrLoc );
|
errors.addNew( "no frame rendering block" , missingErrLoc );
|
||||||
}
|
|
||||||
if ( !errors.empty( ) ) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return errors.empty( );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool T_ParserImpl_::checkCalls( ) noexcept
|
||||||
|
{
|
||||||
calls.clear( );
|
calls.clear( );
|
||||||
uint32_t cfi;
|
uint32_t cfi;
|
||||||
for ( cfi = 0 ; cfi < root->nFunctions( ) ; cfi ++ ) {
|
for ( cfi = 0 ; cfi < output->root.nFunctions( ) ; cfi ++ ) {
|
||||||
calls.next( );
|
calls.next( );
|
||||||
visitor.visit( root->function( cfi ) , [&]( A_Node& node , bool exit ) -> bool {
|
visitor.visit( output->root.function( cfi ) , [&]( A_Node& node , bool exit ) -> bool {
|
||||||
if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) {
|
if ( exit || dynamic_cast< A_ExpressionNode* >( &node ) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -313,14 +320,14 @@ void T_ParserImpl_::parseTopLevel(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& call( (T_CallInstrNode&) node );
|
auto& call( (T_CallInstrNode&) node );
|
||||||
const auto callee( root->functionIndex( call.id( ) ) );
|
const auto callee( output->root.functionIndex( call.id( ) ) );
|
||||||
if ( callee >= 0 ) {
|
if ( callee >= 0 ) {
|
||||||
if ( !calls.contains( cfi , callee ) ) {
|
if ( !calls.contains( cfi , callee ) ) {
|
||||||
calls.add( (uint32_t) callee );
|
calls.add( (uint32_t) callee );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check argument count while we're at it
|
// Check argument count while we're at it
|
||||||
auto& fn( (T_FuncNode&) root->function( callee ) );
|
auto& fn( (T_FuncNode&) output->root.function( callee ) );
|
||||||
if ( fn.arguments( ) != call.arguments( ) ) {
|
if ( fn.arguments( ) != call.arguments( ) ) {
|
||||||
T_StringBuilder sb;
|
T_StringBuilder sb;
|
||||||
sb << "function expects " << fn.arguments( )
|
sb << "function expects " << fn.arguments( )
|
||||||
|
@ -336,12 +343,13 @@ void T_ParserImpl_::parseTopLevel(
|
||||||
return false;
|
return false;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
if ( !errors.empty( ) ) {
|
return errors.empty( );
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
bool T_ParserImpl_::checkInstructionRestrictions( ) noexcept
|
||||||
|
{
|
||||||
T_InstrRestriction callInfo[ calls.size( ) ];
|
T_InstrRestriction callInfo[ calls.size( ) ];
|
||||||
callGraphVisitor.visit( root->functionIndex( "*init*" ) ,
|
callGraphVisitor.visit( output->root.functionIndex( "*init*" ) ,
|
||||||
[&]( uint32_t id , const bool exit ) -> bool {
|
[&]( uint32_t id , const bool exit ) -> bool {
|
||||||
if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) {
|
if ( exit || callInfo[ id ] & E_InstrRestriction::INIT ) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -349,7 +357,7 @@ void T_ParserImpl_::parseTopLevel(
|
||||||
callInfo[ id ] |= E_InstrRestriction::INIT;
|
callInfo[ id ] |= E_InstrRestriction::INIT;
|
||||||
return true;
|
return true;
|
||||||
} );
|
} );
|
||||||
callGraphVisitor.visit( root->functionIndex( "*frame*" ) ,
|
callGraphVisitor.visit( output->root.functionIndex( "*frame*" ) ,
|
||||||
[&]( uint32_t id , const bool exit ) -> bool {
|
[&]( uint32_t id , const bool exit ) -> bool {
|
||||||
if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) {
|
if ( exit || callInfo[ id ] & E_InstrRestriction::FRAME ) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -357,8 +365,8 @@ void T_ParserImpl_::parseTopLevel(
|
||||||
callInfo[ id ] |= E_InstrRestriction::FRAME;
|
callInfo[ id ] |= E_InstrRestriction::FRAME;
|
||||||
return true;
|
return true;
|
||||||
} );
|
} );
|
||||||
for ( auto i = 0u ; i < root->nFunctions( ) ; i ++ ) {
|
for ( auto i = 0u ; i < output->root.nFunctions( ) ; i ++ ) {
|
||||||
visitor.visit( root->function( i ) ,
|
visitor.visit( output->root.function( i ) ,
|
||||||
[&]( A_Node& node , bool exit ) {
|
[&]( A_Node& node , bool exit ) {
|
||||||
if ( exit ) {
|
if ( exit ) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -374,6 +382,96 @@ void T_ParserImpl_::parseTopLevel(
|
||||||
return true;
|
return true;
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
return errors.empty( );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool T_ParserImpl_::collectGlobalTypes( ) noexcept
|
||||||
|
{
|
||||||
|
struct T_Decl_ {
|
||||||
|
E_DataType type;
|
||||||
|
T_SRDLocation location;
|
||||||
|
|
||||||
|
T_Decl_( const E_DataType dt ,
|
||||||
|
T_SRDLocation const& loc ) noexcept
|
||||||
|
: type( dt ) , location( loc )
|
||||||
|
{ }
|
||||||
|
|
||||||
|
T_Decl_( const E_DataType dt ) noexcept
|
||||||
|
: type( dt ) , location{}
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
T_KeyValueTable< T_String , T_Decl_ > type;
|
||||||
|
type.add( T_String::Pooled( "time" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||||
|
type.add( T_String::Pooled( "width" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||||
|
type.add( T_String::Pooled( "height" ) , T_Decl_{ E_DataType::BUILTIN } );
|
||||||
|
|
||||||
|
visitor.visit( output->root , [&]( A_Node& node , bool exit ) -> bool {
|
||||||
|
if ( exit ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
E_DataType dt{ E_DataType::UNKNOWN };
|
||||||
|
T_String id;
|
||||||
|
T_SRDLocation location;
|
||||||
|
if ( node.type( ) == A_Node::OP_SET ) {
|
||||||
|
dt = E_DataType::VARIABLE;
|
||||||
|
id = dynamic_cast< T_SetInstrNode& >( node ).id( );
|
||||||
|
location = dynamic_cast< T_SetInstrNode& >( node ).idLocation( );
|
||||||
|
} else {
|
||||||
|
auto* np{ dynamic_cast< A_ResourceDefInstrNode* >( &node ) };
|
||||||
|
if ( !np ) {
|
||||||
|
return !dynamic_cast< A_ExpressionNode* >( &node );
|
||||||
|
}
|
||||||
|
dt = np->dataType( );
|
||||||
|
id = np->id( );
|
||||||
|
location = np->idLocation( );
|
||||||
|
}
|
||||||
|
assert( dt != E_DataType::UNKNOWN );
|
||||||
|
|
||||||
|
T_Decl_ const* const existing( type.get( id ) );
|
||||||
|
if ( !existing ) {
|
||||||
|
type.add( id , T_Decl_{ dt , location } );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( existing->type == dt ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T_StringBuilder sb;
|
||||||
|
if ( existing->type == E_DataType::BUILTIN ) {
|
||||||
|
sb << "trying to redefine built-in variable " << id;
|
||||||
|
} else {
|
||||||
|
sb << "'" << id << "' redeclared as " << dt
|
||||||
|
<< "; previous declaration as "
|
||||||
|
<< existing->type << " at "
|
||||||
|
<< existing->location;
|
||||||
|
}
|
||||||
|
errors.addNew( std::move( sb ) , location );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} );
|
||||||
|
|
||||||
|
for ( auto const& k : type.keys( ) ) {
|
||||||
|
output->types.add( k , type.get( k )->type );
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.empty( );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
bool T_ParserImpl_::parseTopLevel(
|
||||||
|
T_SRDList const& input ) noexcept
|
||||||
|
{
|
||||||
|
for ( auto const& t : input ) {
|
||||||
|
if ( t.type( ) == E_SRDTokenType::LIST && t.list( ).size( ) > 0 ) {
|
||||||
|
parseFunction( t.list( ) );
|
||||||
|
} else {
|
||||||
|
errors.addNew( "function, init or frame list expected" ,
|
||||||
|
t.location( ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.empty( );
|
||||||
}
|
}
|
||||||
|
|
||||||
void T_ParserImpl_::parseFunction(
|
void T_ParserImpl_::parseFunction(
|
||||||
|
@ -402,15 +500,15 @@ void T_ParserImpl_::parseFunction(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , *root );
|
fn = NewOwned< T_FuncNode >( funcList[ 1 ].stringValue( ) , output->root );
|
||||||
parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
|
parseFunctionArguments( dynamic_cast< T_FuncNode& >( *fn ) ,
|
||||||
funcList[ 2 ] );
|
funcList[ 2 ] );
|
||||||
} else {
|
} else {
|
||||||
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , *root );
|
fn = NewOwned< T_SpecialFuncNode >( ftw == "init" , output->root );
|
||||||
}
|
}
|
||||||
fn->location( ) = fw.location( );
|
fn->location( ) = fw.location( );
|
||||||
|
|
||||||
const auto af( root->addFunction( fn ) );
|
const auto af( output->root.addFunction( fn ) );
|
||||||
if ( af.dupLocation ) {
|
if ( af.dupLocation ) {
|
||||||
T_StringBuilder esb( "duplicate " );
|
T_StringBuilder esb( "duplicate " );
|
||||||
switch ( fn->type( ) ) {
|
switch ( fn->type( ) ) {
|
||||||
|
@ -1388,15 +1486,15 @@ P_ExpressionNode T_ParserImpl_::parseUnaryOp(
|
||||||
/*----------------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
T_Parser::T_Parser( ) noexcept
|
T_Parser::T_Parser( ) noexcept
|
||||||
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &rootNode_ ) ) ,
|
: A_PrivateImplementation( new T_ParserImpl_( &errors_ , &output_ ) ) ,
|
||||||
errors_( 64 ) , rootNode_{}
|
errors_( 64 ) , output_{}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool T_Parser::parse(
|
bool T_Parser::parse(
|
||||||
T_SRDList const& input ) noexcept
|
T_SRDList const& input ) noexcept
|
||||||
{
|
{
|
||||||
errors_.clear( );
|
errors_.clear( );
|
||||||
rootNode_ = NewOwned< T_RootNode >( );
|
output_ = NewOwned< T_ParserOutput >( );
|
||||||
p< T_ParserImpl_ >( ).parseTopLevel( input );
|
p< T_ParserImpl_ >( ).main( input );
|
||||||
return errors_.empty( );
|
return errors_.empty( );
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,75 +37,10 @@ void WriteSRDError(
|
||||||
/*============================================================================*/
|
/*============================================================================*/
|
||||||
|
|
||||||
T_Array< T_SRDError > parserPrototypes(
|
T_Array< T_SRDError > parserPrototypes(
|
||||||
T_OwnPtr< T_RootNode > const& root )
|
T_OwnPtr< T_ParserOutput > const& output )
|
||||||
{
|
{
|
||||||
T_Visitor< opast::A_Node > visitor{ ASTVisitorBrowser };
|
T_Visitor< opast::A_Node > visitor{ ASTVisitorBrowser };
|
||||||
T_Array< T_SRDError > errors;
|
T_Array< T_SRDError > errors;
|
||||||
|
|
||||||
struct T_Decl_ {
|
|
||||||
E_DataType type;
|
|
||||||
T_SRDLocation location;
|
|
||||||
|
|
||||||
T_Decl_( const E_DataType dt ,
|
|
||||||
T_SRDLocation const& loc ) noexcept
|
|
||||||
: type( dt ) , location( loc )
|
|
||||||
{ }
|
|
||||||
|
|
||||||
T_Decl_( const E_DataType dt ) noexcept
|
|
||||||
: type( dt ) , location{}
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
T_KeyValueTable< T_String , T_Decl_ > type;
|
|
||||||
type.add( T_String::Pooled( "time" ) , T_Decl_{ E_DataType::BUILTIN } );
|
|
||||||
type.add( T_String::Pooled( "width" ) , T_Decl_{ E_DataType::BUILTIN } );
|
|
||||||
type.add( T_String::Pooled( "height" ) , T_Decl_{ E_DataType::BUILTIN } );
|
|
||||||
|
|
||||||
visitor.visit( *root , [&]( A_Node& node , bool exit ) -> bool {
|
|
||||||
if ( exit ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
E_DataType dt{ E_DataType::UNKNOWN };
|
|
||||||
T_String id;
|
|
||||||
T_SRDLocation location;
|
|
||||||
if ( node.type( ) == A_Node::OP_SET ) {
|
|
||||||
dt = E_DataType::VARIABLE;
|
|
||||||
id = dynamic_cast< T_SetInstrNode& >( node ).id( );
|
|
||||||
location = dynamic_cast< T_SetInstrNode& >( node ).idLocation( );
|
|
||||||
} else {
|
|
||||||
auto* np{ dynamic_cast< A_ResourceDefInstrNode* >( &node ) };
|
|
||||||
if ( !np ) {
|
|
||||||
return !dynamic_cast< A_ExpressionNode* >( &node );
|
|
||||||
}
|
|
||||||
dt = np->dataType( );
|
|
||||||
id = np->id( );
|
|
||||||
location = np->idLocation( );
|
|
||||||
}
|
|
||||||
assert( dt != E_DataType::UNKNOWN );
|
|
||||||
|
|
||||||
T_Decl_ const* const existing( type.get( id ) );
|
|
||||||
if ( !existing ) {
|
|
||||||
type.add( id , T_Decl_{ dt , location } );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( existing->type == dt ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
T_StringBuilder sb;
|
|
||||||
if ( existing->type == E_DataType::BUILTIN ) {
|
|
||||||
sb << "trying to redefine built-in variable " << id;
|
|
||||||
} else {
|
|
||||||
sb << "'" << id << "' redeclared as " << dt
|
|
||||||
<< "; previous declaration as "
|
|
||||||
<< existing->type << " at "
|
|
||||||
<< existing->location;
|
|
||||||
}
|
|
||||||
errors.addNew( std::move( sb ) , location );
|
|
||||||
|
|
||||||
return false;
|
|
||||||
} );
|
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue