Skip to content

Commit

Permalink
Introduce type ids (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Dec 3, 2023
1 parent 6805d45 commit 27073cb
Show file tree
Hide file tree
Showing 45 changed files with 647 additions and 678 deletions.
13 changes: 12 additions & 1 deletion docs/docs/language/attributes.md
Expand Up @@ -62,4 +62,15 @@ public type A struct {

### Available attributes
- `core.compiler.alwaysEmitVTable: bool`: Always emit a vtable for the annotated struct
- `core.compiler.packed: bool`: Pack the annotated struct
- `core.compiler.packed: bool`: Pack the annotated struct

## Interface attributes
```spice
#[core.compiler.fixedTypeId = 230]
public type A interface {
// ...
}
```

### Available attributes
- `core.compiler.fixedTypeId: int`: Set a fixed type id for the annotated interface. Intended for internal use only.
21 changes: 12 additions & 9 deletions media/specs/better-imports.md
Expand Up @@ -17,14 +17,15 @@ this symbol gets removed and

**Example:** <br>
Source file A imports source file B as `sourceB`. B contains a struct with the name `TestStruct`. Formerly, you would
have written `sourceB::TestStruct` to access the struct from source file A. Now you only need to write `TestStruct`.
have written `sourceB::TestStruct` to access the struct from source file A. Now you only need to write `TestStruct` and the
compile resolves the correct struct automatically.

Registry of A:

| Name | Pointer to SymbolTableEntry | Pointer to scope of symbol | Import entry | Predecessor name |
|---------------------|-----------------------------|----------------------------|---------------------|----------------------|
| sourceB::TestStruct | Pointer to struct entry | Ptr to struct body scope | Ptr to import entry | |
| TestStruct | Pointer to struct entry | Ptr to struct body scope | Ptr to import entry | sourceB::TestStruct |
| Name | TypeId | Pointer to SymbolTableEntry | Pointer to scope of symbol | Import entry |
|---------------------|--------|-----------------------------|----------------------------|---------------------|
| sourceB::TestStruct | 256 | Pointer to struct entry | Ptr to struct body scope | Ptr to import entry |
| TestStruct | 256 | Pointer to struct entry | Ptr to struct body scope | Ptr to import entry |

However, if A also imports source file C, which also exposes a struct with the name `TestStruct`, there would be an
ambiguity between `sourceB::TestStruct` and `sourceC::TestStruct` when you just write `TestStruct`. Therefore, when
Expand All @@ -33,7 +34,9 @@ Accessing `TestStruct` without a scope identifier, leads to a compile error.

Registry of A:

| Name | Pointer to SymbolTableEntry | Pointer to scope of symbol | Import entry | Predecessor name |
|---------------------|-----------------------------|------------------------------|---------------------|------------------|
| sourceB::TestStruct | Pointer to struct entry | Pointer to struct body scope | Ptr to import entry | |
| sourceC::TestStruct | Pointer to struct entry | Pointer to struct body scope | Ptr to import entry | |
| Name | TypeId | Pointer to SymbolTableEntry | Pointer to scope of symbol | Import entry |
|---------------------|--------|-----------------------------|------------------------------|---------------------|
| sourceB::TestStruct | 256 | Pointer to struct entry | Pointer to struct body scope | Ptr to import entry |
| sourceC::TestStruct | 257 | Pointer to struct entry | Pointer to struct body scope | Ptr to import entry |

This behavior is intended to avoid bugs due to accessing the wrong structs by accident.
44 changes: 42 additions & 2 deletions media/test-project/test.spice
@@ -1,5 +1,45 @@
import "../../src-bootstrap/reader/reader";

f<int> main() {
Reader reader = Reader("./test.spice");
}
Reader reader = Reader("./test-file.txt");
printf("%d", reader.isEOF());
while !reader.isEOF() {
printf("%c", reader.getChar());
reader.advance();
}
}

/*import "std/io/file";

f<int> main() {
// Write file
Result<File> fileResult = openFile("./test-file.txt", MODE_WRITE);
assert fileResult.isOk();
File file = fileResult.unwrap();
file.write("Hello, world!\n");
file.close();

// Read file
fileResult = openFile("./test-file.txt", MODE_READ);
assert fileResult.isOk();
file = fileResult.unwrap();
String line = file.readLine();
printf("%s", line);
assert line.getRaw() == "Hello, world!\n";
file.close();

// Append file
fileResult = openFile("./test-file.txt", MODE_APPEND);
assert fileResult.isOk();
file = fileResult.unwrap();
file.write("Hello, again!\n");
file.close();

// Read file
fileResult = openFile("./test-file.txt", MODE_READ);
assert fileResult.isOk();
file = fileResult.unwrap();
assert file.readLine() == String("Hello, world!\n");
assert file.readLine() == String("Hello, again!\n");
file.close();
}*/
7 changes: 3 additions & 4 deletions src-bootstrap/reader/code-loc.spice
Expand Up @@ -2,9 +2,9 @@
import "std/type/long";

public type CodeLoc struct {
unsigned long line
unsigned long col
string sourceFilePath
public unsigned long line
public unsigned long col
public string sourceFilePath
}

public p CodeLoc.ctor(unsigned long line, unsigned long col, string sourceFilePath = "") {
Expand All @@ -28,7 +28,6 @@ public f<String> CodeLoc.toString() {
* @return Pretty code location
*/
public f<String> CodeLoc.toPrettyString() {
String codeLocStr = String(toString(this.line));
if len(this.sourceFilePath) == 0 {
return toString(this.line) + ":" + toString(this.col);
}
Expand Down
27 changes: 14 additions & 13 deletions src-bootstrap/reader/reader.spice
Expand Up @@ -11,29 +11,30 @@ public type Reader struct {
File file
string filename
char curChar = '\0'
unsigned long line = 1l
unsigned long col = 0l
CodeLoc curCodeLoc
}

public p Reader.ctor(const string inputFileName) {
this.filename = inputFileName;
this.curCodeLoc = CodeLoc(1l, 0l, inputFileName);

// Open the file input stream
Result<File> result = openFile(inputFileName, MODE_READ);
if !result.isOk() {
panic(Error("Source file cannot be opened"));
}
this.file = result.unwrap();
}

public p Reader.dtor() {
this.file.close();
// Read the first character
this.advance();
}

/**
* @brief Get the previously read character
*
* @return char Last character
*/
public f<char> Reader.getChar() {
public inline f<char> Reader.getChar() {
return this.curChar;
}

Expand All @@ -42,8 +43,8 @@ public f<char> Reader.getChar() {
*
* @return CodeLoc Code location
*/
public f<CodeLoc> Reader.getCodeLoc() {
return CodeLoc(this.line, this.col, this.filename);
public inline f<CodeLoc&> Reader.getCodeLoc() {
return this.curCodeLoc;
}

/**
Expand All @@ -53,10 +54,10 @@ public p Reader.advance() {
assert !this.isEOF();
this.curChar = (char) this.file.readChar();
if this.curChar == '\n' {
this.line++;
this.col = 0l;
this.curCodeLoc.line++;
this.curCodeLoc.col = 0l;
}
this.col++;
this.curCodeLoc.col++;
}

/**
Expand All @@ -65,7 +66,7 @@ public p Reader.advance() {
*
* @param c Expected char
*/
public p Reader.expect(char c) {
public inline p Reader.expect(char c) {
assert this.curChar == c;
this.advance();
}
Expand All @@ -75,6 +76,6 @@ public p Reader.expect(char c) {
*
* @return At the end or not
*/
public f<bool> Reader.isEOF() {
public inline f<bool> Reader.isEOF() {
return this.file.isEOF();
}
31 changes: 14 additions & 17 deletions src/SourceFile.cpp
Expand Up @@ -588,30 +588,27 @@ SourceFile *SourceFile::requestRuntimeModule(RuntimeModule runtimeModule) {

bool SourceFile::isRuntimeModuleAvailable(RuntimeModule runtimeModule) const { return importedRuntimeModules & runtimeModule; }

void SourceFile::addNameRegistryEntry(const std::string &symbolName, SymbolTableEntry *entry, Scope *scope,
bool keepNewOnCollision /*=true*/, SymbolTableEntry *importEntry /*=nullptr*/,
const std::string &predecessorName /*=""*/) {
void SourceFile::addNameRegistryEntry(const std::string &symbolName, uint64_t typeId, SymbolTableEntry *entry, Scope *scope,
bool keepNewOnCollision, SymbolTableEntry *importEntry) {
if (keepNewOnCollision || !exportedNameRegistry.contains(symbolName)) // Overwrite potential existing entry
exportedNameRegistry[symbolName] = {symbolName, entry, scope, importEntry, predecessorName};
exportedNameRegistry[symbolName] = {symbolName, typeId, entry, scope, importEntry};
else // Name collision => we must remove the existing entry
exportedNameRegistry.erase(symbolName);
}

const NameRegistryEntry *SourceFile::getNameRegistryEntry(std::string symbolName) const {
const NameRegistryEntry *SourceFile::getNameRegistryEntry(const std::string &symbolName) const {
if (!exportedNameRegistry.contains(symbolName))
return nullptr;

// Resolve the 'fullest-qualified' registry entry for the given name
const NameRegistryEntry *registryEntry;
do {
assert(exportedNameRegistry.contains(symbolName));
registryEntry = &exportedNameRegistry.at(symbolName);
if (registryEntry->importEntry)
registryEntry->importEntry->used = true;
symbolName = registryEntry->predecessorName;
} while (!symbolName.empty());
// Resolve registry entry for the given name
assert(exportedNameRegistry.contains(symbolName));
const NameRegistryEntry *entry = &exportedNameRegistry.at(symbolName);

// Mark the import entry as used
if (entry->importEntry != nullptr)
entry->importEntry->used = true;

return registryEntry;
return entry;
}

void SourceFile::checkForSoftErrors() {
Expand Down Expand Up @@ -669,10 +666,10 @@ void SourceFile::mergeNameRegistries(const SourceFile &importedSourceFile, const
std::string newName = importName;
newName += SCOPE_ACCESS_TOKEN;
newName += originalName;
exportedNameRegistry.insert({newName, {newName, entry.targetEntry, entry.targetScope, importEntry}});
exportedNameRegistry.insert({newName, {newName, entry.typeId, entry.targetEntry, entry.targetScope, importEntry}});
// Add the shortened name, considering the name collision
const bool keepOnCollision = importedSourceFile.alwaysKeepSymbolsOnNameCollision;
addNameRegistryEntry(originalName, entry.targetEntry, entry.targetScope, keepOnCollision, importEntry, newName);
addNameRegistryEntry(originalName, entry.typeId, entry.targetEntry, entry.targetScope, keepOnCollision, importEntry);
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/SourceFile.h
Expand Up @@ -87,17 +87,17 @@ struct CompilerOutput {

struct NameRegistryEntry {
std::string name;
uint64_t typeId; // Set for structs, interfaces and enums
SymbolTableEntry *targetEntry;
Scope *targetScope;
SymbolTableEntry *importEntry = nullptr;
std::string predecessorName;
};

class SourceFile {
public:
// Constructors
explicit SourceFile(GlobalResourceManager &resourceManager, SourceFile *parent, std::string name,
const std::filesystem::path &filePath, bool stdFile);
SourceFile(GlobalResourceManager &resourceManager, SourceFile *parent, std::string name, const std::filesystem::path &filePath,
bool stdFile);
SourceFile(const SourceFile &) = delete;

// Friend classes
Expand Down Expand Up @@ -138,9 +138,9 @@ class SourceFile {
[[nodiscard]] bool isAlreadyImported(const std::string &filePathSearch) const;
SourceFile *requestRuntimeModule(RuntimeModule runtimeModule);
bool isRuntimeModuleAvailable(RuntimeModule runtimeModule) const;
void addNameRegistryEntry(const std::string &symbolName, SymbolTableEntry *entry, Scope *scope, bool keepNewOnCollision = true,
SymbolTableEntry *importEntry = nullptr, const std::string &predecessorName = "");
[[nodiscard]] const NameRegistryEntry *getNameRegistryEntry(std::string symbolName) const;
void addNameRegistryEntry(const std::string &symbolName, uint64_t typeId, SymbolTableEntry *entry, Scope *scope,
bool keepNewOnCollision = true, SymbolTableEntry *importEntry = nullptr);
[[nodiscard]] const NameRegistryEntry *getNameRegistryEntry(const std::string &symbolName) const;
void checkForSoftErrors();
void collectAndPrintWarnings();
bool isStringRT() const;
Expand Down
2 changes: 1 addition & 1 deletion src/Spice.g4
Expand Up @@ -9,7 +9,7 @@ functionDef: topLevelDefAttr? specifierLst? F LESS dataType GREATER fctName (LES
procedureDef: topLevelDefAttr? specifierLst? P fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
fctName: (TYPE_IDENTIFIER DOT)? IDENTIFIER | OPERATOR overloadableOp;
structDef: topLevelDefAttr? specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? STRUCT (COLON typeLst)? LBRACE field* RBRACE;
interfaceDef: specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? INTERFACE LBRACE signature+ RBRACE;
interfaceDef: topLevelDefAttr? specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? INTERFACE LBRACE signature+ RBRACE;
enumDef: specifierLst? TYPE TYPE_IDENTIFIER ENUM LBRACE enumItemLst RBRACE;
genericTypeDef: TYPE TYPE_IDENTIFIER typeAltsLst SEMICOLON;
aliasDef: specifierLst? TYPE TYPE_IDENTIFIER ALIAS dataType SEMICOLON;
Expand Down
19 changes: 19 additions & 0 deletions src/ast/ASTBuilder.cpp
Expand Up @@ -6,6 +6,7 @@

#include <SourceFile.h>
#include <ast/ASTNodes.h>
#include <ast/Attributes.h>
#include <exception/ParserError.h>
#include <typechecker/OpRuleManager.h>
#include <util/CommonUtil.h>
Expand Down Expand Up @@ -112,6 +113,7 @@ std::any ASTBuilder::visitStructDef(SpiceParser::StructDefContext *ctx) {

// Enrich
structDefNode->structName = getIdentifier(ctx->TYPE_IDENTIFIER());
structDefNode->typeId = resourceManager.getNextCustomTypeId();
structDefNode->hasTemplateTypes = ctx->LESS();
structDefNode->hasInterfaces = ctx->COLON();

Expand All @@ -123,6 +125,10 @@ std::any ASTBuilder::visitStructDef(SpiceParser::StructDefContext *ctx) {
for (AttrNode *attr : structDefNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_STRUCT;

// Check if a custom type id was set
if (structDefNode->attrs() && structDefNode->attrs()->attrLst()->hasAttr(ATTR_CORE_COMPILER_FIXED_TYPE_ID))
structDefNode->typeId = structDefNode->attrs()->attrLst()->getAttrValueByName(ATTR_CORE_COMPILER_FIXED_TYPE_ID)->intValue;

return concludeNode(ctx, structDefNode);
}

Expand All @@ -131,11 +137,22 @@ std::any ASTBuilder::visitInterfaceDef(SpiceParser::InterfaceDefContext *ctx) {

// Enrich
interfaceDefNode->interfaceName = getIdentifier(ctx->TYPE_IDENTIFIER());
interfaceDefNode->typeId = resourceManager.getNextCustomTypeId();
interfaceDefNode->hasTemplateTypes = ctx->LESS();

// Visit children
visitChildren(ctx);

// Tell the attributes that they are interface attributes
if (interfaceDefNode->attrs())
for (AttrNode *attr : interfaceDefNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_INTERFACE;

// Check if a custom type id was set
if (interfaceDefNode->attrs() && interfaceDefNode->attrs()->attrLst()->hasAttr(ATTR_CORE_COMPILER_FIXED_TYPE_ID))
interfaceDefNode->typeId =
interfaceDefNode->attrs()->attrLst()->getAttrValueByName(ATTR_CORE_COMPILER_FIXED_TYPE_ID)->intValue;

return concludeNode(ctx, interfaceDefNode);
}

Expand All @@ -144,6 +161,7 @@ std::any ASTBuilder::visitEnumDef(SpiceParser::EnumDefContext *ctx) {

// Enrich
enumDefNode->enumName = getIdentifier(ctx->TYPE_IDENTIFIER());
enumDefNode->typeId = resourceManager.getNextCustomTypeId();

// Visit children
visitChildren(ctx);
Expand Down Expand Up @@ -202,6 +220,7 @@ std::any ASTBuilder::visitExtDecl(SpiceParser::ExtDeclContext *ctx) {
// Enrich
extDeclNode->extFunctionName = getIdentifier(ctx->IDENTIFIER() ? ctx->IDENTIFIER() : ctx->TYPE_IDENTIFIER());
extDeclNode->hasArgs = ctx->typeLst();
extDeclNode->isVarArg = ctx->ELLIPSIS();

// Visit children
visitChildren(ctx);
Expand Down

0 comments on commit 27073cb

Please sign in to comment.