Skip to content

Commit

Permalink
Add support for function and module attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed May 31, 2023
1 parent 84dc25b commit d266710
Show file tree
Hide file tree
Showing 40 changed files with 514 additions and 213 deletions.
2 changes: 1 addition & 1 deletion .run/spicetest.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spicetest" type="CMakeGoogleTestRunConfigurationType" factoryName="Google Test" PROGRAM_PARAMS="--update-refs=false" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spicetest" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spicetest" TEST_MODE="SUITE_TEST">
<configuration default="false" name="spicetest" type="CMakeGoogleTestRunConfigurationType" factoryName="Google Test" PROGRAM_PARAMS="--update-refs=true" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spicetest" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spicetest" TEST_MODE="SUITE_TEST">
<envs>
<env name="RUN_TESTS" value="ON" />
<env name="SPICE_STD_DIR" value="$PROJECT_DIR$/std" />
Expand Down
32 changes: 32 additions & 0 deletions docs/docs/language/attributes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Attributes
---

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

## Module attributes

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

### Available attributes

- `core.linker.flag: string`

## Function attributes

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

### Available attributes

- `core.compiler.mangleName: bool`
- `core.linker.dll: bool`
2 changes: 1 addition & 1 deletion media/specs/compile-stages.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ graph TD;

7. **Import Collector** <br>
Input/Output: AST -> AST <br>
Note: Checks, which other source file are imported by the current one. Registers external symbols as described
Note: Checks, which other source file are imported by the current one. Registers external symbols. Process module attributes.
[here](./better-imports.md).

8. **Symbol Table Builder** <br>
Expand Down
2 changes: 0 additions & 2 deletions media/test-project/test.spice
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import "std/os/thread";

ext p usleep(int);

p threadRoutine() {
printf("Test\n");
printf("Hello from thread: %d\n", getThreadId());
Expand Down
21 changes: 13 additions & 8 deletions src/Spice.g4
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
grammar Spice;

// Top level definitions and declarations
entry: (mainFunctionDef | functionDef | procedureDef | structDef | interfaceDef | enumDef | genericTypeDef | aliasDef | globalVarDef | importStmt | extDecl)*;
mainFunctionDef: F LESS TYPE_INT GREATER MAIN LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
functionDef: specifierLst? F LESS dataType GREATER fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
procedureDef: specifierLst? P fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
entry: (mainFunctionDef | functionDef | procedureDef | structDef | interfaceDef | enumDef | genericTypeDef | aliasDef | globalVarDef | importStmt | extDecl | modAttr)*;
mainFunctionDef: fctAttr? F LESS TYPE_INT GREATER MAIN LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
functionDef: fctAttr? specifierLst? F LESS dataType GREATER fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
procedureDef: fctAttr? specifierLst? P fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE;
fctName: (IDENTIFIER DOT)? IDENTIFIER | OPERATOR overloadableOp;
structDef: specifierLst? TYPE IDENTIFIER (LESS typeLst GREATER)? STRUCT (COLON typeLst)? LBRACE field* RBRACE;
interfaceDef: specifierLst? TYPE IDENTIFIER (LESS typeLst GREATER)? INTERFACE LBRACE signature+ RBRACE;
enumDef: specifierLst? TYPE IDENTIFIER ENUM LBRACE enumItemLst RBRACE;
genericTypeDef: TYPE IDENTIFIER typeAltsLst SEMICOLON;
aliasDef: TYPE IDENTIFIER ALIAS dataType SEMICOLON;
globalVarDef: dataType IDENTIFIER (ASSIGN constant)? SEMICOLON;
extDecl: EXT (F LESS dataType GREATER | P) IDENTIFIER LPAREN (typeLst ELLIPSIS?)? RPAREN DLL? SEMICOLON;
extDecl: fctAttr? EXT (F LESS dataType GREATER | P) IDENTIFIER LPAREN (typeLst ELLIPSIS?)? RPAREN SEMICOLON;

// Control structures
unsafeBlockDef: UNSAFE LBRACE stmtLst RBRACE;
Expand Down Expand Up @@ -42,6 +42,10 @@ stmt: (declStmt | assignExpr | returnStmt | breakStmt | continueStmt) SEMICOLON;
declStmt: dataType IDENTIFIER (ASSIGN assignExpr)?;
specifierLst: specifier+;
specifier: CONST | SIGNED | UNSIGNED | INLINE | PUBLIC | HEAP;
modAttr: MOD_ATTR_PREAMBLE LBRACKET attrLst RBRACKET;
fctAttr: FCT_ATTR_PREAMBLE LBRACKET attrLst RBRACKET;
attrLst: attr (COMMA attr)*;
attr: IDENTIFIER (DOT IDENTIFIER)* ASSIGN constant;
importStmt: IMPORT STRING_LIT (AS IDENTIFIER)? SEMICOLON;
returnStmt: RETURN assignExpr?;
breakStmt: BREAK INT_LIT?;
Expand Down Expand Up @@ -74,9 +78,9 @@ postfixUnaryExpr: atomicExpr | postfixUnaryExpr LBRACKET assignExpr RBRACKET | p
atomicExpr: constant | value | IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* | builtinCall | LPAREN assignExpr RPAREN;

// Values
value: functionCall | arrayInitialization | structInstantiation | NIL LESS dataType GREATER;
value: fctCall | arrayInitialization | structInstantiation | NIL LESS dataType GREATER;
constant: DOUBLE_LIT | INT_LIT | SHORT_LIT | LONG_LIT | CHAR_LIT | STRING_LIT | TRUE | FALSE;
functionCall: IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* (DOT IDENTIFIER)* (LESS typeLst GREATER)? LPAREN argLst? RPAREN;
fctCall: IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* (DOT IDENTIFIER)* (LESS typeLst GREATER)? LPAREN argLst? RPAREN;
arrayInitialization: LBRACE argLst? RBRACE;
structInstantiation: IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* (LESS typeLst GREATER)? LBRACE argLst? RBRACE;

Expand Down Expand Up @@ -135,7 +139,6 @@ SIZEOF: 'sizeof';
ALIGNOF: 'alignof';
LEN: 'len';
EXT: 'ext';
DLL: 'dll';
TRUE: 'true';
FALSE: 'false';

Expand Down Expand Up @@ -184,6 +187,8 @@ COMMA: ',';
DOT: '.';
SCOPE_ACCESS: '::';
ELLIPSIS: '...';
FCT_ATTR_PREAMBLE: '#';
MOD_ATTR_PREAMBLE: '#!';

// Regex tokens
DOUBLE_LIT: [-]?[0-9]*[.][0-9]+([eE][+-]?[0-9]+)?;
Expand Down
116 changes: 106 additions & 10 deletions src/ast/ASTBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ std::any ASTBuilder::visitEntry(SpiceParser::EntryContext *ctx) {
currentNode = entryNode->createChild<ImportStmtNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::ExtDeclContext *>(subTree); rule != nullptr) // ExtDecl
currentNode = entryNode->createChild<ExtDeclNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::ModAttrContext *>(subTree); rule != nullptr) // ModAttr
currentNode = entryNode->createChild<ModAttrNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

Expand All @@ -64,7 +66,9 @@ std::any ASTBuilder::visitMainFunctionDef(SpiceParser::MainFunctionDefContext *c

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::ParamLstContext *>(subTree); rule != nullptr) { // ArgLstDef
if (rule = dynamic_cast<SpiceParser::FctAttrContext *>(subTree); rule != nullptr) { // FctAttr
currentNode = mainFctDefNode->createChild<FctAttrNode>(CodeLoc(rule->start, filePath));
} else if (rule = dynamic_cast<SpiceParser::ParamLstContext *>(subTree); rule != nullptr) { // ArgLstDef
currentNode = mainFctDefNode->createChild<ParamLstNode>(CodeLoc(rule->start, filePath));
mainFctDefNode->takesArgs = true;
isParam = true;
Expand All @@ -89,7 +93,9 @@ std::any ASTBuilder::visitFunctionDef(SpiceParser::FunctionDefContext *ctx) {

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::SpecifierLstContext *>(subTree); rule != nullptr) // SpecifierLst
if (rule = dynamic_cast<SpiceParser::FctAttrContext *>(subTree); rule != nullptr) // FctAttr
currentNode = fctDefNode->createChild<FctAttrNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::SpecifierLstContext *>(subTree); rule != nullptr) // SpecifierLst
currentNode = fctDefNode->createChild<SpecifierLstNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::DataTypeContext *>(subTree); rule != nullptr) { // DataType
currentNode = fctDefNode->createChild<DataTypeNode>(CodeLoc(rule->start, filePath));
Expand Down Expand Up @@ -130,7 +136,9 @@ std::any ASTBuilder::visitProcedureDef(SpiceParser::ProcedureDefContext *ctx) {

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::SpecifierLstContext *>(subTree); rule != nullptr) // SpecifierLst
if (rule = dynamic_cast<SpiceParser::FctAttrContext *>(subTree); rule != nullptr) // FctAttr
currentNode = procDefNode->createChild<FctAttrNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::SpecifierLstContext *>(subTree); rule != nullptr) // SpecifierLst
currentNode = procDefNode->createChild<SpecifierLstNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::FctNameContext *>(subTree); rule != nullptr) { // FctName
currentNode = procDefNode->createChild<FctNameNode>(CodeLoc(rule->start, filePath));
Expand Down Expand Up @@ -366,12 +374,12 @@ std::any ASTBuilder::visitExtDecl(SpiceParser::ExtDeclContext *ctx) {

// Extract function name
extDeclNode->extFunctionName = getIdentifier(ctx->IDENTIFIER());
// Extract isDll
extDeclNode->isDll = ctx->DLL() != nullptr;

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::DataTypeContext *>(subTree); rule != nullptr) // DataType
if (rule = dynamic_cast<SpiceParser::FctAttrContext *>(subTree); rule != nullptr) // FctAttr
currentNode = extDeclNode->createChild<FctAttrNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::DataTypeContext *>(subTree); rule != nullptr) // DataType
currentNode = extDeclNode->createChild<DataTypeNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::TypeLstContext *>(subTree); rule != nullptr) { // TypeLst
currentNode = extDeclNode->createChild<TypeLstNode>(CodeLoc(rule->start, filePath));
Expand Down Expand Up @@ -948,6 +956,94 @@ std::any ASTBuilder::visitSpecifier(SpiceParser::SpecifierContext *ctx) {
return nullptr;
}

std::any ASTBuilder::visitModAttr(SpiceParser::ModAttrContext *ctx) {
auto modAttrNode = static_cast<ModAttrNode *>(currentNode);
modAttrNode->reserveChildren(ctx->children.size());
saveErrorMessage(modAttrNode, ctx);

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::AttrLstContext *>(subTree); rule != nullptr) // AttrLst
currentNode = modAttrNode->createChild<AttrLstNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

if (currentNode != modAttrNode) {
visit(rule);
currentNode = modAttrNode;
}
}
return nullptr;
}

std::any ASTBuilder::visitFctAttr(SpiceParser::FctAttrContext *ctx) {
auto fctAttrNode = static_cast<FctAttrNode *>(currentNode);
fctAttrNode->reserveChildren(ctx->children.size());
saveErrorMessage(fctAttrNode, ctx);

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::AttrLstContext *>(subTree); rule != nullptr) // AttrLst
currentNode = fctAttrNode->createChild<AttrLstNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

if (currentNode != fctAttrNode) {
visit(rule);
currentNode = fctAttrNode;
}
}
return nullptr;
}

std::any ASTBuilder::visitAttrLst(SpiceParser::AttrLstContext *ctx) {
auto attrLstNode = static_cast<AttrLstNode *>(currentNode);
attrLstNode->reserveChildren(ctx->children.size());
saveErrorMessage(attrLstNode, ctx);

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::AttrContext *>(subTree); rule != nullptr) // Attr
currentNode = attrLstNode->createChild<AttrNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

if (currentNode != attrLstNode) {
visit(rule);
currentNode = attrLstNode;
}
}
return nullptr;
}

std::any ASTBuilder::visitAttr(SpiceParser::AttrContext *ctx) {
auto attrNode = static_cast<AttrNode *>(currentNode);
attrNode->reserveChildren(ctx->children.size());
saveErrorMessage(attrNode, ctx);

// Extract key
for (const antlr4::tree::TerminalNode *keyFragment : ctx->IDENTIFIER()) {
if (!attrNode->key.empty())
attrNode->key += '.';
attrNode->key += keyFragment->getSymbol()->getText();
}

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::ConstantContext *>(subTree); rule != nullptr) // Constant
currentNode = attrNode->createChild<ConstantNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

if (currentNode != attrNode) {
visit(rule);
currentNode = attrNode;
}
}

return nullptr;
}

