forked from OSchip/llvm-project
[flang] Very early semantic analysis.
Original-commit: flang-compiler/f18@862eca9c5b Reviewed-on: https://github.com/flang-compiler/f18/pull/24 Tree-same-pre-rewrite: false
This commit is contained in:
parent
cb463ab27b
commit
20ac31fa9f
|
@ -13,8 +13,8 @@ namespace semantics {
|
||||||
//
|
//
|
||||||
// Also, identifiers are immutable and are never destroyed.
|
// Also, identifiers are immutable and are never destroyed.
|
||||||
//
|
//
|
||||||
// The comparison of two 'Identifier*' returns true iff their
|
// The comparison of two 'Identifier*' is expected to return
|
||||||
// name are identical.
|
// true iff their name are identical.
|
||||||
//
|
//
|
||||||
class Identifier
|
class Identifier
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ private:
|
||||||
private:
|
private:
|
||||||
std::string name_ ;
|
std::string name_ ;
|
||||||
public:
|
public:
|
||||||
const std::string & name() { return name_ ; }
|
std::string name() const { return name_ ; }
|
||||||
static const Identifier *get(std::string n) ;
|
static const Identifier *get(std::string n) ;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
SEMA_DEFINE_SCOPE(SK_SYSTEM ,"System")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_GLOBAL ,"Global")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_PROGRAM ,"Program")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_MODULE ,"Module")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_SUBMODULE ,"Submodule")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_FUNCTION ,"Function")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_SUBROUTINE ,"Subroutine")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_BLOCKDATA ,"Blockdata")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_USE_MODULE ,"Used Module")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_BLOCK ,"Block")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_DERIVED ,"Derived Type")
|
||||||
|
SEMA_DEFINE_SCOPE(SK_INTERFACE ,"Interface")
|
||||||
|
|
||||||
|
#undef SEMA_DEFINE_SCOPE
|
||||||
|
|
|
@ -4,232 +4,15 @@
|
||||||
#include "flang/Sema/Identifier.h"
|
#include "flang/Sema/Identifier.h"
|
||||||
#include "flang/Sema/Type.h"
|
#include "flang/Sema/Type.h"
|
||||||
#include "flang/Sema/Attr.h"
|
#include "flang/Sema/Attr.h"
|
||||||
|
#include "flang/Sema/Symbol.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace Fortran {
|
namespace Fortran::semantics {
|
||||||
namespace semantics {
|
|
||||||
|
|
||||||
class Scope ;
|
class Scope ;
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// Describe a symbol.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class Symbol {
|
|
||||||
public:
|
|
||||||
enum ClassId {
|
|
||||||
TemporarySymbolId,
|
|
||||||
ParameterSymbolId,
|
|
||||||
SubprogramSymbolId,
|
|
||||||
SubSymbolId,
|
|
||||||
InterfaceSymbolId,
|
|
||||||
ImportedSymbolId,
|
|
||||||
UsedSymbolId,
|
|
||||||
MemberSymbolId,
|
|
||||||
DummyArgumentSymbolId,
|
|
||||||
ExternalSymbolId,
|
|
||||||
NamelistSymbolId,
|
|
||||||
CommonSymbolId,
|
|
||||||
VariableSymbolId,
|
|
||||||
DerivedTypeSymbolId,
|
|
||||||
// ... and probably more to come
|
|
||||||
last_ClassId
|
|
||||||
} ;
|
|
||||||
private:
|
|
||||||
ClassId cid_;
|
|
||||||
Scope * owner_ ; // The scope that owns this symbol
|
|
||||||
const Identifier *name_; // The local name of that symbol
|
|
||||||
private:
|
|
||||||
TypeSpec * type_ = nullptr; // The type associated to that symbol
|
|
||||||
public:
|
|
||||||
Symbol(ClassId cid, Scope * owner, Identifier *name) :
|
|
||||||
cid_(cid), owner_(owner), name_(name) {}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// A local symbol is a temporary placeholder for symbols whose nature cannot be
|
|
||||||
// infered when the it is first encounterd. It shall eventually be replaced by
|
|
||||||
// an actual symbol.
|
|
||||||
//
|
|
||||||
// For instance:
|
|
||||||
//
|
|
||||||
// INTEGER :: x,y,z ! x, y, and z start as TemporarySymbol
|
|
||||||
// VOLATILE :: x ! x is resolved as a VariableSymbol
|
|
||||||
// PARAMETER(y=42) ! y is resolved as a ParameterSymbol
|
|
||||||
// print *, z(10) ! z is resolved as an ExternalSymbol
|
|
||||||
//
|
|
||||||
class TemporarySymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
TemporarySymbol(Scope * owner, Identifier *name) : Symbol(TemporarySymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
// If not NULL, then this is the actual symbol to be used instead of this symbol.
|
|
||||||
Symbol *actual_ ;
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol representing a parameter whose value can be queried.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class ParameterSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
ParameterSymbol(Scope * owner, Identifier *name) : Symbol(ParameterSymbolId,owner,name) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol representing an EXTERNAL function.
|
|
||||||
class ExternalSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
ExternalSymbol(Scope * owner, Identifier *name) : Symbol(ExternalSymbolId,owner,name) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// A symbol representing a variable.
|
|
||||||
//
|
|
||||||
// The variable may be local or part of a common.
|
|
||||||
//
|
|
||||||
// Question: Do we want to represent pointers using VariableSymbol or a dedicated class?
|
|
||||||
//
|
|
||||||
class VariableSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
VariableSymbol(Scope * owner, Identifier *name) : Symbol(VariableSymbolId,owner,name) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol representing a dummy argument.
|
|
||||||
class DummyArgumentSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
DummyArgumentSymbol(Scope * owner, Identifier *name) : Symbol(DummyArgumentSymbolId,owner,name) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol representing a COMMON block.
|
|
||||||
//
|
|
||||||
// TODO: Each member of common shall appear as a VariableSymbol probably with a
|
|
||||||
// boolean flag to indicate that it belongs to a COMMON. The actual
|
|
||||||
// CommonSymbol is then easy to figure out by the local scope.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class CommonSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
CommonSymbol(Scope * owner, Identifier *name) : Symbol(CommonSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
// The content of the common section.
|
|
||||||
std::vector<Identifier *> content_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// A symbol describing a subprogram declared by a FUNCTION or SUBROUTINE construct
|
|
||||||
// (either as a main unit, a internal subprogram, or an interface).
|
|
||||||
//
|
|
||||||
// The SubprogramSymbol only occurs in the local scope providing the FUNCTION
|
|
||||||
// or SUBROUTINE construct.
|
|
||||||
//
|
|
||||||
class SubprogramSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
private:
|
|
||||||
Scope * inner_scope_ ;
|
|
||||||
VariableSymbol *result ; // Shortcut to the variable that holds the result
|
|
||||||
std::vector<DummyArgumentSymbol *> args ;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Symbol describing an interface.
|
|
||||||
// A null name is allowed to represent an unnamed interface.
|
|
||||||
class InterfaceSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
InterfaceSymbol(Scope * owner, Identifier *name) : Symbol(InterfaceSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
Scope * inner_scope_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol imported from the parent or host scope of the local scope either
|
|
||||||
// automatically or via an 'import' statement.
|
|
||||||
class ImportedSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
ImportedSymbol(Scope *owner, Identifier *name) : Symbol(ImportedSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
// Provide the target symbol in the parent or host scope.
|
|
||||||
Symbol *target_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A symbol imported from a module within a submodule.
|
|
||||||
//
|
|
||||||
// FIXME:
|
|
||||||
// I am not sure how submodules scopes shall be implemented.
|
|
||||||
// On one side, a submodule inherit all the symbols from its parent
|
|
||||||
// module or submodule so that part looks a lot like regular host
|
|
||||||
// association.
|
|
||||||
//
|
|
||||||
// However, a submodule may also provide the implementation of
|
|
||||||
// a subroutine or function already declared in the parent module.
|
|
||||||
// A dedicated mecanism is probably needed here. A boolean flag
|
|
||||||
// added to SubprogramSymbol to tag all 'module function' and
|
|
||||||
// 'module subroutine' could be enough. TO BE INVESTIGATED
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class SubSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
SubSymbol(Scope * owner, Identifier *name) : Symbol(SubSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
// Provide the target symbol in the parent or host scope.
|
|
||||||
Symbol *target_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// A symbol provided by one or more USE statements.
|
|
||||||
//
|
|
||||||
// Reminder: a UsedSymbol may be ambiguous even when it has
|
|
||||||
// only one target. This is because ambiguities are always
|
|
||||||
// resolved when the symbol is actually used so a module
|
|
||||||
// may export ambiguous symbols.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
class UsedSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
UsedSymbol(Scope * owner, Identifier *name) : Symbol(UsedSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
// Provide the list of target symbols in the
|
|
||||||
std::vector<Symbol*> targets_ ;
|
|
||||||
bool ambiguous_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// A symbol representing a derived type.
|
|
||||||
//
|
|
||||||
class DerivedTypeSymbol : public Symbol{
|
|
||||||
public:
|
|
||||||
DerivedTypeSymbol(Scope *owner, Identifier *name) : Symbol(DerivedTypeSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
Scope * inner_scope_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// A symbol representing a member in a Derived type
|
|
||||||
//
|
|
||||||
// Those symbols require a dedicated type because they are in a dedicated namespace.
|
|
||||||
//
|
|
||||||
class MemberSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
MemberSymbol(Scope * owner, Identifier *name) : Symbol(MemberSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// A symbol representing a namelist-group
|
|
||||||
|
|
||||||
//
|
|
||||||
class NamelistSymbol : public Symbol {
|
|
||||||
public:
|
|
||||||
NamelistSymbol(Scope * owner, Identifier *name) : Symbol(NamelistSymbolId,owner,name) {}
|
|
||||||
private:
|
|
||||||
std::vector<Symbol*> content_ ;
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE: Support for EQUIVALENCE is not
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
// =======================================================
|
// =======================================================
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -238,7 +21,7 @@ private:
|
||||||
// For now, there is only one class. Not sure if we need more.
|
// For now, there is only one class. Not sure if we need more.
|
||||||
// Of course, there are multiple kinds of scopes in Fortran and
|
// Of course, there are multiple kinds of scopes in Fortran and
|
||||||
// they all behave slightly differently. However, a single Scope class
|
// they all behave slightly differently. However, a single Scope class
|
||||||
// that adjust its behavior according to its ScopeKind is probably
|
// that adjust its behavior according to its Kind is probably
|
||||||
// easier to implement than a dozen of classes.
|
// easier to implement than a dozen of classes.
|
||||||
//
|
//
|
||||||
// Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they
|
// Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they
|
||||||
|
@ -248,7 +31,9 @@ class Scope
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum ScopeKind {
|
enum Kind {
|
||||||
|
SK_SYSTEM, // the unique scope associated to the system (and providing intrinsic procedures)
|
||||||
|
SK_GLOBAL, // the unique global scope containing all units
|
||||||
SK_PROGRAM, // a scope associated to a PROGRAM
|
SK_PROGRAM, // a scope associated to a PROGRAM
|
||||||
SK_MODULE, // a scope associated to a MODULE
|
SK_MODULE, // a scope associated to a MODULE
|
||||||
SK_SUBMODULE, // a scope associated to a SUBMODULE
|
SK_SUBMODULE, // a scope associated to a SUBMODULE
|
||||||
|
@ -263,9 +48,32 @@ public:
|
||||||
// ... and probably more to come
|
// ... and probably more to come
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Scope(Kind k, Scope *p, Symbol *s) ;
|
||||||
|
~Scope() ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ScopeKind kind_ ;
|
Kind kind_ ;
|
||||||
|
|
||||||
|
//
|
||||||
|
// For most ranks, id contains a unique identifier that is increased
|
||||||
|
// each time a new Scope is created.
|
||||||
|
//
|
||||||
|
// The System and Global scope respectively have 0 and 1
|
||||||
|
//
|
||||||
|
// Scopes that are created within the System scope (i.e. intrinsics)
|
||||||
|
// are assigned a negative id.
|
||||||
|
//
|
||||||
|
// Scopes that are created within the Global scope (i.e. modules or
|
||||||
|
// user code) are assigned an id larger than 1
|
||||||
|
//
|
||||||
|
// The unique System and Global scopes do not use that field to store
|
||||||
|
// their respectived id (which are hardcoded). Instead they use it
|
||||||
|
// to count the number of scopes already created.
|
||||||
|
//
|
||||||
|
int id_or_counter_ ;
|
||||||
|
|
||||||
// For scopes associated to a name (so most of them except BLOCK
|
// For scopes associated to a name (so most of them except BLOCK
|
||||||
// and unnamed INTERFACE) provide the associated symbol in the
|
// and unnamed INTERFACE) provide the associated symbol in the
|
||||||
|
@ -368,15 +176,77 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// The system scope is the scope that provides intrinsic subprograms
|
Kind getKind() const { return kind_; }
|
||||||
|
|
||||||
Scope * getSystemScope() ;
|
const Scope * getParentScope() const { return parent_scope_; }
|
||||||
|
Scope * getParentScope() { return parent_scope_; }
|
||||||
|
|
||||||
Symbol *lookup(const Identifier *name) ;
|
//
|
||||||
|
// The system scope is the scope that provides all intrinsics definitions
|
||||||
|
//
|
||||||
|
const Scope * getSystemScope() const;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The Global scope is the scope that is immediately below the System scope.
|
||||||
|
//
|
||||||
|
// It contains the symbols for the Program unit (PROGRAM, MODULE, FUNCTION,
|
||||||
|
// SUBROUTINE and BLOCKDATA)
|
||||||
|
//
|
||||||
|
// However, that scope is not standard in the sense that it requires a
|
||||||
|
// dedicated lookup to find its symbols.
|
||||||
|
// This is because the program units do not see each others by default
|
||||||
|
//
|
||||||
|
// This is also the scope that owns all USE_MODULE.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
Scope * getGlobalScope() ;
|
||||||
|
const Scope * getGlobalScope() const;
|
||||||
|
|
||||||
|
// Look for a symbol locally and in the host scope (if any)
|
||||||
|
Symbol *Lookup(const Identifier *name);
|
||||||
|
const Symbol *Lookup(const Identifier *name) const;
|
||||||
|
|
||||||
|
|
||||||
|
// Look for a symbol locally (do not explore the host scope).
|
||||||
|
//
|
||||||
|
Symbol *LookupLocal(const Identifier *name);
|
||||||
|
const Symbol *LookupLocal(const Identifier *name) const;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lookup for a Program Unit by name
|
||||||
|
//
|
||||||
|
Symbol *LookupProgramUnit(const Identifier *name) ;
|
||||||
|
const Symbol *LookupProgramUnit(const Identifier *name) const;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lookup for a known module.
|
||||||
|
//
|
||||||
|
// The result is either null or a scope of kind SK_USE_MODULE.
|
||||||
|
//
|
||||||
|
Symbol *LookupModule(const Identifier *name);
|
||||||
|
const Symbol *LookupModule(const Identifier *name) const;
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
int getId() const ;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add a symbol to the scope.
|
||||||
|
//
|
||||||
|
void add(Symbol *s) ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
std::string toString(void) ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// a temporary method to fail
|
||||||
|
void fail(const std::string &msg) const ;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // of namespace semantics
|
} // of namespace Fortran::semantics
|
||||||
} // of namespace Fortran
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
SEMA_DEFINE_SYMBOL(BlockDataSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(CommonSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ConstructSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(DerivedTypeSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(DummyArgumentSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ExternalSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(FunctionSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ImportedSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(InterfaceSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(MemberSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ModuleSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(NamelistSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ParameterSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(ProgramSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(SubSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(SubroutineSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(TemporarySymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(UsedSymbol)
|
||||||
|
SEMA_DEFINE_SYMBOL(VariableSymbol)
|
||||||
|
#undef SEMA_DEFINE_SYMBOL
|
|
@ -0,0 +1,291 @@
|
||||||
|
#ifndef FLANG_SEMA_SYMBOL_H
|
||||||
|
#define FLANG_SEMA_SYMBOL_H
|
||||||
|
|
||||||
|
#include "flang/Sema/Identifier.h"
|
||||||
|
#include "flang/Sema/Type.h"
|
||||||
|
#include "flang/Sema/Attr.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
|
class Scope ;
|
||||||
|
|
||||||
|
// Forward declaration of all Symbol classes
|
||||||
|
#define SEMA_DEFINE_SYMBOL(Classname) class Classname;
|
||||||
|
#include "flang/Sema/Symbol.def"
|
||||||
|
#undef SEMA_DEFINE_SYMBOL
|
||||||
|
|
||||||
|
|
||||||
|
// Describe a symbol.
|
||||||
|
class Symbol {
|
||||||
|
public:
|
||||||
|
enum ClassId {
|
||||||
|
#define SEMA_DEFINE_SYMBOL(Classname) Classname##Id,
|
||||||
|
#include "flang/Sema/Symbol.def"
|
||||||
|
#undef SEMA_DEFINE_SYMBOL
|
||||||
|
// ... and probably more to come
|
||||||
|
last_ClassId
|
||||||
|
} ;
|
||||||
|
private:
|
||||||
|
ClassId cid_;
|
||||||
|
Scope * owner_ ; // The scope that owns this symbol
|
||||||
|
const Identifier *name_; // The local name of that symbol
|
||||||
|
private:
|
||||||
|
TypeSpec * type_ = nullptr; // The type associated to that symbol
|
||||||
|
public:
|
||||||
|
|
||||||
|
Symbol(ClassId cid, Scope * owner, const Identifier *name) ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// For now, provide some toXXXX() member to perform the dynamic cast.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SEMA_DEFINE_SYMBOL(Classname) \
|
||||||
|
Classname *to##Classname(void) { \
|
||||||
|
return (cid_==Classname##Id) ? (Classname*)this : nullptr ; \
|
||||||
|
} \
|
||||||
|
const Classname *to##Classname(void) const { \
|
||||||
|
return (cid_==Classname##Id) ? (const Classname*)this : nullptr ; \
|
||||||
|
}
|
||||||
|
#include "flang/Sema/Symbol.def"
|
||||||
|
#undef SEMA_DEFINE_SYMBOL
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Scope *owner() { return owner_; }
|
||||||
|
|
||||||
|
const Identifier * name() { return name_ ; }
|
||||||
|
|
||||||
|
bool Match(const Identifier *ident) { return ident == name_ ; }
|
||||||
|
|
||||||
|
std::string toString() const { return name_->name(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A local symbol is a temporary placeholder for symbols whose nature cannot be
|
||||||
|
// infered when the it is first encounterd. It shall eventually be replaced by
|
||||||
|
// an actual symbol.
|
||||||
|
//
|
||||||
|
// For instance:
|
||||||
|
//
|
||||||
|
// INTEGER :: x,y,z ! x, y, and z start as TemporarySymbol
|
||||||
|
// VOLATILE :: x ! x is resolved as a VariableSymbol
|
||||||
|
// PARAMETER(y=42) ! y is resolved as a ParameterSymbol
|
||||||
|
// print *, z(10) ! z is resolved as an ExternalSymbol
|
||||||
|
//
|
||||||
|
class TemporarySymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
TemporarySymbol(Scope * owner, const Identifier *name) : Symbol(TemporarySymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
// If not NULL, then this is the actual symbol to be used instead of this symbol.
|
||||||
|
Symbol *actual_ ;
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing the name of a Module
|
||||||
|
//
|
||||||
|
class ModuleSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ModuleSymbol(Scope * owner, const Identifier *name) : Symbol(ProgramSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing the name of a Program
|
||||||
|
//
|
||||||
|
class ProgramSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ProgramSymbol(Scope * owner, const Identifier *name) : Symbol(ProgramSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing the name of a Program
|
||||||
|
//
|
||||||
|
class BlockDataSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
BlockDataSymbol(Scope * owner, const Identifier *name) : Symbol(BlockDataSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A symbol representing a parameter whose value can be queried.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class ParameterSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ParameterSymbol(Scope * owner, const Identifier *name) : Symbol(ParameterSymbolId,owner,name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbol representing an EXTERNAL function.
|
||||||
|
class ExternalSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ExternalSymbol(Scope * owner, const Identifier *name) : Symbol(ExternalSymbolId,owner,name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing a variable.
|
||||||
|
//
|
||||||
|
// The variable may be local or part of a common.
|
||||||
|
//
|
||||||
|
// Question: Do we want to represent pointers using VariableSymbol or a dedicated class?
|
||||||
|
//
|
||||||
|
class VariableSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
VariableSymbol(Scope * owner, const Identifier *name) : Symbol(VariableSymbolId,owner,name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbol representing a dummy argument.
|
||||||
|
class DummyArgumentSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
DummyArgumentSymbol(Scope * owner, const Identifier *name) : Symbol(DummyArgumentSymbolId,owner,name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbol representing a COMMON block.
|
||||||
|
//
|
||||||
|
// TODO: Each member of common shall appear as a VariableSymbol probably with a
|
||||||
|
// boolean flag to indicate that it belongs to a COMMON. The actual
|
||||||
|
// CommonSymbol is then easy to figure out by the local scope.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class CommonSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
CommonSymbol(Scope * owner, const Identifier *name) : Symbol(CommonSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
// The content of the common section.
|
||||||
|
std::vector<const Identifier *> content_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol describing a subprogram declared by a FUNCTION or SUBROUTINE construct
|
||||||
|
// (either as a main unit, a internal subprogram, or an interface).
|
||||||
|
//
|
||||||
|
// The SubprogramSymbol only occurs in the local scope providing the FUNCTION
|
||||||
|
// or SUBROUTINE construct.
|
||||||
|
//
|
||||||
|
class SubroutineSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
SubroutineSymbol(Scope * owner, const Identifier *name) : Symbol(SubroutineSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
Scope * inner_scope_ ;
|
||||||
|
std::vector<DummyArgumentSymbol *> args ;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
FunctionSymbol(Scope * owner, const Identifier *name) : Symbol(FunctionSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
Scope * inner_scope_ ;
|
||||||
|
VariableSymbol *result ; // Shortcut to the variable that holds the result
|
||||||
|
std::vector<DummyArgumentSymbol *> args ;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Symbol describing an interface.
|
||||||
|
// A null name is allowed to represent an unnamed interface.
|
||||||
|
class InterfaceSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
InterfaceSymbol(Scope * owner, const Identifier *name) : Symbol(InterfaceSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
Scope * inner_scope_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbol imported from the parent or host scope of the local scope either
|
||||||
|
// automatically or via an 'import' statement.
|
||||||
|
class ImportedSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ImportedSymbol(Scope *owner, const Identifier *name) : Symbol(ImportedSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
// Provide the target symbol in the parent or host scope.
|
||||||
|
Symbol *target_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A symbol imported from a module within a submodule.
|
||||||
|
//
|
||||||
|
// FIXME:
|
||||||
|
// I am not sure how submodules scopes shall be implemented.
|
||||||
|
// On one side, a submodule inherit all the symbols from its parent
|
||||||
|
// module or submodule so that part looks a lot like regular host
|
||||||
|
// association.
|
||||||
|
//
|
||||||
|
// However, a submodule may also provide the implementation of
|
||||||
|
// a subroutine or function already declared in the parent module.
|
||||||
|
// A dedicated mecanism is probably needed here. A boolean flag
|
||||||
|
// added to SubprogramSymbol to tag all 'module function' and
|
||||||
|
// 'module subroutine' could be enough. TO BE INVESTIGATED
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class SubSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
SubSymbol(Scope * owner, const Identifier *name) : Symbol(SubSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
// Provide the target symbol in the parent or host scope.
|
||||||
|
Symbol *target_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A symbol provided by one or more USE statements.
|
||||||
|
//
|
||||||
|
// Reminder: a UsedSymbol may be ambiguous even when it has
|
||||||
|
// only one target. This is because ambiguities are always
|
||||||
|
// resolved when the symbol is actually used so a module
|
||||||
|
// may export ambiguous symbols.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class UsedSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
UsedSymbol(Scope * owner, const Identifier *name) : Symbol(UsedSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
// Provide the list of target symbols in the
|
||||||
|
std::vector<Symbol*> targets_ ;
|
||||||
|
bool ambiguous_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing a derived type.
|
||||||
|
//
|
||||||
|
class DerivedTypeSymbol : public Symbol{
|
||||||
|
public:
|
||||||
|
DerivedTypeSymbol(Scope *owner, const Identifier *name) : Symbol(DerivedTypeSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
Scope * inner_scope_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing a member in a Derived type
|
||||||
|
//
|
||||||
|
class MemberSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
MemberSymbol(Scope * owner, const Identifier *name) : Symbol(MemberSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing a namelist-group
|
||||||
|
//
|
||||||
|
class NamelistSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
NamelistSymbol(Scope * owner, const Identifier *name) : Symbol(NamelistSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
std::vector<Symbol*> content_ ;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// A symbol representing the name of a construct
|
||||||
|
//
|
||||||
|
class ConstructSymbol : public Symbol {
|
||||||
|
public:
|
||||||
|
ConstructSymbol(Scope * owner, const Identifier *name) : Symbol(ConstructSymbolId,owner,name) {}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
} // of namespace
|
||||||
|
|
||||||
|
// NOTE: Support for EQUIVALENCE ...
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
add_library( FlangSemantics
|
add_library( FlangSemantics
|
||||||
Identifier.cc
|
Identifier.cc
|
||||||
Scope.cc
|
Scope.cc
|
||||||
|
Symbol.cc
|
||||||
Type.cc # ../semantics/type.cc
|
Type.cc # ../semantics/type.cc
|
||||||
Attr.cc # ../semantics/attr.cc
|
Attr.cc # ../semantics/attr.cc
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,11 +3,289 @@
|
||||||
#include "flang/Sema/Scope.h"
|
#include "flang/Sema/Scope.h"
|
||||||
#include "flang/Sema/Identifier.h"
|
#include "flang/Sema/Identifier.h"
|
||||||
|
|
||||||
using namespace Fortran;
|
#include <cassert>
|
||||||
using namespace semantics;
|
|
||||||
|
|
||||||
Symbol *
|
|
||||||
Scope::lookup(const Identifier *name)
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
|
Scope::Scope(Kind k, Scope *p, Symbol *s) :
|
||||||
|
kind_(k),
|
||||||
|
id_or_counter_(0),
|
||||||
|
self_(s),
|
||||||
|
parent_scope_(p),
|
||||||
|
host_scope_(p)
|
||||||
{
|
{
|
||||||
|
switch(k) {
|
||||||
|
|
||||||
|
case SK_SYSTEM:
|
||||||
|
assert( parent_scope_ == NULL) ;
|
||||||
|
id_or_counter_ = -1 ;
|
||||||
|
break ;
|
||||||
|
|
||||||
|
case SK_GLOBAL:
|
||||||
|
assert( parent_scope_->kind_ == SK_SYSTEM) ;
|
||||||
|
id_or_counter_ = 2 ;
|
||||||
|
break ;
|
||||||
|
|
||||||
|
case SK_PROGRAM:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL) ;
|
||||||
|
if( self_ ) {
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_MODULE:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_SUBMODULE:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_FUNCTION:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL ||
|
||||||
|
parent_scope_->kind_ == SK_PROGRAM ||
|
||||||
|
parent_scope_->kind_ == SK_MODULE ||
|
||||||
|
parent_scope_->kind_ == SK_FUNCTION ||
|
||||||
|
parent_scope_->kind_ == SK_SUBROUTINE ||
|
||||||
|
parent_scope_->kind_ == SK_INTERFACE ||
|
||||||
|
parent_scope_->kind_ == SK_USE_MODULE
|
||||||
|
) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_SUBROUTINE:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL ||
|
||||||
|
parent_scope_->kind_ == SK_PROGRAM ||
|
||||||
|
parent_scope_->kind_ == SK_MODULE ||
|
||||||
|
parent_scope_->kind_ == SK_FUNCTION ||
|
||||||
|
parent_scope_->kind_ == SK_SUBROUTINE ||
|
||||||
|
parent_scope_->kind_ == SK_INTERFACE ||
|
||||||
|
parent_scope_->kind_ == SK_USE_MODULE
|
||||||
|
) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_BLOCKDATA:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_USE_MODULE:
|
||||||
|
assert( parent_scope_->kind_ == SK_GLOBAL) ;
|
||||||
|
assert( self_ ) ;
|
||||||
|
assert( self_->owner() == parent_scope_ ) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_BLOCK:
|
||||||
|
assert( parent_scope_->kind_ == SK_PROGRAM ||
|
||||||
|
parent_scope_->kind_ == SK_BLOCK ||
|
||||||
|
parent_scope_->kind_ == SK_FUNCTION ||
|
||||||
|
parent_scope_->kind_ == SK_SUBROUTINE
|
||||||
|
) ;
|
||||||
|
if ( self_ ) {
|
||||||
|
assert( self_->owner() == parent_scope_ ) ;
|
||||||
|
assert( self_->toConstructSymbol() ) ;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_DERIVED:
|
||||||
|
assert( parent_scope_->kind_ == SK_PROGRAM ||
|
||||||
|
parent_scope_->kind_ == SK_MODULE ||
|
||||||
|
parent_scope_->kind_ == SK_FUNCTION ||
|
||||||
|
parent_scope_->kind_ == SK_SUBROUTINE ||
|
||||||
|
parent_scope_->kind_ == SK_INTERFACE ||
|
||||||
|
parent_scope_->kind_ == SK_USE_MODULE
|
||||||
|
) ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SK_INTERFACE:
|
||||||
|
assert( parent_scope_->kind_ == SK_PROGRAM ||
|
||||||
|
parent_scope_->kind_ == SK_MODULE ||
|
||||||
|
parent_scope_->kind_ == SK_FUNCTION ||
|
||||||
|
parent_scope_->kind_ == SK_SUBROUTINE ||
|
||||||
|
parent_scope_->kind_ == SK_USE_MODULE
|
||||||
|
) ;
|
||||||
|
|
||||||
|
// Within an interface, the symbols must be explicitly imported
|
||||||
|
// from the parent scope so there is no default host scope.
|
||||||
|
host_scope_ = NULL ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fail("unknown scope kind") ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
} // of switch
|
||||||
|
|
||||||
|
// Figure out the ID
|
||||||
|
if (id_or_counter_ == 0) {
|
||||||
|
if ( Scope *global_scope = getGlobalScope() ) {
|
||||||
|
// Within the global scope, the IDs are growing
|
||||||
|
id_or_counter_ = global_scope->id_or_counter_++ ;
|
||||||
|
} else {
|
||||||
|
// Within the system scope, the IDs are shrinking (in negative)
|
||||||
|
Scope *system_scope = const_cast<Scope *>( getSystemScope() ) ;
|
||||||
|
id_or_counter_ = system_scope->id_or_counter_-- ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scope::~Scope() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Scope::fail(const std::string &msg) const
|
||||||
|
{
|
||||||
|
std::cerr << "FATAL Scope: " << msg << "\n" ;
|
||||||
|
exit(1) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Scope *
|
||||||
|
Scope::getSystemScope() const
|
||||||
|
{
|
||||||
|
for ( const Scope * scope = this ; scope ; scope = scope->parent_scope_) {
|
||||||
|
if ( scope->kind_ == SK_SYSTEM )
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
fail("System scope not found") ;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Scope *
|
||||||
|
Scope::getGlobalScope()
|
||||||
|
{
|
||||||
|
for ( auto scope = this ; scope ; scope = scope->parent_scope_) {
|
||||||
|
if ( scope->kind_ == SK_GLOBAL )
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Scope *
|
||||||
|
Scope::getGlobalScope() const
|
||||||
|
{
|
||||||
|
return const_cast<Scope*>(this)->getGlobalScope() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol *
|
||||||
|
Scope::Lookup(const Identifier *name)
|
||||||
|
{
|
||||||
|
if ( kind_ == SK_GLOBAL )
|
||||||
|
return parent_scope_->Lookup(name) ;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return nullptr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol *
|
||||||
|
Scope::LookupLocal(const Identifier *name)
|
||||||
|
{
|
||||||
|
if ( kind_ == SK_GLOBAL )
|
||||||
|
return nullptr ;
|
||||||
|
|
||||||
|
auto & entries{this->entries_} ;
|
||||||
|
for (auto it = entries.rbegin() ; it != entries.rend() ; ++it) {
|
||||||
|
Symbol *s = *it ;
|
||||||
|
if ( s->Match(name) )
|
||||||
|
return s ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Symbol *
|
||||||
|
Scope::LookupProgramUnit(const Identifier *name)
|
||||||
|
{
|
||||||
|
Scope * scope = this->getGlobalScope() ;
|
||||||
|
auto & entries{scope->entries_} ;
|
||||||
|
|
||||||
|
for (auto it = entries.rbegin() ; it != entries.rend() ; ++it) {
|
||||||
|
Symbol *s = *it ;
|
||||||
|
std::cerr << "found " << s << " " << s->name() << "\n" ;
|
||||||
|
if ( s->toModuleSymbol() ||
|
||||||
|
s->toProgramSymbol() ||
|
||||||
|
s->toSubroutineSymbol() ||
|
||||||
|
s->toFunctionSymbol() ||
|
||||||
|
s->toBlockDataSymbol()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( s->Match(name) )
|
||||||
|
return s ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Symbol *
|
||||||
|
Scope::LookupProgramUnit(const Identifier *name) const
|
||||||
|
{
|
||||||
|
return const_cast<Scope*>(this)->LookupProgramUnit(name) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol *
|
||||||
|
Scope::LookupModule(const Identifier *name)
|
||||||
|
{
|
||||||
|
return nullptr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Symbol *
|
||||||
|
Scope::LookupModule(const Identifier *name) const
|
||||||
|
{
|
||||||
|
return const_cast<Scope*>(this)->LookupModule(name) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Scope::getId(void) const
|
||||||
|
{
|
||||||
|
if (this->kind_ == SK_SYSTEM)
|
||||||
|
return 0;
|
||||||
|
else if (this->kind_ == SK_GLOBAL)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return id_or_counter_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Scope::add(Symbol *s)
|
||||||
|
{
|
||||||
|
assert(s) ;
|
||||||
|
assert(s->owner()==this) ;
|
||||||
|
entries_.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string Scope::toString(void)
|
||||||
|
{
|
||||||
|
const char * info;
|
||||||
|
switch(kind_) {
|
||||||
|
#define SEMA_DEFINE_SCOPE(KIND,INFO) case KIND: info=INFO ; break;
|
||||||
|
#include "flang/Sema/Scope.def"
|
||||||
|
#undef SEMA_DEFINE_SCOPE
|
||||||
|
default:
|
||||||
|
info = "???????" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( self_ ) {
|
||||||
|
return std::string("#") + std::to_string(getId()) + " " + info + " (" + self_->toString() + ")" ;
|
||||||
|
} else {
|
||||||
|
return std::string("#") + std::to_string(getId()) + " " + info + " (***UNNAMED***)" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // of namespace
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
|
||||||
|
#include "flang/Sema/Symbol.h"
|
||||||
|
#include "flang/Sema/Scope.h"
|
||||||
|
#include "flang/Sema/Identifier.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace Fortran::semantics {
|
||||||
|
|
||||||
|
|
||||||
|
Symbol::Symbol(ClassId cid, Scope * owner, const Identifier *name) :
|
||||||
|
cid_(cid),
|
||||||
|
owner_(owner),
|
||||||
|
name_(name)
|
||||||
|
{
|
||||||
|
owner->add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // of namespace
|
|
@ -52,9 +52,14 @@ CLASS_TRAIT(TupleTrait);
|
||||||
// later thus allowing the parser to be build without an dependency
|
// later thus allowing the parser to be build without an dependency
|
||||||
// with the Sema library
|
// with the Sema library
|
||||||
//
|
//
|
||||||
|
|
||||||
|
namespace Fortran {
|
||||||
|
namespace semantics {
|
||||||
template <typename T> struct Semantic {
|
template <typename T> struct Semantic {
|
||||||
Semantic(T*) {}
|
Semantic(T*) {}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Most non-template classes in this file use these default definitions
|
// Most non-template classes in this file use these default definitions
|
||||||
// for their move constructor and move assignment operator=, and disable
|
// for their move constructor and move assignment operator=, and disable
|
||||||
|
@ -64,7 +69,7 @@ template <typename T> struct Semantic {
|
||||||
classname &operator=(classname &&) = default; \
|
classname &operator=(classname &&) = default; \
|
||||||
classname(const classname &) = delete; \
|
classname(const classname &) = delete; \
|
||||||
classname &operator=(const classname &) = delete; \
|
classname &operator=(const classname &) = delete; \
|
||||||
Semantic<classname> * s = nullptr \
|
Fortran::semantics::Semantic<classname> * s = nullptr \
|
||||||
|
|
||||||
// Almost all classes in this file have no default constructor.
|
// Almost all classes in this file have no default constructor.
|
||||||
#define BOILERPLATE(classname) \
|
#define BOILERPLATE(classname) \
|
||||||
|
@ -81,7 +86,7 @@ template <typename T> struct Semantic {
|
||||||
classname &operator=(const classname &) { return *this; }; \
|
classname &operator=(const classname &) { return *this; }; \
|
||||||
classname &operator=(classname &&) { return *this; }; \
|
classname &operator=(classname &&) { return *this; }; \
|
||||||
using EmptyTrait = std::true_type; \
|
using EmptyTrait = std::true_type; \
|
||||||
Semantic<classname> * s = nullptr ; \
|
Fortran::semantics::Semantic<classname> * s = nullptr ; \
|
||||||
}
|
}
|
||||||
|
|
||||||
// Many classes below simply wrap a std::variant<> discriminated union,
|
// Many classes below simply wrap a std::variant<> discriminated union,
|
||||||
|
@ -2748,6 +2753,7 @@ struct MainProgram {
|
||||||
ExecutionPart, std::optional<InternalSubprogramPart>,
|
ExecutionPart, std::optional<InternalSubprogramPart>,
|
||||||
Statement<EndProgramStmt>>
|
Statement<EndProgramStmt>>
|
||||||
t;
|
t;
|
||||||
|
enum { PROG, SPEC, EXEC, INTERNAL, END } ;
|
||||||
};
|
};
|
||||||
|
|
||||||
// R1405 module-stmt -> MODULE module-name
|
// R1405 module-stmt -> MODULE module-name
|
||||||
|
@ -3072,6 +3078,7 @@ struct FunctionSubprogram {
|
||||||
std::tuple<Statement<FunctionStmt>, SpecificationPart, ExecutionPart,
|
std::tuple<Statement<FunctionStmt>, SpecificationPart, ExecutionPart,
|
||||||
std::optional<InternalSubprogramPart>, Statement<EndFunctionStmt>>
|
std::optional<InternalSubprogramPart>, Statement<EndFunctionStmt>>
|
||||||
t;
|
t;
|
||||||
|
enum { FUNC, SPEC, EXEC, INTERNAL, END } ;
|
||||||
};
|
};
|
||||||
|
|
||||||
// R1534 subroutine-subprogram ->
|
// R1534 subroutine-subprogram ->
|
||||||
|
|
|
@ -24,7 +24,6 @@ target_link_libraries( test-type
|
||||||
add_executable( test-sema
|
add_executable( test-sema
|
||||||
test-sema.cc
|
test-sema.cc
|
||||||
sema-impl.cc
|
sema-impl.cc
|
||||||
foobar.cc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries( test-sema
|
target_link_libraries( test-sema
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
|
||||||
|
// ============ Forward declarations ==============
|
||||||
|
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const T &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( T &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const T &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( T &x) ;
|
||||||
|
|
||||||
|
#ifndef IGNORE_Scalar
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Scalar<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Scalar<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Scalar<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Scalar<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Constant
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Constant<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Constant<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Constant<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Constant<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Integer
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Integer<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Integer<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Integer<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Integer<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Logical
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Logical<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Logical<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Logical<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Logical<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_DefaultChar
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::DefaultChar<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::DefaultChar<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::DefaultChar<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::DefaultChar<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Indirection
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Indirection<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Indirection<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Indirection<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Indirection<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Statement
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Statement<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Statement<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Statement<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Statement<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_optional
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const std::optional<T> &x) ;
|
||||||
|
template <typename T> inline auto & GET_VALUE( std::optional<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( const std::optional<T> &x) ;
|
||||||
|
template <typename T> inline bool HAS_VALUE( std::optional<T> &x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// =========== Actual implementation of GET_VALUE() and HAS_VALUE() ==============================
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const T &x) { return x ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( T &x) { return x ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const T &x) { return true; }
|
||||||
|
template <typename T> inline bool HAS_VALUE( T &x) { return true; }
|
||||||
|
|
||||||
|
#ifndef IGNORE_Scalar
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Scalar<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Scalar<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Scalar<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Scalar<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Constant
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Constant<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Constant<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Constant<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Constant<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Integer
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Integer<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Integer<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Integer<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Integer<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Logical
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Logical<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Logical<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Logical<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Logical<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_DefaultChar
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::DefaultChar<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::DefaultChar<T> &x) { return GET_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::DefaultChar<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::DefaultChar<T> &x) { return HAS_VALUE(x.thing) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Indirection
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Indirection<T> &x) { return GET_VALUE(*x) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_Statement
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const psr::Statement<T> &x) { return GET_VALUE(x.statement) ;}
|
||||||
|
template <typename T> inline auto & GET_VALUE( psr::Statement<T> &x) { return GET_VALUE(x.statement) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( const psr::Statement<T> &x) { return HAS_VALUE(x.statement) ;}
|
||||||
|
template <typename T> inline bool HAS_VALUE( psr::Statement<T> &x) { return HAS_VALUE(x.statement) ;}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IGNORE_optional
|
||||||
|
template <typename T> inline const auto & GET_VALUE( const std::optional<T> &x) { return GET_VALUE(x.value()) ; }
|
||||||
|
template <typename T> inline auto & GET_VALUE( std::optional<T> &x) { return GET_VALUE(x.value()) ; }
|
||||||
|
template <typename T> inline bool HAS_VALUE( const std::optional<T> &x) { return x.has_value() && HAS_VALUE(*x); }
|
||||||
|
template <typename T> inline bool HAS_VALUE( std::optional<T> &x) { return x.has_value() && HAS_VALUE(*x); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T> inline auto GET_OPT_VALUE(const T &x) {
|
||||||
|
if ( HAS_VALUE(x) ) {
|
||||||
|
return & GET_VALUE(x) ;
|
||||||
|
} else {
|
||||||
|
return decltype(&GET_VALUE(x)){0} ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> inline auto GET_OPT_VALUE(T &x) {
|
||||||
|
if ( HAS_VALUE(x) ) {
|
||||||
|
return & GET_VALUE(x) ;
|
||||||
|
} else {
|
||||||
|
return decltype(&GET_VALUE(x)){0} ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef GET_VALUE
|
||||||
|
#undef HAS_VALUE
|
||||||
|
#undef GET_OPT_VALUE
|
|
@ -0,0 +1,119 @@
|
||||||
|
#ifndef FLANG_SUPPORT_GET_VALUE_H
|
||||||
|
|
||||||
|
#undef IGNORE_optional
|
||||||
|
#undef IGNORE_Statement
|
||||||
|
#undef IGNORE_Scalar
|
||||||
|
#undef IGNORE_Constant
|
||||||
|
#undef IGNORE_Indirection
|
||||||
|
#undef IGNORE_Logical
|
||||||
|
#undef IGNORE_DefaultChar
|
||||||
|
|
||||||
|
// Each include of "GetValue.def" provides a set of helper functions
|
||||||
|
// whose names are specified by the macros GET_VALUE, HAS_VALUE and
|
||||||
|
// GET_OPT_VALUE.
|
||||||
|
//
|
||||||
|
// The purpose of those function is to provide easier access to the
|
||||||
|
// parse-tree by ignoring some wrapper classes/
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// GET_VALUE(x) provides a reference to the value that x is holding.
|
||||||
|
//
|
||||||
|
// The following wrapper classes are ignored unless the corresponding
|
||||||
|
// IGNORES_xxx macro is defined.
|
||||||
|
//
|
||||||
|
// Scalar<T>
|
||||||
|
// Constant<T>
|
||||||
|
// Integer<T>
|
||||||
|
// Logical<T>
|
||||||
|
// DefaultChar<T>
|
||||||
|
// Indirection<T>
|
||||||
|
// Statement<T>
|
||||||
|
// std::optional<T>
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// HAS_VALUE(x) return true if it is legal to call GET_VALUE(x) in case x
|
||||||
|
// contains some std::optional<T>
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// Constant<std::optional<Indirection<std::optional<int>>>> &x = ... ;
|
||||||
|
// if ( HasValue(x) ) {
|
||||||
|
// const int &v = getValue(x) ;
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// GET_OPT_VALUE(T &x) is equivalent to
|
||||||
|
//
|
||||||
|
// HAS_VALUE(x) ? &GET_VALUE(x) : (Type*) nullptr
|
||||||
|
//
|
||||||
|
// here Type is the type of GET_VALUE(x)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// const Scalar<optional<Integer<Expr>>> & z = ...
|
||||||
|
// const Expr *ptr_z = GET_OPT_VALUE(z) ;
|
||||||
|
// if ( ptr_z ) {
|
||||||
|
// ... do something with *ptr_z %
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
// This is the default version that handles all wrapper
|
||||||
|
|
||||||
|
#define GET_VALUE GetValue
|
||||||
|
#define HAS_VALUE HasValue
|
||||||
|
#define GET_OPT_VALUE GetOptValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
|
||||||
|
// HAS_VALUE and GET_OPT_VALUE are only interesting when
|
||||||
|
// std::optional is not ignored.
|
||||||
|
// We need to give a name to the function but they are pretty much useless
|
||||||
|
#define IGNORE_optional
|
||||||
|
#define GET_VALUE GetOptionalValue
|
||||||
|
#define HAS_VALUE HasOptionalValue__
|
||||||
|
#define GET_OPT_VALUE GetOptValue__
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_optional
|
||||||
|
|
||||||
|
#define IGNORE_Statement
|
||||||
|
#define GET_VALUE GetStatementValue
|
||||||
|
#define HAS_VALUE HasStatementValue
|
||||||
|
#define GET_OPT_VALUE GetOptStatementValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_Statement
|
||||||
|
|
||||||
|
#define IGNORE_Scalar
|
||||||
|
#define GET_VALUE GetScalarValue
|
||||||
|
#define HAS_VALUE HasScalarValue
|
||||||
|
#define GET_OPT_VALUE GetOptScalarValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_Scalar
|
||||||
|
|
||||||
|
#define IGNORE_Constant
|
||||||
|
#define GET_VALUE GetConstantValue
|
||||||
|
#define HAS_VALUE HasConstantValue
|
||||||
|
#define GET_OPT_VALUE GetOptConstantValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_Constant
|
||||||
|
|
||||||
|
#define IGNORE_Indirection
|
||||||
|
#define GET_VALUE GetIndirectionValue
|
||||||
|
#define HAS_VALUE HasIndirectionValue
|
||||||
|
#define GET_OPT_VALUE GetOptIndirectionValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_Indirection
|
||||||
|
|
||||||
|
|
||||||
|
#define IGNORE_Logical
|
||||||
|
#define GET_VALUE GetLogicalValue
|
||||||
|
#define HAS_VALUE HasLogicalValue
|
||||||
|
#define GET_OPT_VALUE GetOptLogicalValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_Logical
|
||||||
|
|
||||||
|
#define IGNORE_DefaultChar
|
||||||
|
#define GET_VALUE GetDefaultCharValue
|
||||||
|
#define HAS_VALUE HasDefaultCharValue
|
||||||
|
#define GET_OPT_VALUE GetOptDefaultCharValue
|
||||||
|
#include "GetValue.def"
|
||||||
|
#undef IGNORE_DefaultChar
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,762 @@
|
||||||
|
|
||||||
|
#include "../../lib/parser/format-specification.h"
|
||||||
|
#include "../../lib/parser/idioms.h"
|
||||||
|
#include "../../lib/parser/indirection.h"
|
||||||
|
#include "../../lib/parser/parse-tree-visitor.h"
|
||||||
|
#include "../../lib/parser/parse-tree.h"
|
||||||
|
|
||||||
|
#include "flang/Sema/Scope.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace psr = Fortran::parser ;
|
||||||
|
namespace sema = Fortran::semantics ;
|
||||||
|
|
||||||
|
#include "GetValue.h"
|
||||||
|
|
||||||
|
#define TODO do { std::cerr << "NOT YET IMPLEMENTED " << __FILE__ << ":" << __LINE__ << "\n" ; exit(1) ; } while(0)
|
||||||
|
#define CONSUME(x) do { (void)x ; } while(0)
|
||||||
|
#define TRACE_CALL() do { std::cerr << "*** call " << __PRETTY_FUNCTION__ << "\n" ; } while(0)
|
||||||
|
#define TRACE(msg) do { std::cerr << msg << "\n" ; } while(0)
|
||||||
|
#define FAIL(msg) do { std::cerr << "FATAL " << __FILE__ << ":" << __LINE__ << ":\n " << msg << "\n" ; exit(1) ; } while(0)
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize the pointer used to attach semantic information to each parser-tree node
|
||||||
|
//
|
||||||
|
// Ideally, the function should be called once at the begining of the corresponding Pre()
|
||||||
|
// member in Pass1. However, in case a forward reference to the Semantic<> data would be
|
||||||
|
// required, no error will occur when setting strict=false.
|
||||||
|
//
|
||||||
|
template <typename T> Fortran::semantics::Semantic<T> & initSema(const T &node, bool strict=true) {
|
||||||
|
if (node.s) {
|
||||||
|
if (strict)
|
||||||
|
FAIL( "Duplicate call of " << __PRETTY_FUNCTION__ ) ;
|
||||||
|
else
|
||||||
|
return *(node.s);
|
||||||
|
}
|
||||||
|
auto s = new Fortran::semantics::Semantic<T>( const_cast<T*>(&node) ) ;
|
||||||
|
const_cast<T&>(node).s = s;
|
||||||
|
return *s ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retreive the semantic information attached to a parser-tree node
|
||||||
|
template <typename T> Fortran::semantics::Semantic<T> & getSema(const T &node) {
|
||||||
|
assert(node.s) ;
|
||||||
|
return *(node.s) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
#include "flang/Sema/Scope.h"
|
||||||
|
#include <type_traits>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fortran::semantics
|
||||||
|
{
|
||||||
|
|
||||||
|
using namespace Fortran::parser ;
|
||||||
|
|
||||||
|
template <typename ParserClass>
|
||||||
|
Semantic<ParserClass> &
|
||||||
|
sema(ParserClass *node)
|
||||||
|
{
|
||||||
|
if ( node->s == NULL ) {
|
||||||
|
node->s = new Semantic<ParserClass>(node) ;
|
||||||
|
}
|
||||||
|
return *(node->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P, typename T> P * vget( T & x ) {
|
||||||
|
return std::get_if<P>(x.u) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename P, typename T> P * vget_i( T & x ) {
|
||||||
|
if ( auto v = std::get_if<Indirection<P>>(&x.u) ) {
|
||||||
|
return &(**v) ;
|
||||||
|
} else {
|
||||||
|
return nullptr ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LabelTable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
// TODO: what to put here
|
||||||
|
Provenance loc;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<int,Entry> entries_ ;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void add( int label , Provenance loc )
|
||||||
|
{
|
||||||
|
if (label<1 || label>99999) return ; // Hoops!
|
||||||
|
auto &entry = entries_[label] ;
|
||||||
|
entry.loc = loc ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool find(int label, Provenance &loc)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto it = entries_.find(label);
|
||||||
|
if( it != entries_.end()) {
|
||||||
|
Entry & entry{it->second};
|
||||||
|
loc = entry.loc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump()
|
||||||
|
{
|
||||||
|
TRACE( "==== Label Table ====");
|
||||||
|
for ( int i=1 ; i<=99999 ;i++) {
|
||||||
|
Provenance p;
|
||||||
|
if ( find(i,p) ) {
|
||||||
|
TRACE( " #" << i << " at " << p.offset() ) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TRACE( "=====================");
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // of class LabelTable
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
class LabelTableStack {
|
||||||
|
private:
|
||||||
|
std::stack<LabelTable*> stack ;
|
||||||
|
public:
|
||||||
|
LabelTable *PushLabelTable( LabelTable *table )
|
||||||
|
{
|
||||||
|
assert(table!=NULL);
|
||||||
|
stack.push(table);
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopLabelTable( LabelTable *table )
|
||||||
|
{
|
||||||
|
assert( !stack.empty() ) ;
|
||||||
|
assert( stack.top() == table ) ;
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
LabelTable & GetLabelTable() {
|
||||||
|
assert( !stack.empty() ) ;
|
||||||
|
return *stack.top() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NoLabelTable() {
|
||||||
|
return stack.empty() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // of class LabelTableStack
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Declare here the members of the Semantic<> information that will
|
||||||
|
// be attached to each parse-tree class. The default is an empty struct.
|
||||||
|
//
|
||||||
|
// Here are a few common fields
|
||||||
|
//
|
||||||
|
// Scope *scope_provider=0 ; // For each node providing a new scope
|
||||||
|
// int stmt_label=0 ; // For each node that consumes a label
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
#define DEFINE_SEMANTIC(Class) template <> struct Semantic<Class> { Semantic<Class>(Class *node) {}
|
||||||
|
#define END_SEMANTIC }
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(ProgramUnit)
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(MainProgram)
|
||||||
|
Scope *scope_provider=0 ;
|
||||||
|
LabelTable *label_table=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(FunctionSubprogram)
|
||||||
|
Scope *scope_provider=0 ;
|
||||||
|
LabelTable *label_table=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(FunctionStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(EndFunctionStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(TypeDeclarationStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(AssignmentStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(PrintStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(ProgramStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(EndProgramStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
DEFINE_SEMANTIC(ImplicitStmt)
|
||||||
|
int stmt_label=0 ;
|
||||||
|
END_SEMANTIC;
|
||||||
|
|
||||||
|
|
||||||
|
} // of namespace Fortran::semantics
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
using sema::Scope ;
|
||||||
|
using sema::LabelTable ;
|
||||||
|
using sema::LabelTableStack ;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Fortran::parser {
|
||||||
|
|
||||||
|
|
||||||
|
class Pass1 : public LabelTableStack {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Pass1() : current_label(-1)
|
||||||
|
{
|
||||||
|
system_scope = new Scope(Scope::SK_SYSTEM, nullptr, nullptr ) ;
|
||||||
|
unit_scope = new Scope(Scope::SK_GLOBAL, system_scope, nullptr) ;
|
||||||
|
current_scope = nullptr ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
int current_label; // hold the value of a statement label until it get consumed (-1 means none)
|
||||||
|
Provenance current_label_loc;
|
||||||
|
|
||||||
|
Scope * system_scope ;
|
||||||
|
Scope * unit_scope ;
|
||||||
|
Scope * current_scope ;
|
||||||
|
|
||||||
|
|
||||||
|
Scope *EnterScope(Scope *s) {
|
||||||
|
assert(s) ;
|
||||||
|
assert(s->getParentScope() == current_scope ) ;
|
||||||
|
current_scope = s ;
|
||||||
|
TRACE("Entering Scope " << s->toString() );
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeaveScope(Scope::Kind k) {
|
||||||
|
assert( current_scope->getKind() == k ) ;
|
||||||
|
TRACE("Leaving Scope " << current_scope->toString() );
|
||||||
|
current_scope = current_scope->getParentScope() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Trace the location and label of any x with an accessible Statement<> in its type.
|
||||||
|
template <typename T> void TraceStatementInfo(const T &x) {
|
||||||
|
auto & s = GetStatementValue(x) ;
|
||||||
|
// TODO: compilation will fail is 's' is not of type Statement<...>.
|
||||||
|
// Do we have a type trait to detect Statement<>?
|
||||||
|
// if constexpr ( s is a Statement<> ) {
|
||||||
|
if ( s.label ) {
|
||||||
|
TRACE("stmt: loc=" << s.provenance.offset() << " label=" << s.label ) ;
|
||||||
|
} else {
|
||||||
|
TRACE("stmt: loc=" << s.provenance.offset() ) ;
|
||||||
|
}
|
||||||
|
// } else { TRACE("stmt: none") ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void
|
||||||
|
CheckStatementName( const sema::Identifier *expect , const sema::Identifier *found , std::string ctxt, bool required )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( expect ) {
|
||||||
|
if ( found && found != expect ) {
|
||||||
|
FAIL("Unexpected " << ctxt << " name '" << found->name() << "' (expected '" << expect->name() << "') ");
|
||||||
|
}
|
||||||
|
} else if ( found ) {
|
||||||
|
FAIL("Unexpected " << ctxt << " name '" << found->name() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Consume a label produced by a previous Statement.
|
||||||
|
// That function should be called exactly once between each pair of Statement<>.
|
||||||
|
//
|
||||||
|
// For now, the sole purpose of the 'stmt' is to provide a relevant type
|
||||||
|
// for __PRETTY_FUNCTION__ but the association <label,stmt> will eventually be stored
|
||||||
|
// somewhere.
|
||||||
|
//
|
||||||
|
// I still haven't figured out how to do that efficiently.
|
||||||
|
//
|
||||||
|
// There is obviously the problem of the type that could be solved using a huge
|
||||||
|
// std::variant but there is also the problem that node addresses are still subject
|
||||||
|
// to change where the tree requires a rewrite.
|
||||||
|
//
|
||||||
|
// Another way could be to store the label in the Semantic field of stmt.
|
||||||
|
// That is relatively easy to do but that does not really solve the
|
||||||
|
// problem of matching a label with its target.
|
||||||
|
//
|
||||||
|
template<typename T>
|
||||||
|
int ConsumeLabel(const T &stmt)
|
||||||
|
{
|
||||||
|
if ( current_label == -1 ) {
|
||||||
|
FAIL("No label to consume in " << __PRETTY_FUNCTION__ );
|
||||||
|
} else {
|
||||||
|
int label = current_label ;
|
||||||
|
current_label = -1 ;
|
||||||
|
|
||||||
|
auto &sema = getSema(stmt);
|
||||||
|
sema.stmt_label = label;
|
||||||
|
|
||||||
|
if ( label != 0 ) {
|
||||||
|
LabelTable & table = GetLabelTable() ;
|
||||||
|
Provenance old_loc ;
|
||||||
|
if ( table.find(label, old_loc) ) {
|
||||||
|
FAIL("Duplicate label " << label
|
||||||
|
<< "at @" << current_label_loc.offset()
|
||||||
|
<< "and @" << old_loc.offset() ) ;
|
||||||
|
} else {
|
||||||
|
table.add( label, current_label_loc) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return label ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename T> bool Pre(const T &x) {
|
||||||
|
TRACE( "*** fallback " << __PRETTY_FUNCTION__ ) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void Post(const T &) {
|
||||||
|
TRACE( "*** fallback " << __PRETTY_FUNCTION__ ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for std::variant
|
||||||
|
|
||||||
|
|
||||||
|
template <typename... A> bool Pre(const std::variant<A...> &) {
|
||||||
|
//std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... A> void Post(const std::variant<A...> &) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for std::tuple
|
||||||
|
|
||||||
|
template <typename... A> bool Pre(const std::tuple<A...> &) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... A> void Post(const std::tuple<A...> &) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for std::string
|
||||||
|
|
||||||
|
bool Pre(const std::string &x) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const std::string &) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for Indirection<>
|
||||||
|
|
||||||
|
template <typename T> bool Pre(const psr::Indirection<T> &x) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void Post(const psr::Indirection<T> &) {
|
||||||
|
// std::cerr << "@@@ fallback " << __PRETTY_FUNCTION__ << "\n" ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ========== Statement<> ===========
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Pre(const psr::Statement<T> &x) {
|
||||||
|
if ( current_label != -1 ) {
|
||||||
|
TRACE("*** Label " << current_label << " (" << current_label_loc.offset() << ") was not consumed in " << __PRETTY_FUNCTION__ );
|
||||||
|
}
|
||||||
|
current_label = 0 ;
|
||||||
|
current_label_loc = x.provenance ;
|
||||||
|
if ( x.label.has_value() ) {
|
||||||
|
//
|
||||||
|
// TODO: The parser stores the label in a std::uint64_t but does not report overflow
|
||||||
|
// which means that the following labels are currently accepted as valid:
|
||||||
|
// 18446744073709551617 = 2^64+1 = 1
|
||||||
|
// 18446744073709551618 = 2^64+2 = 2
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
if ( 1 <= x.label.value() && x.label.value() <= 99999 ) {
|
||||||
|
current_label = x.label.value() ;
|
||||||
|
} else {
|
||||||
|
FAIL( "##### Illegal label value " << x.label.value() << " at @" << x.provenance.offset() ) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Post(const psr::Statement<T> &x) {
|
||||||
|
if ( current_label!=-1 ) {
|
||||||
|
TRACE("*** Label " << current_label << " (" << current_label_loc.offset() << ") was not consumed in " << __PRETTY_FUNCTION__ );
|
||||||
|
current_label=-1 ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== ProgramUnit ===========
|
||||||
|
|
||||||
|
bool Pre(const ProgramUnit &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
current_scope = unit_scope;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const ProgramUnit &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== MainProgram ===========
|
||||||
|
|
||||||
|
bool Pre(const MainProgram &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
|
||||||
|
|
||||||
|
sema::ProgramSymbol * symbol{0};
|
||||||
|
const ProgramStmt * program_stmt = GetOptValue( std::get<x.PROG>(x.t) ) ;
|
||||||
|
const EndProgramStmt & end_stmt = GetValue( std::get<x.END>(x.t) ) ;
|
||||||
|
|
||||||
|
const sema::Identifier * program_ident{0};
|
||||||
|
|
||||||
|
if ( program_stmt ) {
|
||||||
|
const std::string & name = program_stmt->v ;
|
||||||
|
TRACE("program name = " << name ) ;
|
||||||
|
program_ident = sema::Identifier::get(name) ;
|
||||||
|
symbol = new sema::ProgramSymbol( current_scope, program_ident ) ;
|
||||||
|
TraceStatementInfo( std::get<x.PROG>(x.t) ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Should we create a symbol when there is no PROGRAM statement?
|
||||||
|
|
||||||
|
// Install the scope
|
||||||
|
sema.scope_provider = EnterScope( new Scope(Scope::SK_PROGRAM, current_scope, symbol) ) ;
|
||||||
|
|
||||||
|
// Install the label table
|
||||||
|
sema.label_table = PushLabelTable( new LabelTable ) ;
|
||||||
|
|
||||||
|
// Check the name consistancy
|
||||||
|
const std::string * end_name = GetOptValue(end_stmt.v) ;
|
||||||
|
const sema::Identifier * end_ident = end_name ? sema::Identifier::get(*end_name) : nullptr ;
|
||||||
|
|
||||||
|
CheckStatementName(program_ident,end_ident,"program",false) ;
|
||||||
|
|
||||||
|
// if ( program_ident ) {
|
||||||
|
// if ( end_ident && program_ident != end_ident ) {
|
||||||
|
// FAIL("Unexpected end program name '" << end_ident->name() << "' (expected '" << program_ident->name() << "') ");
|
||||||
|
// }
|
||||||
|
// } else if ( program_ident ) {
|
||||||
|
// FAIL("Unexpected end program name '" << end_ident->name() << "'");
|
||||||
|
// }
|
||||||
|
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const MainProgram &x) {
|
||||||
|
auto &sema = getSema(x);
|
||||||
|
GetLabelTable().dump() ;
|
||||||
|
PopLabelTable(sema.label_table) ;
|
||||||
|
LeaveScope(Scope::SK_PROGRAM) ;
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== FunctionSubprogram ===========
|
||||||
|
|
||||||
|
bool Pre(const FunctionSubprogram &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
|
||||||
|
const FunctionStmt & function_stmt = GetValue(std::get<x.FUNC>(x.t)) ;
|
||||||
|
const EndFunctionStmt & end_stmt = GetValue(std::get<x.END>(x.t)) ;
|
||||||
|
|
||||||
|
const std::string &function_name = std::get<1>(function_stmt.t) ;
|
||||||
|
const sema::Identifier *function_ident = sema::Identifier::get(function_name) ;
|
||||||
|
|
||||||
|
// TODO: lookup for name conflict
|
||||||
|
sema::Symbol *lookup ;
|
||||||
|
if ( current_scope->getKind() == Scope::SK_GLOBAL ) {
|
||||||
|
lookup = current_scope->LookupProgramUnit(function_ident) ;
|
||||||
|
if (lookup) FAIL("A unit '" << function_ident->name() << "' is already declared") ;
|
||||||
|
} else {
|
||||||
|
lookup = current_scope->LookupLocal(function_ident) ;
|
||||||
|
// TODO: There are a few cases, a function redeclaration is not necessarily a problem.
|
||||||
|
// A typical example is a PRIVATE or PUBLIC statement in a module
|
||||||
|
if (lookup) FAIL("A unit '" << function_ident->name() << "' is already declared") ;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto symbol = new sema::FunctionSymbol( current_scope, function_ident ) ;
|
||||||
|
sema.scope_provider = EnterScope( new Scope(Scope::SK_FUNCTION, current_scope, symbol) ) ;
|
||||||
|
|
||||||
|
// Install the label table
|
||||||
|
sema.label_table = PushLabelTable( new LabelTable ) ;
|
||||||
|
|
||||||
|
TraceStatementInfo( std::get<x.FUNC>(x.t) ) ;
|
||||||
|
|
||||||
|
// Check the end function name
|
||||||
|
const std::string * end_name = GetOptValue(end_stmt.v) ;
|
||||||
|
const sema::Identifier * end_ident = end_name ? sema::Identifier::get(*end_name) : nullptr ;
|
||||||
|
|
||||||
|
CheckStatementName(function_ident,end_ident,"function",false) ;
|
||||||
|
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const FunctionSubprogram &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = getSema(x);
|
||||||
|
GetLabelTable().dump() ;
|
||||||
|
PopLabelTable(sema.label_table) ;
|
||||||
|
LeaveScope(Scope::SK_FUNCTION) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== SubroutineSubprogram ===========
|
||||||
|
|
||||||
|
bool Pre(const SubroutineSubprogram &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const SubroutineSubprogram &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== Module ===========
|
||||||
|
|
||||||
|
bool Pre(const Module &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const Module &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== BlockData ===========
|
||||||
|
|
||||||
|
bool Pre(const BlockData &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const BlockData &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========== FunctionStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const FunctionStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const FunctionStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== EndFunctionStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const EndFunctionStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const EndFunctionStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== TypeDeclarationStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const TypeDeclarationStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const TypeDeclarationStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== ImplicitStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const ImplicitStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const ImplicitStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== PrintStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const PrintStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const PrintStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== AssignmentStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const AssignmentStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const AssignmentStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== AssignmentStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const ProgramStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const ProgramStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========== AssignmentStmt ===========
|
||||||
|
|
||||||
|
bool Pre(const EndProgramStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
(void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const EndProgramStmt &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// Do not remove: This is a skeleton for new node types
|
||||||
|
// =========== XXX ===========
|
||||||
|
|
||||||
|
bool Pre(const XXX &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
auto &sema = initSema(x);
|
||||||
|
(void) sema ;
|
||||||
|
// (void) ConsumeLabel(x) ;
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Post(const XXX &x) {
|
||||||
|
TRACE_CALL() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void run(const ProgramUnit &p) {
|
||||||
|
assert( NoLabelTable() ) ;
|
||||||
|
current_scope = unit_scope;
|
||||||
|
Walk(p,*this) ;
|
||||||
|
assert( current_scope == unit_scope ) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
} ;
|
||||||
|
|
||||||
|
} // of namespace Fortran::parser
|
||||||
|
|
||||||
|
|
||||||
|
void DoSemanticAnalysis( const psr::Program &all)
|
||||||
|
{
|
||||||
|
psr::Pass1 pass1 ;
|
||||||
|
for (const psr::ProgramUnit &unit : all.v) {
|
||||||
|
TRACE("===========================================================================================================");
|
||||||
|
pass1.run(unit) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "../../lib/parser/grammar.h"
|
||||||
|
#include "../../lib/parser/idioms.h"
|
||||||
|
#include "../../lib/parser/indirection.h"
|
||||||
|
#include "../../lib/parser/message.h"
|
||||||
|
#include "../../lib/parser/parse-state.h"
|
||||||
|
#include "../../lib/parser/parse-tree.h"
|
||||||
|
#include "../../lib/parser/preprocessor.h"
|
||||||
|
#include "../../lib/parser/prescan.h"
|
||||||
|
#include "../../lib/parser/provenance.h"
|
||||||
|
#include "../../lib/parser/source.h"
|
||||||
|
#include "../../lib/parser/user-state.h"
|
||||||
|
#include "../../lib/semantics/attr.h"
|
||||||
|
#include "../../lib/semantics/type.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
using namespace Fortran;
|
||||||
|
using namespace parser;
|
||||||
|
|
||||||
|
extern void DoSemanticAnalysis(const Program &);
|
||||||
|
|
||||||
|
//static void visitProgramUnit(const ProgramUnit &unit);
|
||||||
|
|
||||||
|
int main(int argc, char *const argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Expected 1 source file, got " << (argc - 1) << "\n";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string path{argv[1]};
|
||||||
|
AllSources allSources;
|
||||||
|
std::stringstream error;
|
||||||
|
const auto *sourceFile = allSources.Open(path, &error);
|
||||||
|
if (!sourceFile) {
|
||||||
|
std::cerr << error.str() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvenanceRange range{allSources.AddIncludedFile(
|
||||||
|
*sourceFile, ProvenanceRange{})};
|
||||||
|
Messages messages{allSources};
|
||||||
|
CookedSource cooked{&allSources};
|
||||||
|
Preprocessor preprocessor{&allSources};
|
||||||
|
bool prescanOk{Prescanner{&messages, &cooked, &preprocessor}.Prescan(range)};
|
||||||
|
messages.Emit(std::cerr);
|
||||||
|
if (!prescanOk) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
cooked.Marshal();
|
||||||
|
ParseState state{cooked};
|
||||||
|
UserState ustate;
|
||||||
|
std::optional<Program> result{program.Parse(&state)};
|
||||||
|
if (!result.has_value() || state.anyErrorRecovery()) {
|
||||||
|
std::cerr << "parse FAILED\n";
|
||||||
|
state.messages()->Emit(std::cerr);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
DoSemanticAnalysis(*result) ;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue