Skip to content

Commit

Permalink
Function pointers (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed May 5, 2023
1 parent 913c4b6 commit 2924e29
Show file tree
Hide file tree
Showing 80 changed files with 1,411 additions and 472 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ jobs:
uses: actions/cache@v3
with:
path: /home/runner/work/spice/llvm
key: llvm-16.0.2-evict1
key: llvm-16.0.3

- name: Setup LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
cd ..
rm -rf llvm
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project llvm
mkdir ./llvm/build
cd ./llvm/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja ../llvm
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ jobs:
uses: actions/cache@v3
with:
path: /home/runner/work/spice/llvm
key: llvm-16.0.2-evict1
key: llvm-16.0.3

- name: Setup LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
cd ..
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project llvm
mkdir ./llvm/build
cd ./llvm/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja ../llvm
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ jobs:
uses: actions/cache@v3
with:
path: /home/runner/work/spice/spice/llvm
key: llvm-16.0.2-linux-x64-evict1
key: llvm-16.0.3-linux-x64

- name: Setup LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project.git llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project.git llvm
mkdir ./llvm/build
cd ./llvm/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja -Wno-dev -Wattributes ../llvm
Expand Down Expand Up @@ -96,7 +96,7 @@ jobs:
choco uninstall llvm --all-versions
Remove-Item -Recurse -Force C:/Strawberry
echo "Uninstalling done."
Invoke-WebRequest https://chillibits.com/files/gh/mingw64_12_2_0.7z -OutFile mingw64.7z
Invoke-WebRequest https://chillibits.com/files/gh/mingw64_13_1_0.7z -OutFile mingw64.7z
echo "Downloading done."
7z x mingw64.7z -oC:\mingw64
echo "Unpacking done."
Expand All @@ -114,12 +114,12 @@ jobs:
uses: actions/cache@v3
with:
path: D:/a/spice/spice/llvm
key: llvm-16.0.2-win-x64-evict1
key: llvm-16.0.3-win-x64

- name: Setup LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project.git llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project.git llvm
setx /M PATH "%PATH%;C:\mingw64\mingw64\bin"
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
echo "Adding MinGW to path done."
Expand Down
2 changes: 1 addition & 1 deletion .run/spice.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d -ir ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="RUN_TESTS" value="OFF" />
<env name="SPICE_STD_DIR" value="$PROJECT_DIR$/std" />
Expand Down
2 changes: 1 addition & 1 deletion dev-setup.bat
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ echo done.

REM - Clone LLVM
echo [Step 2] Cloning LLVM (Could take a while) ...
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project llvm
echo done.

REM - Build LLVM
Expand Down
2 changes: 1 addition & 1 deletion dev-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ colored_echo "done."

# Clone LLVM
colored_echo "[Step 2] Cloning LLVM (Could take a while) ... "
git clone --depth 1 --branch llvmorg-16.0.2 https://github.com/llvm/llvm-project llvm
git clone --depth 1 --branch llvmorg-16.0.3 https://github.com/llvm/llvm-project llvm
colored_echo "done."

# Build LLVM
Expand Down
25 changes: 22 additions & 3 deletions docs/docs/language/builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ printf("Here is a string: %s.\nAnd here is a double: %f", "Demo", 1.123);
```

## The `sizeof` builtin
Sizeof returns the internal size of a variable or a constant in bits. To get the size in bytes, simply divide the result by 8.
Sizeof returns the internal size of a variable, constant or type in bits. To get the size in bytes, simply divide the result by 8.

### Signature
`int sizeof(<any variable>)`
Expand All @@ -53,8 +53,6 @@ Sizeof returns the internal size of a variable or a constant in bits. To get the
`any variable`: Variable or constant of any type.
`any type`: Any data type

If the variable is a pointer type, the size of the contained type is being returned.

### Usage example
```spice
sizeof(12); // 32
Expand All @@ -66,6 +64,27 @@ sizeof(intArray); // 9 * 32 = 288
sizeof("Hello World!"); // 64 (Strings are Char pointers internally)
```

## The `alignof` builtin
Alignof returns the alignment of a variable, constant or type in bytes. To get the alignment in bits, simply multiply the result by 8.

### Signature
`int alignof(<any variable>)`
`int alignof(type <any type>)`

`any variable`: Variable or constant of any type.
`any type`: Any data type

### Usage example
```spice
alignof(12); // 4
sizeof(type int) // 4
DemoStruct s = DemoStruct{123, 56l, 12s, "String"};
sizeof(intArray); // 8 (the long value is the widest type in the struct)
sizeof("Hello World!"); // 8 (Strings are Char pointers internally)
```

## The `len` builtin
Len returns the length of a Spice array in items.

Expand Down
16 changes: 12 additions & 4 deletions docs/docs/language/main-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
title: Main Function
---

The `main` function is the entry point of any Spice program. When the system runs your compiled executable, the `main` function is the first function, which is called by the system. Therefore, it is mandatory and must have the signature `main(): int` or `main(int argc, string[] argv): int` for retrieving command line arguments. The main function has a return value of type `int`, which represents the return code / exit code / status code of the executable.
The `main` function is the entry point of any Spice program. When the system runs your compiled executable, the `main` function is
the first function, which is called by the system. Therefore, it is mandatory and must have the signature `int main()` or
`int main(int argc, string[] argv)` for retrieving command line arguments. The main function has a return value of type `int`,
which represents the return code / exit code / status code of the executable.

## Usage

Expand All @@ -13,12 +16,17 @@ f<int> main() {
}
```

Like any other [function](../functions), the `main` function automatically declares the variable `result` of the same type as the function return type. You then have the option to use either the return statement (e.g.: `return 1;`) or assign a value to the `result` variable. If you choose the second option, the value of this variable gets returned at the end of the function body. Unlike normal functions, the `main` function has `0` as the initial value assigned to the `result` variable for reasons of convenience and to not always have to write `return 0;` or `result = 0;` to exit the program with a positive exit code.
Like any other [function](../functions), the `main` function automatically declares the variable `result` of the same type as the
function return type. You then have the option to use either the return statement (e.g.: `return 1;`) or assign a value to the
`result` variable. If you choose the second option, the value of this variable gets returned at the end of the function body.
Unlike normal functions, the `main` function has `0` as the initial value assigned to the `result` variable for reasons of
convenience and to not always have to write `return 0;` or `result = 0;` to exit the program with a positive exit code.

!!! info "Hello World program"
Now, as you know how to start a program in Spice, you may like to write your first Spice program. Visit the guide for the [Hello World example](../hello-world) to get started!
Now, as you know how to start a program in Spice, you may like to write your first Spice program. Visit the guide for the
[Hello World example](../hello-world) to get started!

## CLI Arguments
## Command Line Arguments
Spice programs can accept command line arguments similar you would write it in C:

```spice
Expand Down
10 changes: 5 additions & 5 deletions docs/docs/language/structs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
title: Structs
---

Spice offers support for C-like structs to collect variables in groups.
Spice offers support for C-like structs to aggregate variables in groups.

## Usage

To declare a `struct`, list all field names with types like this:
To declare a `struct`, list all field types with names like this:

```spice
type Person struct {
Expand All @@ -16,10 +16,10 @@ type Person struct {
}
```

this structs can only be declared in the global scope, in parallel to [functions](../functions) and [procedures](../procedures).
Structs can only be declared in the global scope, like [functions](../functions) and [procedures](../procedures).

For creating an instance of the declared struct, you can pass values for all (or none) fields in curly braces. To access fields of
the instance, you can address the fields by their names:
For creating an instance of the declared struct, you can pass values for either all or none of the fields in curly braces.
To access a field of the instance, you can address the field by its name:

```spice
type Person struct {
Expand Down
14 changes: 12 additions & 2 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
ext f<byte*> malloc(int);

f<int> test(string input) {
return 12;
}

p invoke(f<int>(string) fctPtr) {
fctPtr("string");
}

f<int> main() {
long alignment = alignof(type Test);
printf("Alignment: %d", alignment);
f<int>(string) testFct = test;
invoke(testFct);
}
3 changes: 2 additions & 1 deletion src/Spice.g4
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ structInstantiation: IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* (LESS typeLst GREATER

// Types
dataType: specifierLst? baseDataType (MUL | BITWISE_AND | LBRACKET (INT_LIT | IDENTIFIER)? RBRACKET)*;
baseDataType: TYPE_DOUBLE | TYPE_INT | TYPE_SHORT | TYPE_LONG | TYPE_BYTE | TYPE_CHAR | TYPE_STRING | TYPE_BOOL | TYPE_DYN | customDataType;
baseDataType: TYPE_DOUBLE | TYPE_INT | TYPE_SHORT | TYPE_LONG | TYPE_BYTE | TYPE_CHAR | TYPE_STRING | TYPE_BOOL | TYPE_DYN | customDataType | functionDataType;
customDataType: IDENTIFIER (SCOPE_ACCESS IDENTIFIER)* (LESS typeLst GREATER)?;
functionDataType: (P | F LESS dataType GREATER) LPAREN typeLst? RPAREN;

// Shorthands
assignOp: ASSIGN | PLUS_EQUAL | MINUS_EQUAL | MUL_EQUAL | DIV_EQUAL | REM_EQUAL | SHL_EQUAL | SHR_EQUAL | AND_EQUAL | OR_EQUAL | XOR_EQUAL;
Expand Down
26 changes: 26 additions & 0 deletions src/ast/ASTBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,9 @@ std::any ASTBuilder::visitBaseDataType(SpiceParser::BaseDataTypeContext *ctx) {
if (rule = dynamic_cast<SpiceParser::CustomDataTypeContext *>(subTree); rule != nullptr) { // CustomDataType
baseDataTypeNode->type = BaseDataTypeNode::TY_CUSTOM;
currentNode = baseDataTypeNode->createChild<CustomDataTypeNode>(CodeLoc(rule->start, filePath));
} else if (rule = dynamic_cast<SpiceParser::FunctionDataTypeContext *>(subTree); rule != nullptr) { // FunctionDataType
baseDataTypeNode->type = BaseDataTypeNode::TY_FUNCTION;
currentNode = baseDataTypeNode->createChild<FunctionDataTypeNode>(CodeLoc(rule->start, filePath));
} else if (auto t1 = dynamic_cast<TerminalNode *>(subTree); t1->getSymbol()->getType() == SpiceParser::TYPE_DOUBLE)
baseDataTypeNode->type = BaseDataTypeNode::TYPE_DOUBLE;
else if (auto t2 = dynamic_cast<TerminalNode *>(subTree); t2->getSymbol()->getType() == SpiceParser::TYPE_INT)
Expand Down Expand Up @@ -1873,6 +1876,29 @@ std::any ASTBuilder::visitCustomDataType(SpiceParser::CustomDataTypeContext *ctx
return nullptr;
}

std::any ASTBuilder::visitFunctionDataType(SpiceParser::FunctionDataTypeContext *ctx) {
auto functionDataTypeNode = static_cast<FunctionDataTypeNode *>(currentNode);
functionDataTypeNode->reserveChildren(ctx->children.size());
saveErrorMessage(functionDataTypeNode, ctx);

for (ParserRuleContext::ParseTree *subTree : ctx->children) {
ParserRuleContext *rule;
if (rule = dynamic_cast<SpiceParser::DataTypeContext *>(subTree); rule != nullptr) { // DataType
currentNode = functionDataTypeNode->createChild<DataTypeNode>(CodeLoc(rule->start, filePath));
functionDataTypeNode->isFunction = true;
} else if (rule = dynamic_cast<SpiceParser::TypeLstContext *>(subTree); rule != nullptr) // TypeLst
currentNode = functionDataTypeNode->createChild<TypeLstNode>(CodeLoc(rule->start, filePath));
else
assert(dynamic_cast<TerminalNode *>(subTree)); // Fail if we did not get a terminal

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

std::any ASTBuilder::visitAssignOp(SpiceParser::AssignOpContext *ctx) {
auto assignExprNode = static_cast<AssignExprNode *>(currentNode);
assignExprNode->reserveChildren(ctx->children.size());
Expand Down
1 change: 1 addition & 0 deletions src/ast/ASTBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class ASTBuilder : private CompilerPass, public SpiceVisitor {
std::any visitDataType(SpiceParser::DataTypeContext *ctx) override;
std::any visitBaseDataType(SpiceParser::BaseDataTypeContext *ctx) override;
std::any visitCustomDataType(SpiceParser::CustomDataTypeContext *ctx) override;
std::any visitFunctionDataType(SpiceParser::FunctionDataTypeContext *ctx) override;
std::any visitAssignOp(SpiceParser::AssignOpContext *ctx) override;
std::any visitPrefixUnaryOp(SpiceParser::PrefixUnaryOpContext *ctx) override;
std::any visitOverloadableOp(SpiceParser::OverloadableOpContext *ctx) override;
Expand Down
46 changes: 38 additions & 8 deletions src/ast/ASTNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ class FctDefNode : public ASTNode {
[[nodiscard]] std::string getSymbolTableEntryName() const {
const FctNameNode *functionName = getChild<FctNameNode>();
assert(functionName != nullptr);
return functionName->name + ":" + codeLoc.toPrettyLine();
return functionName->name + ":" + codeLoc.toPrettyLineAndColumn();
}
[[nodiscard]] bool returnsOnAllControlPaths(bool *overrideUnreachable) const override;
std::vector<Function *> *getFctManifestations() override { return &fctManifestations; }
Expand Down Expand Up @@ -332,7 +332,7 @@ class ProcDefNode : public ASTNode {
[[nodiscard]] std::string getSymbolTableEntryName() const {
const FctNameNode *functionName = getChild<FctNameNode>();
assert(functionName != nullptr);
return functionName->name + ":" + codeLoc.toPrettyLine();
return functionName->name + ":" + codeLoc.toPrettyLineAndColumn();
}
bool returnsOnAllControlPaths(bool *overrideUnreachable) const override;
std::vector<Function *> *getFctManifestations() override { return &procManifestations; }
Expand Down Expand Up @@ -1589,16 +1589,24 @@ class ConstantNode : public ASTNode {

class FunctionCallNode : public ASTNode {
public:
enum FunctionCallType : uint8_t { TYPE_ORDINARY, TYPE_METHOD, TYPE_CTOR, TYPE_FCT_PTR };

// Structs
struct FunctionCallData {
bool isConstructorCall = false;
bool isMethodCall = false;
// Members
FunctionCallType callType = TYPE_ORDINARY;
bool isImported = false;
bool isDownCall = false;
SymbolType thisType = SymbolType(TY_DYN); // Is filled if method or ctor call
std::vector<SymbolType> argTypes;
Function *callee = nullptr;
Scope *calleeParentScope = nullptr;

// Methods
[[nodiscard]] bool isOrdinaryCall() const { return callType == TYPE_ORDINARY; }
[[nodiscard]] bool isMethodCall() const { return callType == TYPE_METHOD; }
[[nodiscard]] bool isCtorCall() const { return callType == TYPE_CTOR; }
[[nodiscard]] bool isFctPtrCall() const { return callType == TYPE_FCT_PTR; }
};

// Constructors
Expand Down Expand Up @@ -1716,7 +1724,8 @@ class BaseDataTypeNode : public ASTNode {
TYPE_STRING,
TYPE_BOOL,
TYPE_DYN,
TY_CUSTOM
TY_CUSTOM,
TY_FUNCTION
};

// Constructors
Expand All @@ -1728,6 +1737,7 @@ class BaseDataTypeNode : public ASTNode {

// Public get methods
[[nodiscard]] CustomDataTypeNode *customDataType() const { return getChild<CustomDataTypeNode>(); }
[[nodiscard]] FunctionDataTypeNode *functionDataType() const { return getChild<FunctionDataTypeNode>(); }

// Public members
Type type = TYPE_NONE;
Expand All @@ -1754,9 +1764,29 @@ class CustomDataTypeNode : public ASTNode {
std::string fqTypeName;
std::vector<std::string> typeNameFragments;
std::vector<SymbolTableEntry *> customTypes;
bool isParamType = false;
bool isFieldType = false;
bool isReturnType = false;
};

// =================================================== FunctionDataTypeNode ======================================================

class FunctionDataTypeNode : public ASTNode {
public:
// Constructors
using ASTNode::ASTNode;

// Visitor methods
std::any accept(AbstractASTVisitor *visitor) override { return visitor->visitFunctionDataType(this); }
std::any accept(ParallelizableASTVisitor *visitor) const override { return visitor->visitFunctionDataType(this); }

// Public get methods
[[nodiscard]] DataTypeNode *returnType() const { return getChild<DataTypeNode>(); }
[[nodiscard]] TypeLstNode *paramTypeLst() const { return getChild<TypeLstNode>(); }

// Util methods
void customItemsInitialization(size_t manifestationCount) override { customTypes.resize(manifestationCount, nullptr); }

// Public members
std::vector<SymbolTableEntry *> customTypes;
bool isFunction = false; // Function or procedure
};

} // namespace spice::compiler
2 changes: 2 additions & 0 deletions src/ast/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,6 @@ std::any ASTVisitor::visitBaseDataType(BaseDataTypeNode *node) { return visitChi

std::any ASTVisitor::visitCustomDataType(CustomDataTypeNode *node) { return visitChildren(node); }

std::any ASTVisitor::visitFunctionDataType(FunctionDataTypeNode *node) { return visitChildren(node); }

} // namespace spice::compiler
1 change: 1 addition & 0 deletions src/ast/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class ASTVisitor : public AbstractASTVisitor {
std::any visitDataType(DataTypeNode *node) override;
std::any visitBaseDataType(BaseDataTypeNode *node) override;
std::any visitCustomDataType(CustomDataTypeNode *node) override;
std::any visitFunctionDataType(FunctionDataTypeNode *node) override;
};

} // namespace spice::compiler
Loading

0 comments on commit 2924e29

Please sign in to comment.