-
Notifications
You must be signed in to change notification settings - Fork 32
/
ObCodeModel.h
348 lines (303 loc) · 13.7 KB
/
ObCodeModel.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#ifndef OBCODEMODEL_H
#define OBCODEMODEL_H
/*
* Copyright 2019, 2020 Rochus Keller <mailto:me@rochus-keller.ch>
*
* This file is part of the Oberon parser/code model library.
*
* The following is the license that applies to this copy of the
* library. For a license to use the library under conditions
* other than those described here, please email to me@rochus-keller.ch.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License (GPL) versions 2.0 or 3.0 as published by the Free Software
* Foundation and appearing in the file LICENSE.GPL included in
* the packaging of this file. Please review the following information
* to ensure GNU General Public Licensing requirements will be met:
* http://www.fsf.org/licensing/licenses/info/GPLv2.html and
* http://www.gnu.org/copyleft/gpl.html.
*/
#include <QObject>
#include <Oberon/ObSynTree.h>
#include <Oberon/ObErrors.h>
#include <QMap>
#include <QStringList>
#include <QVector>
#include <QHash>
#include <bitset>
#include <QVariant>
#ifdef _DEBUG
#include <QtDebug>
#endif
class QTextStream;
class QIODevice;
namespace Ob
{
class Errors;
class FileCache;
class CodeModel : public QObject
{
public:
class NamedThing;
class Procedure;
class Element;
class Type;
class Module;
class Scope;
typedef QPair<const SynTree*,const NamedThing*> IdentUse; // idUse -> decl
typedef QList<IdentUse> IdentUseList;
typedef QHash<QString,IdentUseList> IdentUseDir; // sourcePath -> ids
typedef QMultiHash<const NamedThing*,const SynTree*> RevIndex;
typedef std::bitset<32> Set;
enum DesigOpCode { UnknownOp, IdentOp, PointerOp, TypeOp, ProcedureOp, ArrayOp };
struct DesigOp
{
DesigOpCode d_op;
SynTree* d_arg;
const NamedThing* d_sym;
DesigOp(DesigOpCode op = UnknownOp, SynTree* arg = 0, const NamedThing* t = 0):d_op(op),d_arg(arg),d_sym(t){}
};
typedef QList<DesigOp> DesigOpList;
typedef QPair<QPair<const Module*,const SynTree*>,QPair<const NamedThing*,const SynTree*> > Quali;
class NamedThing
{
public:
NamedThing():d_def(0),d_id(0),d_public(false),d_scope(0),d_var(false) {}
virtual ~NamedThing() {}
template<typename T>
const T* to() const { return dynamic_cast<const T*>(this); }
SynTree* d_def; // not owned; zeigt auf Context oder 0 für Stubs
SynTree* d_id; // not owned; zeigt auf Tok_ident oder 0
Scope* d_scope; // not owned; the scope which owns me (only if named)
QByteArray d_name;
bool d_public;
bool d_var; // var param
bool isStub() const { return d_def == 0; }
virtual const Type* getType() const { return 0; }
virtual QByteArray typeName() const { return ""; }
virtual QList<Element*> getParams() const { return QList<Element*>(); }
virtual bool isPredefProc() const { return false; }
};
class Element : public NamedThing
{
public:
enum Kind { Unknown,
ABS, ODD, LEN, LSL, ASR, ROR, FLOOR, FLT, ORD, CHR, INC, DEC, INCL, EXCL,
NEW, ASSERT, PACK, UNPK,
MAX, CAP, LONG, SHORT, HALT, COPY, ASH, MIN, SIZE, ENTIER, // OBN-2
BITS, INF, TRUE, FALSE, // BBOX
LED, // LED not global proc in Oberon report, but used as such in Project Oberon
Constant, Variable, StubProc
};
static const char* s_kindName[];
Element(Kind k = Unknown, Type* t = 0);
Element(const QVariant&, const QByteArray& name, const Type*);
~Element();
Kind d_kind;
SynTree* d_st; // not owned; Const: expr;
const Type* d_type; // not owned; Variable: type
QList<Element*> d_vals; // owned, only StubProc
QVariant d_const; // value in case of Constant
bool isPredefProc() const { return d_kind >= ABS && d_kind <= LED; }
QByteArray typeName() const;
virtual const Type* getType() const { return d_type; }
virtual QList<Element*> getParams() const;
};
class Scope : public NamedThing
{
public:
typedef QMap<QByteArray,NamedThing*> Names;
Scope():d_outer(0){}
const NamedThing* findByName(const QByteArray& , bool recursive = true) const;
const Module* getModule() const;
void addToScope( NamedThing* );
Scope* d_outer;
Names d_names; // not owned
};
class GlobalScope : public Scope
{
public:
GlobalScope():d_boolType(0),d_intType(0),d_realType(0),d_stringType(0),
d_nilType(0),d_setType(0){}
~GlobalScope();
QList<Element*> d_elems; // owned
QList<Module*> d_mods; // owned
QList<Type*> d_types; // owned
Type* d_boolType; // not owned
Type* d_intType; // not owned
Type* d_realType; // not owned
Type* d_stringType; // not owned
Type* d_nilType; // not owned
Type* d_setType; // not owned
Type* d_charType; // not owned
Type* d_anyRec; // not owned
virtual QByteArray typeName() const { return "GlobalScope"; }
};
class Unit : public Scope
{
public:
Unit() {}
~Unit();
QList<Type*> getNamedTypes() const;
QList<Element*> getConsts() const;
QList<Element*> getVars() const;
QList<Element*> getStubProcs() const;
QList<Element*> getUnknowns() const;
QList<Procedure*> d_procs; // owned
QList<Element*> d_elems; // owned;
QList<Type*> d_types; // owned
QList<SynTree*> d_body; // statements, not owned
};
class Module : public Unit
{
public:
// Module mit d_st==0 ist ein Stub
Module():d_isDef(false),d_isExt(false),d_sysString(0) {}
~Module() { if( d_def ) delete d_def; } // Module owns full SynTree
QList<Module*> d_using, d_usedBy; // not owned
bool d_isDef, d_isExt;
SynTree* d_sysString;
QByteArray typeName() const { return "Module"; }
};
class Procedure : public Unit
{
public:
Procedure():d_type(0),d_receiver(0),d_sysFlag(0){}
~Procedure();
QList<Element*> d_vals; // owned; params
Type* d_type; // not owned; return type
Element* d_receiver; // owned
SynTree* d_sysFlag; // not owned
const Type* getType() const { return d_type; }
QByteArray typeName() const { return "Procedure"; }
virtual QList<Element*> getParams() const { return d_vals; }
};
class Type : public NamedThing
{
public:
enum Kind { Unknown,
BOOLEAN, CHAR, SHORTINT, INTEGER, LONGINT, REAL, LONGREAL, BYTE, SET, // Basic Types
STRING, NIL, // Basic Type Helpers
TypeRef, Array, Record, Pointer, ProcRef
};
static const char* s_kindName[];
typedef QMap<QByteArray,Element*> Vals;
Type(Kind k = Unknown);
~Type();
bool isBasicType() const { return d_kind >= BOOLEAN && d_kind <= NIL; }
const Type* deref() const;
QByteArray typeName() const;
const Element* findByName( const QByteArray& ) const;
const NamedThing* find( const QByteArray& ) const;
virtual const Type* getType() const { return this; }
virtual QList<Element*> getParams() const;
Kind d_kind;
const Type* d_type; // not owned; Record: base; ProcRef: return; TypeRef, Array, Pointer: type
SynTree* d_st; // not owned; Array: dim expression; Ref: quali
Vals d_vals; // owned; Record: fields; ProcRef: Params
quint32 d_len; // Array: len
#ifdef OB_OBN2
typedef QMap<QByteArray,Procedure*> Procs;
Procs d_procs; // not owned (Scope::d_procs is the owner instead)
#endif
};
class TypeAlias : public NamedThing // wird nur temporär auf dem Stack verwendet
{
public:
TypeAlias():d_alias(0),d_newType(0){}
NamedThing* d_alias; // not owned
const Type* d_newType; // not owned
const Type* getType() const { return d_newType; }
};
explicit CodeModel(QObject *parent = 0);
void clear();
void setSynthesize(bool on) { d_synthesize = on; }
void setTrackIds(bool on) { d_trackIds = on; }
Errors* getErrs() const { return d_errs; }
FileCache* getFc() const { return d_fc; }
void setEnableExt( bool b ) { d_enableExt = b; }
void setSenseExt( bool b ) { d_senseExt = b; }
void addPreload( const QByteArray& name, const QByteArray& source );
bool parseFiles( const QStringList& );
const GlobalScope& getGlobalScope() const { return d_scope; }
const Type* typeOfExpression( const Unit*, SynTree* ) const;
bool isArrayOfChar( const NamedThing* ) const;
QVariant evalExpression(const Unit*, SynTree* expr) const; // returns longlong, double, Set, bytearray, bool or invalid
static SynTree* flatten( SynTree*, int stopAt = 0 );
DesigOpList derefDesignator( const Unit*,const SynTree*) const;
Quali derefQualident(const Unit*,const SynTree*);
static void dump( QTextStream&, const SynTree*, int level = 0 );
IdentUse findSymbolBySourcePos(const QString& file, quint32 line, quint16 col ) const;
QList<const SynTree*> findReferencingSymbols( const NamedThing*, const QString& file = QString() );
QList<Token> getComments( QString file ) const;
static SynTree* findFirstChild(const SynTree*, int type , int startWith = 0);
static QByteArrayList getBuitinIdents();
protected:
quint32 parseFile( const QString& );
quint32 parseFile(QIODevice* , const QString& path);
void checkModuleDependencies();
QList<Module*> findProcessingOrder();
void processDeclSeq(Unit*,SynTree*);
void processConstDeclaration(Unit*,SynTree*);
void processTypeDeclaration(Unit*,SynTree*);
void processVariableDeclaration(Unit*,SynTree*);
void processProcedureDeclaration(Unit*,SynTree*);
bool checkNameNotInScope(Scope* scope, SynTree* id);
bool checkNameNotInRecord(const Type* scope, SynTree* id);
void checkTypeRules(Unit*);
void checkNames(Unit*);
void checkNames(Unit*, SynTree*, const Type* expected = 0);
void checkAssig( Unit*, const DesigOpList&, SynTree* expr );
void checkCaseStatement(Unit* ds, SynTree* st);
void checkWithStatement(Unit* ds, SynTree* st);
void checkGuard(Unit* ds, SynTree* guard, SynTree* stats );
Type* parseType( Unit* ds, SynTree* t );
Type* parseTypeRef( Unit* ds, SynTree* t );
Type* parsePointerType( Unit* ds, SynTree* t );
Type* parseRecordType( Unit* ds, SynTree* t );
Type* parseArrayType( Unit* ds, SynTree* t );
Type* parseProcType( Unit* ds, SynTree* t );
Type* parseFormalParams(Unit* ds, SynTree* t, QList<Element*>&);
void resolveTypeRefs( Unit* ds );
void resolveRefType( Unit* ds, Type* t );
Quali derefQualident(Unit* ds, SynTree* t , bool report, bool synthesize = false );
DesigOpList derefDesignator( Unit*,SynTree*,bool report, bool synthesize, const CodeModel::Type* expected = 0);
enum DesigOpErr { NoError, NotFound, InvalidOperation, MissingType };
const NamedThing* applyDesigOp(Unit* ds, const NamedThing* input, const DesigOp& dop, DesigOpErr* errOut ,
bool synthesize = true, const Type* expected = 0);
const Type* typeOfExpression( Unit*, Element::Kind, SynTree* args ) const;
QVariant evalSimpleExpression(const Unit*, SynTree* expr) const;
QVariant evalTerm(const Unit*, SynTree* expr) const;
QVariant evalFactor(const Unit*, SynTree* expr) const;
QVariant evalLiteral(const Unit*, SynTree* expr) const;
void index( const SynTree* idUse, const NamedThing* decl );
void addLowerCaseGlobals();
bool error(Errors::Source s, const SynTree*, const QString& msg) const;
static QPair<SynTree*,bool> getIdentFromIdentDef(SynTree*);
static DesigOp getSelectorOp( SynTree* );
static inline DesigOpErr sayInvalid(const DesigOp& dop, const char* input )
{
#ifdef _DEBUG
qDebug() << "*** invalid op" << dop.d_op << "with input" << input <<
(dop.d_arg ? dop.d_arg->d_tok.d_sourcePath : QString()) <<
(dop.d_arg ? dop.d_arg->d_tok.d_lineNr : -1 );
#endif
return InvalidOperation;
}
private:
GlobalScope d_scope;
QHash<QString,QList<Token> > d_comments;
QHash<QByteArray,QByteArray> d_preload;
Errors* d_errs;
FileCache* d_fc;
IdentUseDir d_dir;
RevIndex d_revDir;
bool d_synthesize; // create stubs if needed
bool d_trackIds;
bool d_enableExt; // Allow for both uppercase and lowercase keywords and builtins, idents with underscores
bool d_senseExt; // automacitally determine if extensions enabled (starts with lower case module, etc.)
};
}
//Q_DECLARE_METATYPE( Ob::CodeModel::Set )
#endif // OBCODEMODEL_H