std::any ASTBuilder::visitImportStmt(SpiceParser::ImportStmtContext *ctx) {
auto importStmtNode = static_cast<ImportStmtNode *>(currentNode);
importStmtNode->reserveChildren(ctx->children.size());
Expand Down Expand Up @@ -1552,8 +1648,8 @@ std::any ASTBuilder::visitValue(SpiceParser::ValueContext *ctx) {

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::FunctionCallContext *>(subTree); rule != nullptr) // FunctionCall
currentNode = valueNode->createChild<FunctionCallNode>(CodeLoc(rule->start, filePath));
if (rule = dynamic_cast<SpiceParser::FctCallContext *>(subTree); rule != nullptr) // FctCall
currentNode = valueNode->createChild<FctCallNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::ArrayInitializationContext *>(subTree); rule != nullptr) // ArrayInitialization
currentNode = valueNode->createChild<ArrayInitializationNode>(CodeLoc(rule->start, filePath));
else if (rule = dynamic_cast<SpiceParser::StructInstantiationContext *>(subTree); rule != nullptr) // StructInstantiation
Expand Down Expand Up @@ -1626,8 +1722,8 @@ std::any ASTBuilder::visitConstant(SpiceParser::ConstantContext *ctx) {
return nullptr;
}

std::any ASTBuilder::visitFunctionCall(SpiceParser::FunctionCallContext *ctx) {
auto fctCallNode = static_cast<FunctionCallNode *>(currentNode);
std::any ASTBuilder::visitFctCall(SpiceParser::FctCallContext *ctx) {
auto fctCallNode = static_cast<FctCallNode *>(currentNode);
fctCallNode->reserveChildren(ctx->children.size());
saveErrorMessage(fctCallNode, ctx);

Expand Down
6 changes: 5 additions & 1 deletion src/ast/ASTBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class ASTBuilder : private CompilerPass, public SpiceVisitor {
std::any visitDeclStmt(SpiceParser::DeclStmtContext *ctx) override;
std::any visitSpecifierLst(SpiceParser::SpecifierLstContext *ctx) override;
std::any visitSpecifier(SpiceParser::SpecifierContext *ctx) override;
std::any visitFctAttr(SpiceParser::FctAttrContext *ctx) override;
std::any visitModAttr(SpiceParser::ModAttrContext *ctx) override;
std::any visitAttrLst(SpiceParser::AttrLstContext *ctx) override;
std::any visitAttr(SpiceParser::AttrContext *ctx) override;
std::any visitImportStmt(SpiceParser::ImportStmtContext *ctx) override;
std::any visitReturnStmt(SpiceParser::ReturnStmtContext *ctx) override;
std::any visitBreakStmt(SpiceParser::BreakStmtContext *ctx) override;
Expand Down Expand Up @@ -92,7 +96,7 @@ class ASTBuilder : private CompilerPass, public SpiceVisitor {
std::any visitPostfixUnaryExpr(SpiceParser::PostfixUnaryExprContext *ctx) override;
std::any visitAtomicExpr(SpiceParser::AtomicExprContext *ctx) override;
std::any visitValue(SpiceParser::ValueContext *ctx) override;
std::any visitFunctionCall(SpiceParser::FunctionCallContext *ctx) override;
std::any visitFctCall(SpiceParser::FctCallContext *ctx) override;
std::any visitArrayInitialization(SpiceParser::ArrayInitializationContext *ctx) override;
std::any visitStructInstantiation(SpiceParser::StructInstantiationContext *ctx) override;
std::any visitConstant(SpiceParser::ConstantContext *ctx) override;
Expand Down
13 changes: 12 additions & 1 deletion src/ast/ASTNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ 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;
}

AttrNode *AttrLstNode::getAttrByName(const char *key) const { return getAttrsByName(key).back(); }

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

bool AssertStmtNode::returnsOnAllControlPaths(bool *overrideUnreachable) const {
const bool returns = hasCompileTimeValue() && !compileTimeValue.boolValue;
*overrideUnreachable |= returns;
Expand Down Expand Up @@ -129,7 +140,7 @@ const CompileTimeValue &LogicalOrExprNode::getCompileTimeValue() const {
*
* @return Has return value receiver or not
*/
bool FunctionCallNode::hasReturnValueReceiver() const {
bool FctCallNode::hasReturnValueReceiver() const {
ASTNode *node = parent;
while (!node->isAssignExpr()) {
if (node->children.size() > 1)
Expand Down
Loading

0 comments on commit d266710

Please sign in to comment.