Skip to content

Commit

Permalink
Rework attribute handling (#366)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Nov 11, 2023
1 parent 2e3bc37 commit d6396bb
Show file tree
Hide file tree
Showing 26 changed files with 336 additions and 93 deletions.
30 changes: 22 additions & 8 deletions docs/docs/language/attributes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,52 @@ title: Attributes

Spice offers the option to annotate single function or whole modules via attributes.

## Module attributes
!!! note "Optional value for bool attributes"
For attributes of type bool, the value `true` can be omitted.

## Module attributes
```spice
// Attribute for the whole module
#![core.linker.flags = "-pthread"]
#![core.linker.flag = "-pthread"]
// ...
```

### Available attributes
- `core.linker.flag: string (default: "")`: Append linker flag
- `core.compiler.alwaysKeepOnNameCollision: bool (default: false)`: Always keep the symbols of this source files when merging the name registries of multiple source files

- `core.linker.flag: string`: Append linker flag
- `core.compiler.alwaysKeepOnNameCollision: bool`: Always keep the symbols of this source files when merging the name registries of multiple source files

## Function attributes

```spice
#[core.compiler.mangleName = false]
f<int> test(long input) {
return input == 0 ? 123 : 456;
}
// ...
```

### Available attributes
- `core.compiler.mangle: bool (default: true)`: Enable/disable name mangling for the annotated function
- `core.compiler.mangledName: string`: Set the mangled name for the annotated function


- `core.compiler.mangle: bool`: Disable name mangling for the annotated function
## External declaration attributes
```spice
#[core.compiler.mangleName = false]
ext f<heap byte*> malloc(unsigned long);
// ...
```

### Available attributes
- `core.compiler.mangle: bool (default: true)`: Enable/disable name mangling for the annotated function
- `core.compiler.mangledName: string`: Set the mangled name for the annotated function
- `core.linker.dll: bool`: Enable linkage as dll (only relevant for Windows)

## Struct attributes

## Struct attributes
```spice
#[core.compiler.alwaysEmitVTable = true]
public type A struct {
Expand All @@ -43,6 +58,5 @@ 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
34 changes: 2 additions & 32 deletions media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,33 +1,3 @@
import "std/iterator/iterable";
import "std/data/pair";
#![core.linker.flag]

type T short|int|long;

type MockIterator<T> struct : Iterable<T> {
T item
unsigned long cursor
}

p MockIterator.ctor() {
this.cursor = 0l;
}

f<T&> MockIterator.get() {
return this.item;
}

f<Pair<unsigned long, T&>> MockIterator.getIdx() {
return Pair<unsigned long, T&>(0l, this.item);
}

f<bool> MockIterator.isValid() {
return true;
}

p MockIterator.next() {}

f<int> main() {
foreach dyn item : MockIterator<short>() {
printf("Demo item: %d\n", item);
}
}
f<int> main() {}
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ set(SOURCES
ast/ParallelizableASTVisitor.h
ast/ASTBuilder.cpp
ast/ASTBuilder.h
ast/Attributes.h
# AST visualizer
visualizer/ASTVisualizer.cpp
visualizer/ASTVisualizer.h
Expand Down
2 changes: 1 addition & 1 deletion src/Spice.g4
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ specifier: CONST | SIGNED | UNSIGNED | INLINE | PUBLIC | HEAP | COMPOSE;
modAttr: MOD_ATTR_PREAMBLE LBRACKET attrLst RBRACKET;
topLevelDefAttr: FCT_ATTR_PREAMBLE LBRACKET attrLst RBRACKET;
attrLst: attr (COMMA attr)*;
attr: IDENTIFIER (DOT IDENTIFIER)* ASSIGN constant;
attr: IDENTIFIER (DOT IDENTIFIER)* (ASSIGN constant)?;
importStmt: IMPORT STRING_LIT (AS IDENTIFIER)? SEMICOLON;
returnStmt: RETURN assignExpr?;
breakStmt: BREAK INT_LIT?;
Expand Down
39 changes: 39 additions & 0 deletions src/ast/ASTBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ std::any ASTBuilder::visitFunctionDef(SpiceParser::FunctionDefContext *ctx) {
// Tell the return type that it is one
fctDefNode->returnType()->isReturnType = true;

// Tell the attributes that they are function attributes
if (fctDefNode->attrs())
for (AttrNode *attr : fctDefNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_FCT_PROC;

return concludeNode(ctx, fctDefNode);
}

Expand All @@ -71,6 +76,11 @@ std::any ASTBuilder::visitProcedureDef(SpiceParser::ProcedureDefContext *ctx) {
procDefNode->name = procDefNode->getChild<FctNameNode>();
procDefNode->isMethod = procDefNode->name->nameFragments.size() > 1;

// Tell the attributes that they are procedure attributes
if (procDefNode->attrs())
for (AttrNode *attr : procDefNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_FCT_PROC;

return concludeNode(ctx, procDefNode);
}

Expand Down Expand Up @@ -108,6 +118,11 @@ std::any ASTBuilder::visitStructDef(SpiceParser::StructDefContext *ctx) {
// Visit children
visitChildren(ctx);

// Tell the attributes that they are struct attributes
if (structDefNode->attrs())
for (AttrNode *attr : structDefNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_STRUCT;

return concludeNode(ctx, structDefNode);
}

Expand Down Expand Up @@ -191,6 +206,11 @@ std::any ASTBuilder::visitExtDecl(SpiceParser::ExtDeclContext *ctx) {
// Visit children
visitChildren(ctx);

// Tell the attributes that they are ext decl attributes
if (extDeclNode->attrs())
for (AttrNode *attr : extDeclNode->attrs()->attrLst()->attributes())
attr->target = AttrNode::TARGET_EXT_DECL;

return concludeNode(ctx, extDeclNode);
}

Expand Down Expand Up @@ -466,6 +486,10 @@ std::any ASTBuilder::visitModAttr(SpiceParser::ModAttrContext *ctx) {
// Visit children
visitChildren(ctx);

// Tell the attributes that they are module attributes
for (AttrNode *attr : modAttrNode->attrLst()->attributes())
attr->target = AttrNode::TARGET_MODULE;

return concludeNode(ctx, modAttrNode);
}

Expand Down Expand Up @@ -500,6 +524,21 @@ std::any ASTBuilder::visitAttr(SpiceParser::AttrContext *ctx) {
// Visit children
visitChildren(ctx);

// Come up with type
if (ctx->constant()) {
if (ctx->constant()->STRING_LIT())
attrNode->type = AttrNode::TYPE_STRING;
else if (ctx->constant()->INT_LIT())
attrNode->type = AttrNode::TYPE_INT;
else if (ctx->constant()->TRUE() || ctx->constant()->FALSE())
attrNode->type = AttrNode::TYPE_BOOL;
else
throw ParserError(attrNode->value()->codeLoc, INVALID_ATTR_VALUE_TYPE, "Invalid attribute value type");
} else {
// If no value is given, use the bool type
attrNode->type = AttrNode::TYPE_BOOL;
}

return concludeNode(ctx, attrNode);
}

Expand Down
40 changes: 32 additions & 8 deletions src/ast/ASTNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <ast/ASTNodes.h>

#include <ast/Attributes.h>
#include <symboltablebuilder/SymbolTableBuilder.h>

namespace spice::compiler {
Expand Down Expand Up @@ -67,19 +68,42 @@ bool StmtLstNode::returnsOnAllControlPaths(bool *) const {
return returns;
}

std::vector<AttrNode *> AttrLstNode::getAttrsByName(const char *key) const {
const std::vector<AttrNode *> attrs = attributes();
std::vector<AttrNode *> newAttrs;
std::copy_if(attrs.begin(), attrs.end(), std::back_inserter(newAttrs), [=](const AttrNode *attr) { return attr->key == key; });
return newAttrs;
std::vector<const CompileTimeValue *> AttrLstNode::getAttrValuesByName(const std::string &key) const {
assert(ATTR_CONFIGS.contains(key));
const std::vector<AttrNode *> attrNodes = attributes();

std::vector<const CompileTimeValue *> attributeValues;
for (AttrNode *attrNode : attrNodes) {
// Skip attributes with different keys
if (attrNode->key != key)
continue;

// Found a matching attribute
const CompileTimeValue *value = attrNode->getValue();
if (!value) {
// If the attribute has no value, we use the default value
const AttrConfigValue &config = ATTR_CONFIGS.at(key);
attributeValues.push_back(&DEFAULT_BOOL_COMPILE_VALUE);
} else {
// If the attribute has a value, we use the value
attributeValues.push_back(value);
}
}

return attributeValues;
}

AttrNode *AttrLstNode::getAttrByName(const char *key) const {
const std::vector<AttrNode *> attrs = getAttrsByName(key);
const CompileTimeValue *AttrLstNode::getAttrValueByName(const std::string &key) const {
const std::vector<const CompileTimeValue *> attrs = getAttrValuesByName(key);
return attrs.empty() ? nullptr : attrs.back();
}

const CompileTimeValue &AttrNode::getValue() const { return value()->compileTimeValue; }
bool AttrLstNode::hasAttr(const std::string &key) const {
const std::vector<AttrNode *> attrs = attributes();
return std::ranges::any_of(attrs, [&](AttrNode *attr) { return attr->key == key; });
}

const CompileTimeValue *AttrNode::getValue() const { return value() ? &value()->compileTimeValue : nullptr; }

bool AssertStmtNode::returnsOnAllControlPaths(bool *overrideUnreachable) const {
const bool returns = hasCompileTimeValue() && !compileTimeValue.boolValue;
Expand Down
30 changes: 20 additions & 10 deletions src/ast/ASTNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1014,22 +1014,30 @@ class AttrLstNode : public ASTNode {
[[nodiscard]] std::vector<AttrNode *> attributes() const { return getChildren<AttrNode>(); }

// Other methods
[[nodiscard]] std::vector<AttrNode *> getAttrsByName(const char *key) const;
[[nodiscard]] AttrNode *getAttrByName(const char *key) const;
[[nodiscard]] std::vector<const CompileTimeValue *> getAttrValuesByName(const std::string &key) const;
[[nodiscard]] const CompileTimeValue *getAttrValueByName(const std::string &key) const;
[[nodiscard]] bool hasAttr(const std::string &key) const;
};

// ============================================================ AttrNode =========================================================

class AttrNode : public ASTNode {
public:
// Enums
static constexpr const char *const ATTR_CORE_LINKER_FLAG = "core.linker.flag";
static constexpr const char *const ATTR_CORE_LINKER_DLL = "core.linker.dll";
static constexpr const char *const ATTR_CORE_COMPILER_MANGLED_NAME = "core.compiler.mangledName";
static constexpr const char *const ATTR_CORE_COMPILER_MANGLE = "core.compiler.mangle";
static constexpr const char *const ATTR_CORE_COMPILER_KEEP_ON_NAME_COLLISION = "core.compiler.alwaysKeepOnNameCollision";
static constexpr const char *const ATTR_CORE_COMPILER_EMIT_VTABLE = "core.compiler.alwaysEmitVTable";
static constexpr const char *const ATTR_CORE_COMPILER_PACKED = "core.compiler.packed";
enum AttrTarget : uint8_t {
TARGET_INVALID = 0,
TARGET_MODULE = 1 << 0,
TARGET_STRUCT = 1 << 1,
TARGET_FCT_PROC = 1 << 2,
TARGET_EXT_DECL = 1 << 3,
};

enum AttrType : uint8_t {
ATTR_TYPE_INVALID,
TYPE_STRING,
TYPE_BOOL,
TYPE_INT,
};

// Constructors
using ASTNode::ASTNode;
Expand All @@ -1042,10 +1050,12 @@ class AttrNode : public ASTNode {
[[nodiscard]] ConstantNode *value() const { return getChild<ConstantNode>(); }

// Other methods
[[nodiscard]] const CompileTimeValue &getValue() const;
[[nodiscard]] const CompileTimeValue *getValue() const;

// Public members
std::string key;
AttrType type = ATTR_TYPE_INVALID;
AttrTarget target = TARGET_INVALID;
};

// ======================================================== ImportStmtNode =======================================================
Expand Down
Loading

0 comments on commit d6396bb

Please sign in to comment.