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.
|
||||
//
|
||||
// The comparison of two 'Identifier*' returns true iff their
|
||||
// name are identical.
|
||||
// The comparison of two 'Identifier*' is expected to return
|
||||
// true iff their name are identical.
|
||||
//
|
||||
class Identifier
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ private:
|
|||
private:
|
||||
std::string name_ ;
|
||||
public:
|
||||
const std::string & name() { return name_ ; }
|
||||
std::string name() const { return name_ ; }
|
||||
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/Type.h"
|
||||
#include "flang/Sema/Attr.h"
|
||||
#include "flang/Sema/Symbol.h"
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
namespace Fortran::semantics {
|
||||
|
||||
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.
|
||||
// Of course, there are multiple kinds of scopes in Fortran and
|
||||
// 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.
|
||||
//
|
||||
// Remark: ImportedSymbol and UsedSymbol may be created on the fly the first time they
|
||||
|
@ -248,7 +31,9 @@ class Scope
|
|||
{
|
||||
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_MODULE, // a scope associated to a MODULE
|
||||
SK_SUBMODULE, // a scope associated to a SUBMODULE
|
||||
|
@ -262,10 +47,33 @@ public:
|
|||
|
||||
// ... and probably more to come
|
||||
} ;
|
||||
|
||||
private:
|
||||
|
||||
ScopeKind kind_ ;
|
||||
public:
|
||||
|
||||
Scope(Kind k, Scope *p, Symbol *s) ;
|
||||
~Scope() ;
|
||||
|
||||
private:
|
||||
|
||||
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
|
||||
// and unnamed INTERFACE) provide the associated symbol in the
|
||||
|
@ -366,17 +174,79 @@ private:
|
|||
//
|
||||
std::vector<Symbol*> owned_ ;
|
||||
|
||||
public:
|
||||
|
||||
// The system scope is the scope that provides intrinsic subprograms
|
||||
|
||||
Scope * getSystemScope() ;
|
||||
public:
|
||||
|
||||
Symbol *lookup(const Identifier *name) ;
|
||||
Kind getKind() const { return kind_; }
|
||||
|
||||
const Scope * getParentScope() const { return parent_scope_; }
|
||||
Scope * getParentScope() { return parent_scope_; }
|
||||
|
||||
//
|
||||
// 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
|
||||
} // of namespace Fortran::semantics
|
||||
|
||||
#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
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
add_library( FlangSemantics
|
||||
Identifier.cc
|
||||
Scope.cc
|
||||
Identifier.cc
|
||||
Scope.cc
|
||||
Symbol.cc
|
||||
Type.cc # ../semantics/type.cc
|
||||
Attr.cc # ../semantics/attr.cc
|
||||
)
|
||||
|
|
|
@ -3,11 +3,289 @@
|
|||
#include "flang/Sema/Scope.h"
|
||||
#include "flang/Sema/Identifier.h"
|
||||
|
||||
using namespace Fortran;
|
||||
using namespace semantics;
|
||||
#include <cassert>
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
// with the Sema library
|
||||
//
|
||||
template <typename T> struct Semantic {
|
||||
|
||||
namespace Fortran {
|
||||
namespace semantics {
|
||||
template <typename T> struct Semantic {
|
||||
Semantic(T*) {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Most non-template classes in this file use these default definitions
|
||||
// for their move constructor and move assignment operator=, and disable
|
||||
|
@ -64,7 +69,7 @@ template <typename T> struct Semantic {
|
|||
classname &operator=(classname &&) = default; \
|
||||
classname(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.
|
||||
#define BOILERPLATE(classname) \
|
||||
|
@ -81,7 +86,7 @@ template <typename T> struct Semantic {
|
|||
classname &operator=(const classname &) { return *this; }; \
|
||||
classname &operator=(classname &&) { return *this; }; \
|
||||
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,
|
||||
|
@ -2748,6 +2753,7 @@ struct MainProgram {
|
|||
ExecutionPart, std::optional<InternalSubprogramPart>,
|
||||
Statement<EndProgramStmt>>
|
||||
t;
|
||||
enum { PROG, SPEC, EXEC, INTERNAL, END } ;
|
||||
};
|
||||
|
||||
// R1405 module-stmt -> MODULE module-name
|
||||
|
@ -3072,6 +3078,7 @@ struct FunctionSubprogram {
|
|||
std::tuple<Statement<FunctionStmt>, SpecificationPart, ExecutionPart,
|
||||
std::optional<InternalSubprogramPart>, Statement<EndFunctionStmt>>
|
||||
t;
|
||||
enum { FUNC, SPEC, EXEC, INTERNAL, END } ;
|
||||
};
|
||||
|
||||
// R1534 subroutine-subprogram ->
|
||||
|
|
|
@ -24,7 +24,6 @@ target_link_libraries( test-type
|
|||
add_executable( test-sema
|
||||
test-sema.cc
|
||||
sema-impl.cc
|
||||
foobar.cc
|
||||
)
|
||||
|
||||
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