├── .tito ├── packages │ ├── memeasm │ └── .readme └── tito.props ├── nfpm ├── postinstall.sh ├── postremove.sh ├── generateInstallers.sh └── nfpm.yaml ├── images ├── logo.png ├── logo_square.png ├── logo.svg └── logo_square.svg ├── examples ├── Makefile ├── rot13 │ ├── rot13_test.c │ ├── Makefile │ └── rot13.memeasm ├── toupper │ ├── Makefile │ ├── toupper_test.c │ └── toupper.memeasm ├── Stalinsort │ ├── Makefile │ ├── stalinsort_test.c │ └── stalin-sort.memeasm ├── alphabet.memeasm ├── HelloWorld.memeasm ├── reverse-string.memeasm ├── is_nice.memeasm ├── the-link.memeasm └── brainfuck.memeasm ├── .github ├── dependabot.yml ├── workflows │ ├── runnable_example.memeasm │ ├── multiple_files │ │ ├── Makefile │ │ ├── main.memeasm │ │ └── function.memeasm │ ├── example.memeasm │ ├── create_installers.yml │ ├── build_test.yml │ ├── compile_and_run_test.yml │ ├── codeql-analysis.yml │ ├── publish-aur.yml │ └── compilation_test.yml └── ISSUE_TEMPLATE │ ├── new-command-suggestion.md │ └── bug_report.md ├── .gitignore ├── PKGBUILD ├── memeasm.spec ├── AUTHORS.md ├── compiler ├── compiler.h ├── translator │ ├── translator.h │ └── translator.c ├── analyser │ ├── parameters.h │ ├── analyser.h │ ├── jumpMarkers.h │ ├── randomCommands.h │ ├── comparisons.h │ ├── functions.h │ ├── analysisHelper.h │ ├── jumpMarkers.c │ ├── comparisons.c │ ├── functions.c │ ├── analyser.c │ ├── randomCommands.c │ └── analysisHelper.c ├── parser │ ├── functionParser.h │ ├── parser.h │ ├── fileParser.h │ ├── parser.c │ ├── functionParser.c │ └── fileParser.c ├── logger │ ├── log.h │ └── log.c ├── commands.h ├── memeasm.c └── compiler.c ├── install_windows.bat ├── Makefile └── README.md /.tito/packages/memeasm: -------------------------------------------------------------------------------- 1 | 1.6-1 ./ 2 | -------------------------------------------------------------------------------- /nfpm/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo make install -------------------------------------------------------------------------------- /nfpm/postremove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo make uninstall 3 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kammt/MemeAssembly/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/logo_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kammt/MemeAssembly/HEAD/images/logo_square.png -------------------------------------------------------------------------------- /nfpm/generateInstallers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | nfpm package -p deb -t "../" 3 | nfpm package -p rpm -t "../" -------------------------------------------------------------------------------- /.tito/tito.props: -------------------------------------------------------------------------------- 1 | [buildconfig] 2 | builder = tito.builder.Builder 3 | tagger = tito.tagger.VersionTagger 4 | changelog_do_not_remove_cherrypick = 0 5 | changelog_format = %s (%ae) 6 | -------------------------------------------------------------------------------- /.tito/packages/.readme: -------------------------------------------------------------------------------- 1 | the .tito/packages directory contains metadata files 2 | named after their packages. Each file has the latest tagged 3 | version and the project's relative directory. 4 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: 4 | for file in *.memeasm ; do memeasm -o $$(basename $$file .memeasm) $$file ; done 5 | for dir in */ ; do cd $$dir && make && cd .. ; done 6 | 7 | -------------------------------------------------------------------------------- /examples/rot13/rot13_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void rot13(char *str); 4 | 5 | int main() { 6 | char str[] = "Nu, V frr lbh'er n zna bs phygher nf jryy."; 7 | printf("Before rot13: %s\n", str); 8 | rot13(str); 9 | printf("After rot13: %s\n", str); 10 | } 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Set update schedule for GitHub Actions 2 | 3 | version: 2 4 | updates: 5 | 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | # Check for updates to GitHub Actions every week 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /examples/rot13/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= 3 | 4 | ifeq ($(shell uname -s),Linux) 5 | CFLAGS+=-gstabs -no-pie 6 | endif 7 | 8 | .PHONY: all clean 9 | 10 | all: rot13_test.c 11 | memeasm -g -O -o rot13.o rot13.memeasm 12 | $(CC) -o rot13 $^ rot13.o $(CFLAGS) 13 | 14 | clean: 15 | rm rot13 16 | -------------------------------------------------------------------------------- /examples/toupper/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= 3 | 4 | ifeq ($(shell uname -s),Linux) 5 | CFLAGS += -gstabs -no-pie 6 | endif 7 | 8 | .PHONY: all clean 9 | 10 | all: toupper_test.c 11 | memeasm -g -O -o toupper.o toupper.memeasm 12 | $(CC) -o toupper $^ toupper.o $(CFLAGS) 13 | 14 | clean: 15 | rm toupper 16 | -------------------------------------------------------------------------------- /examples/Stalinsort/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS= 3 | 4 | ifeq ($(shell uname -s),Linux) 5 | CFLAGS += -gstabs -no-pie 6 | endif 7 | 8 | .PHONY: all clean 9 | 10 | all: stalinsort_test.c 11 | memeasm -O -o stalinsort.o stalin-sort.memeasm -g 12 | $(CC) -o stalinsort $^ stalinsort.o $(CFLAGS) 13 | 14 | clean: 15 | rm stalinsort 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Executable 2 | ./memeasm 3 | memeasm.exe 4 | 5 | #Temporary files for local testing 6 | tmp.asm 7 | example.S 8 | example.memeasm 9 | example.exe 10 | tmp 11 | tmp.o 12 | /examples/Stalinsort/stalinsort.S 13 | test 14 | test.S 15 | 16 | #IDE files 17 | **/.idea/ 18 | **/.vscode/ 19 | 20 | #compiledb used by clang tooling 21 | compile_commands.json 22 | -------------------------------------------------------------------------------- /.github/workflows/runnable_example.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | ebx is brilliant, but I like 64 3 | sneak 100 eax 4 | 5 | upgrade 6 | upvote ebx 7 | upvote eax 8 | corporate needs you to find the difference between eax and 25 9 | fuck go back 10 | 11 | they're the same picture 12 | I see this as an absolute win 13 | -------------------------------------------------------------------------------- /examples/alphabet.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | eax is brilliant, but I like 65 3 | 4 | upgrade 5 | what can I say except al 6 | what can I say except \s 7 | upvote eax 8 | corporate needs you to find the difference between eax and 91 9 | fuck go back 10 | 11 | they're the same picture 12 | what can I say except \n 13 | 14 | I see this as an absolute win 15 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Tobias Kamm 2 | pkgname=memeassembly 3 | pkgver=VERSION_STRING 4 | pkgrel=PKGREL 5 | pkgdesc="A Meme-based programming language" 6 | arch=('x86_64') 7 | url="https://kammt.github.io/MemeAssembly/#/" 8 | license=('GPL3') 9 | depends=('gcc') 10 | source=(https://github.com/kammt/MemeAssembly/archive/refs/tags/v$pkgver.tar.gz) 11 | sha256sums=('256SUM') 12 | 13 | package() { 14 | cd "MemeAssembly-$pkgver" 15 | 16 | make DESTDIR="$pkgdir" install 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/multiple_files/Makefile: -------------------------------------------------------------------------------- 1 | # This should work just fine 2 | main: main.memeasm function.memeasm 3 | memeasm -o $@ $^ 4 | 5 | .PHONY: fail_undefined fail_no_main clean 6 | 7 | # This should fail as the function referenced in main.memeasm is defined in a file that is not given as input file 8 | fail_undefined: main.memeasm 9 | memeasm -o $@ $^ 10 | 11 | # This should fail as we don't have a main function 12 | fail_no_main: function.memeasm 13 | memeasm -o $@ $^ 14 | 15 | clean: 16 | rm -f main fail_undefined fail_no_main 17 | -------------------------------------------------------------------------------- /examples/HelloWorld.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | what can I say except H 3 | what can I say except e 4 | what can I say except l 5 | what can I say except l 6 | what can I say except o 7 | what can I say except \s 8 | what can I say except W 9 | what can I say except o 10 | what can I say except r 11 | what can I say except l 12 | what can I say except d 13 | what can I say except ! 14 | what can I say except \n 15 | 16 | I see this as an absolute win 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-command-suggestion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Command Suggestion 3 | about: Propose a new command 4 | title: "[Command suggestion]" 5 | labels: enhancement, new command 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Command Syntax:** (e.g. `stonks [register]`) 11 | 12 | 13 | **Command Description:** 14 | 15 | 16 | Which x86_64 Assembly instruction(s) would be used to translate this command? 17 | 18 | 19 | What could this command be useful for? 20 | 21 | 22 | What Meme is your command based on? (You can also provide a link to e.g. its KnowYourMeme page) 23 | -------------------------------------------------------------------------------- /.github/workflows/example.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun function 2 | ebx is brilliant, but I like 64 3 | parry 20 you filthy casual ebx 4 | sneak 100 dx 5 | guess I'll die or draw 25 6 | guess I'll die 7 | upgrade 8 | stonks rax 9 | not stonks rax 10 | upvote edx 11 | they're the same picture 12 | corporate needs you to find the difference between eax and 48 13 | fuck go back 14 | downvote ecx 15 | eax units are ready, with 69 more well on the way 16 | confused stonks 17 | bitconneeeeeeect eax 155 18 | I see this as an absolute win 19 | -------------------------------------------------------------------------------- /nfpm/nfpm.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # check https://nfpm.goreleaser.com/configuration for detailed usage 3 | # 4 | name: "memeasm" 5 | arch: "amd64" 6 | platform: "linux" 7 | version: VERSION_STRING 8 | section: "default" 9 | priority: "extra" 10 | depends: 11 | - gcc 12 | - make 13 | maintainer: "Tobias Kamm " 14 | description: | 15 | A Meme-based programming language 16 | homepage: "https://kammt.github.io/MemeAssembly" 17 | license: "GPLv3" 18 | # Contents to add to the package 19 | # This can be binaries or any other files. 20 | contents: 21 | - src: ../compiler 22 | dst: /compiler 23 | - src: ../Makefile 24 | dst: Makefile 25 | scripts: 26 | postinstall: ./postinstall.sh 27 | postremove: ./postremove.sh 28 | -------------------------------------------------------------------------------- /memeasm.spec: -------------------------------------------------------------------------------- 1 | Name: memeasm 2 | Version: 1.6 3 | Release: 1%{?dist} 4 | Summary: A Meme-based programming language 5 | 6 | License: GPLv3 7 | URL: https://github.com/kammt/MemeAssembly 8 | Source0: %{name}-%{version}.tar.gz 9 | 10 | BuildRequires: gcc 11 | BuildRequires: make 12 | Requires: gcc 13 | 14 | %description 15 | The compiler for MemeAssembly, a Meme-based programming language. 16 | Learn more at https://kammt.github.io/MemeAssembly. 17 | 18 | %prep 19 | %autosetup 20 | 21 | 22 | %build 23 | %global debug_package %{nil} 24 | %make_build 25 | 26 | 27 | %install 28 | %make_install 29 | 30 | 31 | %files 32 | %license LICENSE 33 | /usr/local/bin/memeasm 34 | 35 | 36 | %changelog 37 | * Sun May 07 2023 tobias 1.6-1 38 | - new package built with tito 39 | 40 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Project Authors 2 | ## Main Contributor 3 | - Tobias Kamm 4 | 5 | ## Other contributors 6 | - [xarantolus](https://github.com/xarantolus) 7 | - Implementation of the "what can I say except [...]" command in v0.1 8 | - Suggested the idea of Monke Jump Markers 9 | - Bug reporting 10 | - [aengelke](https://github.com/aengelke) 11 | - Fixed some buffer overruns 12 | - Adapted the compilation to no longer use temporary files 13 | - Implementation of toupper_str in MemeASM 14 | - [M3L73D](https://github.com/M3L73D) 15 | - Implementation of rot13 in MemeASM 16 | - Added a "Nice"-message that is printed by the compiler whenever 69 or 420 are used in the code 17 | - [RobinMarchart](https://github.com/RobinMarchart) 18 | - Implementation of the "it's a trap" command 19 | 20 | ## Special thanks 21 | - [xarantolus](https://github.com/xarantolus) for creating the [VSCode Extension for MemeAssembly](https://github.com/xarantolus/MemeAssembly-vscode) 22 | -------------------------------------------------------------------------------- /.github/workflows/multiple_files/main.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | what can I say except T 3 | what can I say except h 4 | what can I say except i 5 | what can I say except s 6 | what can I say except space 7 | what can I say except i 8 | what can I say except s 9 | what can I say except space 10 | what can I say except i 11 | what can I say except n 12 | what can I say except space 13 | what can I say except m 14 | what can I say except a 15 | what can I say except i 16 | what can I say except n 17 | what can I say except . 18 | what can I say except m 19 | what can I say except e 20 | what can I say except m 21 | what can I say except e 22 | what can I say except a 23 | what can I say except s 24 | what can I say except m 25 | what can I say except \n 26 | 27 | function: whomst has summoned the almighty one 28 | return to monke uaaua 29 | 30 | I see this as an absolute win 31 | -------------------------------------------------------------------------------- /compiler/compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef COMPILER_H 21 | #define COMPILER_H 22 | 23 | #include 24 | #include 25 | #include "commands.h" 26 | 27 | _Noreturn void compile(struct compileState compileState, char* outputFileName); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /compiler/translator/translator.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_TRANSLATOR_H 21 | #define MEMEASSEMBLY_TRANSLATOR_H 22 | 23 | #include "../commands.h" 24 | 25 | #include 26 | 27 | void writeToFile(struct compileState* compileState, FILE *outputFile); 28 | 29 | #endif //MEMEASSEMBLY_TRANSLATOR_H 30 | -------------------------------------------------------------------------------- /compiler/analyser/parameters.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_PARAMETERS_H 21 | #define MEMEASSEMBLY_PARAMETERS_H 22 | 23 | #include "../commands.h" 24 | 25 | void checkParameters(struct parsedCommand *parsedCommand, char* inputFileName, struct compileState* compileState); 26 | 27 | #endif //MEMEASSEMBLY_PARAMETERS_H 28 | -------------------------------------------------------------------------------- /compiler/parser/functionParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_FUNCTIONPARSER_H 21 | #define MEMEASSEMBLY_FUNCTIONPARSER_H 22 | 23 | #include "parser.h" 24 | #include "fileParser.h" 25 | 26 | void parseFunctions(struct file* fileStruct, struct commandsArray commandsArray, struct compileState* compileState); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /compiler/parser/parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "../commands.h" 24 | 25 | #ifndef MEMEASSEMBLY_PARSER_H 26 | #define MEMEASSEMBLY_PARSER_H 27 | 28 | void parseFile(struct file* fileStruct, FILE* inputFile, struct compileState* compileState); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the MemeASM compiler 4 | title: "[Bug]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 |
15 | Code example or used MemeASM code 16 |
17 | 
18 | 
19 | 20 | Used compiler flags[e.g. -O-1 -S]: 21 | 22 | 23 | **Debug Output** 24 | 25 |
26 |
27 | 
28 | 
29 | 30 | 31 | **Screenshots** 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | **System Info (please complete the following information):** 35 | - Linux Distribution: [e.g. Arch Linux] 36 | - GCC Version: [e.g. 11.1.0] 37 | - MemeASM Compiler Version: [e.g. 1.0.1] 38 | - Installation method: [e.g. Makefile, deb-package etc.] 39 | -------------------------------------------------------------------------------- /examples/toupper/toupper_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | #include 20 | 21 | extern void toupper_str(char* str); 22 | 23 | int main() { 24 | char str[] = "The qu1ck BR0wN FoX juMpZ ov3r ThE l4zY dog. Hel1, Y3aH!"; 25 | printf("Before toupper_str: %s\n", str); 26 | toupper_str(str); 27 | printf("After toupper_str: %s\n", str); 28 | } 29 | -------------------------------------------------------------------------------- /compiler/analyser/analyser.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_ANALYSER_H 21 | #define MEMEASSEMBLY_ANALYSER_H 22 | 23 | #include "../commands.h" 24 | #include "../compiler.h" 25 | 26 | #include "comparisons.h" 27 | #include "functions.h" 28 | #include "jumpMarkers.h" 29 | #include "parameters.h" 30 | #include "randomCommands.h" 31 | 32 | void analyseCommands(struct compileState* compileState); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /compiler/analyser/jumpMarkers.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_JUMPMARKERS_H 21 | #define MEMEASSEMBLY_JUMPMARKERS_H 22 | 23 | #include "../commands.h" 24 | 25 | void analyseMonkeMarkers(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 26 | void analyseJumpMarkers(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 27 | 28 | #endif //MEMEASSEMBLY_JUMPMARKERS_H 29 | -------------------------------------------------------------------------------- /compiler/analyser/randomCommands.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_RANDOMCOMMANDS_H 21 | #define MEMEASSEMBLY_RANDOMCOMMANDS_H 22 | 23 | #include "../commands.h" 24 | 25 | void setConfusedStonksJumpLabel(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) ; 26 | void chooseLinesToBeDeleted(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 27 | 28 | #endif //MEMEASSEMBLY_RANDOMCOMMANDS_H 29 | -------------------------------------------------------------------------------- /compiler/parser/fileParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_FILEPARSER_H 21 | #define MEMEASSEMBLY_FILEPARSER_H 22 | 23 | #include "parser.h" 24 | 25 | struct commandsArray { 26 | struct parsedCommand* arrayPointer; 27 | size_t size; 28 | }; 29 | 30 | /** 31 | * Parses an input file line by line and fills a provided struct commandsArray 32 | */ 33 | void parseCommands(FILE *inputFile, char* inputFileName, struct compileState* compileState, struct commandsArray* commandsArray); 34 | #endif 35 | -------------------------------------------------------------------------------- /compiler/analyser/comparisons.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_COMPARISONS_H 21 | #define MEMEASSEMBLY_COMPARISONS_H 22 | 23 | #include "../commands.h" 24 | #include 25 | 26 | void analyseWhoWouldWinCommands(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 27 | void analyseTheyreTheSamePictureCommands(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 28 | 29 | #endif //MEMEASSEMBLY_COMPARISONS_H 30 | -------------------------------------------------------------------------------- /compiler/analyser/functions.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_FUNCTIONS_H 21 | #define MEMEASSEMBLY_FUNCTIONS_H 22 | 23 | #include "../commands.h" 24 | #include 25 | 26 | void analyseFunctions(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 27 | void analyseCall(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState); 28 | bool mainFunctionExists(struct compileState* compileState); 29 | 30 | #endif //MEMEASSEMBLY_FUNCTIONS_H 31 | -------------------------------------------------------------------------------- /compiler/parser/parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "parser.h" 21 | #include "functionParser.h" 22 | #include 23 | 24 | void parseFile(struct file* fileStruct, FILE* inputFile, struct compileState* compileState) { 25 | struct commandsArray commandsArray; 26 | parseCommands(inputFile, fileStruct->fileName, compileState, &commandsArray); 27 | 28 | parseFunctions(fileStruct, commandsArray, compileState); 29 | fileStruct->loc = commandsArray.size; 30 | fileStruct->parsedCommands = commandsArray.arrayPointer; 31 | } 32 | -------------------------------------------------------------------------------- /compiler/analyser/analysisHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_ANALYSISHELPER_H 21 | #define MEMEASSEMBLY_ANALYSISHELPER_H 22 | 23 | #include "analyser.h" 24 | 25 | void checkDuplicateDefinition(struct commandLinkedList* commandLinkedList, struct compileState* compileState, bool oncePerFile, uint8_t parametersToCheck, char* itemName); 26 | void checkCompanionCommandExistence(struct commandLinkedList* parentCommands, struct commandLinkedList* childCommands, struct compileState* compileState, uint8_t parametersToCheck, bool sameFile, char* itemName); 27 | 28 | #endif //MEMEASSEMBLY_ANALYSISHELPER_H 29 | -------------------------------------------------------------------------------- /examples/Stalinsort/stalinsort_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | #include 20 | 21 | int array[] = {1, 5, 2, 8, 9, 3, 4, 12, 14, 69, 420, 0}; 22 | size_t arraySize = sizeof array / sizeof(int); 23 | 24 | extern void stalinSort(int array[], size_t *arraySize); 25 | 26 | int main() { 27 | printf("Before sorting:\n"); 28 | for(size_t i = 0; i < arraySize; i++) { 29 | printf("%d,", array[i]); 30 | } 31 | 32 | stalinSort(array, &arraySize); 33 | 34 | printf("\nAfter sorting:\n"); 35 | for(size_t i = 0; i < arraySize; i++) { 36 | printf("%d,", array[i]); 37 | } 38 | printf("\n"); 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/multiple_files/function.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun function 2 | what can I say except T 3 | what can I say except h 4 | what can I say except i 5 | what can I say except s 6 | what can I say except space 7 | what can I say except i 8 | what can I say except s 9 | what can I say except space 10 | what can I say except i 11 | what can I say except n 12 | what can I say except space 13 | what can I say except f 14 | what can I say except u 15 | what can I say except n 16 | what can I say except c 17 | what can I say except t 18 | what can I say except i 19 | what can I say except o 20 | what can I say except n 21 | what can I say except . 22 | what can I say except m 23 | what can I say except e 24 | what can I say except m 25 | what can I say except e 26 | what can I say except a 27 | what can I say except s 28 | what can I say except m 29 | what can I say except \n 30 | 31 | monke uaaua 32 | 33 | I see this as an absolute win 34 | 35 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun emptyFunctionTest 36 | I see this as an absolute win 37 | 38 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun functionReturnTest 39 | who would win? eax or 7 40 | no, I don't think I will 41 | eax wins 42 | right back at ya, buckaroo 43 | 7 wins 44 | I see this as an absolute win 45 | 46 | -------------------------------------------------------------------------------- /examples/reverse-string.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | 3 | sneak 100 rax 4 | 5 | What the hell happened here? rcx counts how many characters have been read 6 | sneak 100 rcx 7 | 8 | upgrade 9 | 10 | What the hell happened here? Read one character from stdin 11 | let me in. LET ME IIIIIIIIN al 12 | 13 | What the hell happened here? Check if it's the end of a line 14 | corporate needs you to find the difference between al and \n 15 | 16 | What the hell happened here? If not, we just push it to the stack (very efficient lol) 17 | stonks rax 18 | upvote rcx 19 | 20 | fuck go back 21 | 22 | they're the same picture 23 | 24 | 25 | What the hell happened here? Now write what we've seen 26 | banana 27 | 28 | What the hell happened here? Check if we have reached the end of the input 29 | who would win? rcx or 0 30 | 31 | 0 wins 32 | What the hell happened here? rcx is <= 0, jump to end of program 33 | return to monke uaaaaaua 34 | 35 | rcx wins 36 | What the hell happened here? rcx > 0, get the character and print it to stdout 37 | 38 | not stonks rax 39 | what can I say except al 40 | 41 | downvote rcx 42 | 43 | where banana 44 | 45 | monke uaaaaaua 46 | 47 | what can I say except \n 48 | 49 | I see this as an absolute win 50 | -------------------------------------------------------------------------------- /examples/toupper/toupper.memeasm: -------------------------------------------------------------------------------- 1 | What the hell happened here? This program takes a pointer to a string in rdi, the string is modified in-place 2 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun toupper_str 3 | 4 | eax is brilliant, but I like 97 5 | 6 | What the hell happened here? Loop over all lower case chars between 97 and 123 7 | upgrade 8 | stonks rdi 9 | 10 | What the hell happened here? We search for the current char in the string 11 | banana 12 | sneak 100 edx 13 | dl is brilliant, but I like rdi do you know de wey 14 | who would win? edx or 1 15 | 16 | What the hell happened here? We get here if edx is greater than 1 => not zero 17 | edx wins 18 | who would win? dl or al 19 | What the hell happened here? Jump below if al is not equal to dl 20 | 21 | What the hell happened here? Clear one bit to make it UPPER CASE 22 | bitconneeeeeeect dl 223 23 | rdi do you know de wey is brilliant, but I like dl 24 | 25 | dl wins 26 | al wins 27 | 28 | upvote rdi 29 | where banana 30 | 31 | What the hell happened here? edx contained 0, meaning this is the end of the string 32 | 1 wins 33 | not stonks rdi 34 | 35 | upvote eax 36 | corporate needs you to find the difference between eax and 123 37 | 38 | fuck go back 39 | 40 | they're the same picture 41 | right back at ya, buckaroo 42 | -------------------------------------------------------------------------------- /.github/workflows/create_installers.yml: -------------------------------------------------------------------------------- 1 | name: Create Installers 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version String' 8 | required: true 9 | 10 | jobs: 11 | create-installers: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 16 | - uses: actions/checkout@v4 17 | 18 | - name: Install nfpm 19 | run: | 20 | echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list 21 | sudo apt update 22 | sudo apt install nfpm 23 | 24 | - name: Replace VERSION_STRING in nfpm configuration file with parameter 25 | run: | 26 | cd nfpm 27 | sed -i 's/VERSION_STRING/${{ github.event.inputs.version }}/g' nfpm.yaml 28 | cd .. 29 | 30 | - name: Generate *.deb/*.rmp installer files 31 | run: | 32 | cd nfpm 33 | ./generateInstallers.sh 34 | cd .. 35 | 36 | - name: Archive debian installer 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: memeasm_${{ github.event.inputs.version }}_amd64.deb 40 | path: memeasm_${{ github.event.inputs.version }}_amd64.deb 41 | 42 | - name: Archive rpm installer 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: memeasm-${{ github.event.inputs.version }}.x86_64.rpm 46 | path: memeasm-${{ github.event.inputs.version }}.x86_64.rpm 47 | -------------------------------------------------------------------------------- /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build Test 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | push: 9 | branches: [ main, develop ] 10 | pull_request: 11 | branches: [ main, develop ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | jobs: 17 | 18 | build_linux: 19 | # The type of runner that the job will run on 20 | runs-on: ubuntu-latest 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 25 | - uses: actions/checkout@v4 26 | 27 | - name: Build Linux 28 | run: make CFLAGS+=-Werror debug 29 | 30 | build_windows: 31 | # The type of runner that the job will run on 32 | runs-on: windows-2019 33 | 34 | # Steps represent a sequence of tasks that will be executed as part of the job 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - name: Run Makefile 39 | shell: powershell 40 | run: | 41 | $env:Path += ";C:\msys64\mingw64\bin" 42 | mingw32-make.exe debug 43 | 44 | build_macos: 45 | # The type of runner that the job will run on 46 | runs-on: macos-latest 47 | 48 | steps: 49 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 50 | - uses: actions/checkout@v4 51 | 52 | - name: Build MacOS 53 | run: make CFLAGS+=-Werror debug 54 | -------------------------------------------------------------------------------- /.github/workflows/compile_and_run_test.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Compile and Run Test 3 | 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main/develop branch 6 | push: 7 | branches: [ main, develop ] 8 | pull_request: 9 | branches: [ main, develop ] 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | run_linux: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - name: Compile the project 21 | run: make debug 22 | 23 | - name: Run the compiler on runnable_example.memeasm 24 | run: ./memeasm -d -o tmp .github/workflows/runnable_example.memeasm 25 | 26 | - name: Run the executable 27 | run: ./tmp 28 | 29 | run_windows: 30 | runs-on: windows-2019 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - name: Compile memeasm 36 | shell: powershell 37 | run: | 38 | $env:Path += ";C:\msys64\mingw64\bin" 39 | mingw32-make.exe debug 40 | 41 | - name: Run compiler on runnable_example.memeasm 42 | shell: powershell 43 | run: ./memeasm.exe -d -o tmp.exe .github\workflows\runnable_example.memeasm 44 | 45 | - name: Run the executable 46 | shell: powershell 47 | run: ./tmp.exe 48 | 49 | run_macos: 50 | runs-on: macos-11 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | 55 | - name: Compile the project 56 | run: make debug 57 | 58 | - name: Run the compiler on runnable_example.memeasm 59 | run: ./memeasm -d -o tmp .github/workflows/runnable_example.memeasm 60 | - name: Run the executable 61 | run: ./tmp 62 | -------------------------------------------------------------------------------- /install_windows.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | ECHO Checking for correct installation of mingw64 3 | 4 | WHERE gcc 5 | IF %ERRORLEVEL% == 0 goto :checkMake 6 | ECHO Error! Could not find gcc in your PATH. Check https://kammt.github.io/MemeAssembly/#/getting-started for more information 7 | ECHO Installation failed 8 | PAUSE 9 | EXIT 1 10 | 11 | :checkMake 12 | WHERE mingw32-make 13 | IF %ERRORLEVEL% == 0 goto :downloadMemeasm 14 | ECHO Error! Could not find mingw32-make in your PATH. Check https://kammt.github.io/MemeAssembly/#/getting-started for more information 15 | ECHO Installation failed 16 | PAUSE 17 | EXIT 1 18 | 19 | :downloadMemeasm 20 | ECHO All dependencies satisfied 21 | ECHO Downloading latest release of MemeAssembly... 22 | powershell -Command "(New-Object Net.WebClient).DownloadFile('https://github.com/kammt/MemeAssembly/zipball/master', '%temp%\MemeAssembly.zip') 23 | ECHO Extracting... 24 | powershell Expand-Archive %temp%\MemeAssembly.zip -DestinationPath %temp%\MemeAssembly-latest 25 | cd %temp%\MemeAssembly-latest\kammt-* 26 | 27 | :runMakefile 28 | set /p installDir="Enter the desired installation directory. Make sure that you have the required priviledges (Default: C:\Program Files\MemeAssembly): " 29 | IF NOT DEFINED installDir SET "installDir=C:\Program Files\MemeAssembly" 30 | ECHO Compiling... 31 | mingw32-make 32 | ECHO Installing... 33 | mkdir "%installDir%" 34 | move "./memeasm.exe" "%installDir%\memeasm.exe" 35 | icacls "%installDir%\memeasm.exe" /t /grant everyone:R 36 | 37 | :cleanup 38 | ECHO Cleaning up... 39 | cd ../.. 40 | DEL /S /Q MemeAssembly-latest 41 | DEL /F MemeAssembly.zip 42 | 43 | :endofinstall 44 | ECHO Installation done! To use MemeAssembly from the command line, add the directory %installDir% to your system-wide PATH-Variable 45 | PAUSE 46 | EXIT 0 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC_win=x86_64-w64-mingw32-gcc 2 | 3 | PLATFORM_MACRO= 4 | ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... 5 | PLATFORM_MACRO=WINDOWS 6 | else 7 | detected_OS := $(shell uname -s) 8 | ifeq ($(detected_OS),Darwin) 9 | PLATFORM_MACRO=MACOS 10 | else 11 | PLATFORM_MACRO=LINUX 12 | endif 13 | endif 14 | 15 | # Compiler Flags 16 | CFLAGS+=-std=gnu17 -D $(PLATFORM_MACRO) -O2 17 | CFLAGS_DEBUG+=-O0 -Wall -Wextra -Wpedantic -Wmisleading-indentation -g 18 | 19 | # Destination directory for make install 20 | bindir=/usr/local/bin 21 | 22 | INSTALL=install 23 | INSTALL_PROGRAM=$(INSTALL) 24 | 25 | # Files to compile 26 | FILES=compiler/memeasm.c compiler/compiler.c compiler/logger/log.c compiler/parser/parser.c compiler/parser/fileParser.c compiler/parser/functionParser.c compiler/analyser/analysisHelper.c compiler/analyser/parameters.c compiler/analyser/functions.c compiler/analyser/jumpMarkers.c compiler/analyser/comparisons.c compiler/analyser/randomCommands.c compiler/analyser/analyser.c compiler/translator/translator.c 27 | 28 | .PHONY: all clean debug uninstall install windows 29 | 30 | # Standard compilation 31 | all: 32 | $(CC) -o memeasm $(FILES) $(CFLAGS) 33 | 34 | # Compilation with debugging-flags 35 | debug: 36 | $(CC) -o memeasm $(FILES) $(CFLAGS) $(CFLAGS_DEBUG) 37 | 38 | # Remove the compiled executable from this directory 39 | clean: 40 | $(RM) memeasm 41 | 42 | # Removes "memeasm" from DESTDIR 43 | uninstall: 44 | $(RM) $(DESTDIR)/memeasm 45 | 46 | # Compiles an executable and stores it in DESTDIR 47 | install: all 48 | mkdir -p $(DESTDIR)$(bindir) 49 | $(INSTALL_PROGRAM) memeasm $(DESTDIR)$(bindir)/memeasm 50 | 51 | # For building a windows executable under Linux 52 | windows: 53 | $(CC_win) -o memeasm.exe $(FILES) $(CFLAGS) 54 | -------------------------------------------------------------------------------- /compiler/logger/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef LOG_H 21 | #define LOG_H 22 | 23 | #include //Printf() function 24 | #include "../compiler.h" 25 | 26 | #define RED "\x1B[31m" 27 | #define GRN "\x1B[32m" 28 | #define YEL "\x1B[33m" 29 | #define BLU "\x1B[34m" 30 | #define MAG "\x1B[35m" 31 | #define CYN "\x1B[36m" 32 | #define WHT "\x1B[37m" 33 | #define RESET "\x1B[0m" 34 | 35 | void printInformationHeader(); 36 | 37 | void printErrorASCII(); 38 | 39 | void printThanosASCII(size_t deletedLines); 40 | 41 | void printNiceASCII(); 42 | 43 | void printDebugMessage(logLevel logLevel, char* message, unsigned varArgNum, ...); 44 | void printStatusMessage(logLevel logLevel, char* message); 45 | 46 | void printError(char* inputFileName, unsigned lineNum, struct compileState* compileState, char* message, unsigned varArgNum, ...); 47 | void printNote(char* message, bool indent, unsigned varArgNum, ...); 48 | 49 | void printInternalCompilerError(char* message, bool report, unsigned varArgNum, ...); 50 | 51 | #define CHECK_ALLOC(ptr) \ 52 | if (!ptr) { \ 53 | printInternalCompilerError("%s:%u: Ran out of memory during compilation", false, 2, __FILE__, __LINE__); \ 54 | exit(EXIT_FAILURE); \ 55 | } 56 | #endif 57 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "CodeQL" 3 | 4 | on: 5 | push: 6 | branches: [ main, develop ] 7 | pull_request: 8 | branches: [ main ] 9 | schedule: 10 | - cron: '42 21 * * 2' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'cpp' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 26 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | # Initializes the CodeQL tools for scanning. 33 | - name: Initialize CodeQL 34 | uses: github/codeql-action/init@v3 35 | with: 36 | languages: ${{ matrix.language }} 37 | # If you wish to specify custom queries, you can do so here or in a config file. 38 | # By default, queries listed here will override any specified in a config file. 39 | # Prefix the list here with "+" to use these queries and those in the config file. 40 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 41 | 42 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 43 | # If this step fails, then you should remove it and run the build manually (see below) 44 | - name: Autobuild 45 | uses: github/codeql-action/autobuild@v3 46 | 47 | # ℹ️ Command-line programs to run using the OS shell. 48 | # 📚 https://git.io/JvXDl 49 | 50 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 51 | # and modify them (or add more) to build your code if your project 52 | # uses a compiled language 53 | 54 | #- run: | 55 | # make bootstrap 56 | # make release 57 | 58 | - name: Perform CodeQL Analysis 59 | uses: github/codeql-action/analyze@v3 60 | -------------------------------------------------------------------------------- /examples/rot13/rot13.memeasm: -------------------------------------------------------------------------------- 1 | What the hell happened here? https://en.wikipedia.org/wiki/ROT13 2 | 3 | What the hell happened here? This program takes pointer to a string in rdi, the string is modified in-place 4 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun rot13 5 | 6 | stonks rdi 7 | 8 | What the hell happened here? Loop over all characters in string 9 | upgrade 10 | sneak 100 edx 11 | dl is brilliant, but I like rdi do you know de wey 12 | 13 | What the hell happened here? Jump to the end, if edx is zero (end of the string) 14 | who would win? edx or 1 15 | edx wins 16 | 17 | What the hell happened here? Compare char with each lowercase letter 18 | eax is brilliant, but I like 97 19 | banana 20 | corporate needs you to find the difference between dl and al 21 | upvote eax 22 | who would win? eax or 122 23 | 122 wins 24 | where banana 25 | 26 | What the hell happened here? If there is a match with a letter 27 | they're the same picture 28 | 29 | What the hell happened here? Perform a "rotation" 30 | dx units are ready, with 13 more well on the way 31 | who would win? dx or 123 32 | dx wins 33 | monke uauauaa 34 | parry 26 you filthy casual dx 35 | 123 wins 36 | 91 wins 37 | rdi do you know de wey is brilliant, but I like dl 38 | 39 | upvote rdi 40 | fuck go back 41 | 42 | What the hell happened here? Get here if there was no match in the previous loop 43 | eax wins 44 | 45 | What the hell happened here? Compare char with each uppercase letter 46 | ecx is brilliant, but I like 65 47 | monke uaaua 48 | who would win? dl or cl 49 | 50 | What the hell happened here? If there is a match with a letter 51 | dx units are ready, with 13 more well on the way 52 | who would win? dx or 91 53 | return to monke uauauaa 54 | 55 | dl wins 56 | cl wins 57 | upvote ecx 58 | who would win? ecx or 90 59 | 90 wins 60 | return to monke uaaua 61 | 62 | What the hell happened here? If no match is found 63 | ecx wins 64 | upvote rdi 65 | fuck go back 66 | 67 | 1 wins 68 | not stonks rdi 69 | 70 | I see this as an absolute win 71 | 72 | -------------------------------------------------------------------------------- /.github/workflows/publish-aur.yml: -------------------------------------------------------------------------------- 1 | name: Publish to AUR 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: 7 | inputs: 8 | version: 9 | description: 'Repository Tag (without v, e.g. 1.6)' 10 | required: true 11 | pkgrel: 12 | required: true 13 | default: "1" 14 | 15 | 16 | jobs: 17 | aur-publish: 18 | runs-on: ubuntu-latest 19 | steps: 20 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 21 | - uses: actions/checkout@v4 22 | 23 | - name: Replace VERSION_STRING in PKGBUILD file with parameter 24 | if: ${{ github.event_name == 'release' }} 25 | run: sed -i 's/VERSION_STRING/${GITHUB_REF:11}/g' PKGBUILD 26 | 27 | - name: Replace VERSION_STRING in PKGBUILD file with parameter 28 | if: ${{ github.event_name == 'workflow_dispatch' }} 29 | run: sed -i 's/VERSION_STRING/${{ github.event.inputs.version }}/g' PKGBUILD 30 | 31 | - name: Replace PKGREL in PKGBUILD file with parameter 32 | if: ${{ github.event_name == 'release' }} 33 | run: sed -i 's/PKGREL/1/g' PKGBUILD 34 | 35 | - name: Replace PKGREL in PKGBUILD file with parameter 36 | if: ${{ github.event_name == 'workflow_dispatch' }} 37 | run: sed -i 's/PKGREL/${{ github.event.inputs.pkgrel }}/g' PKGBUILD 38 | 39 | - name: Replace 256SUM in PKGBUILD file with parameter 40 | if: ${{ github.event_name == 'release' }} 41 | run: | 42 | wget https://github.com/${{ github.repository }}/archive/$GITHUB_REF.tar.gz 43 | export CHECKSUM= 44 | sed -i "s/256SUM/$(sha256sum $GITHUB_REF.tar.gz | cut -d " " -f 1)/g" PKGBUILD 45 | 46 | - name: Replace 256SUM in PKGBUILD file with parameter 47 | if: ${{ github.event_name == 'workflow_dispatch' }} 48 | run: | 49 | wget https://github.com/${{ github.repository }}/archive/refs/tags/v${{ github.event.inputs.version }}.tar.gz 50 | sed -i "s/256SUM/$(sha256sum v${{ github.event.inputs.version }}.tar.gz | cut -d " " -f 1)/g" PKGBUILD 51 | 52 | - name: Output PKGBUILD for debugging purposes 53 | run: cat PKGBUILD 54 | 55 | - name: Publish AUR package with modified PKGBUILD 56 | uses: KSXGitHub/github-actions-deploy-aur@v3.0.1 57 | with: 58 | pkgname: memeassembly 59 | pkgbuild: ./PKGBUILD 60 | commit_username: ${{ secrets.AUR_USERNAME }} 61 | commit_email: ${{ secrets.AUR_EMAIL }} 62 | ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} 63 | commit_message: Update AUR package 64 | ssh_keyscan_types: rsa,dsa,ecdsa,ed25519 65 | -------------------------------------------------------------------------------- /examples/Stalinsort/stalin-sort.memeasm: -------------------------------------------------------------------------------- 1 | What the hell happened here? This program takes a pointer to the array in rdi, the number of elements as a pointer in rsi. No value is returned, but the array and the array size are modified 2 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun stalinSort 3 | 4 | What the hell happened here? The following register setup is defined: 5 | What the hell happened here? - rdi pointer to the array 6 | What the hell happened here? - rsi pointer to the array size 7 | What the hell happened here? 8 | What the hell happened here? - r8 loop counter 9 | What the hell happened here? - r9 new array size 10 | What the hell happened here? - r10 temporary register for comparisons 11 | What the hell happened here? - rax pointer to the next array element that was not in order (i.e. it can be overwritten 12 | What the hell happened here? - ecx the last array element that was in order 13 | 14 | What the hell happened here? Set up our loop counters 15 | sneak 100 r8 16 | sneak 100 r9 17 | 18 | What the hell happened here? Move the first value into the destination 19 | ecx is brilliant, but I like rdi do you know de wey 20 | rax is brilliant, but I like rdi 21 | 22 | What the hell happened here? Move our pointers along 23 | rdi units are ready, with 4 more well on the way 24 | rax units are ready, with 4 more well on the way 25 | 26 | What the hell happened here? Increase our loop counters 27 | upvote r8 28 | upvote r9 29 | 30 | corporate needs you to find the difference between r8 and rsi do you know de wey 31 | 32 | upgrade 33 | What the hell happened here? Move the current value into r10d 34 | r10d is brilliant, but I like rdi do you know de wey 35 | who would win? r10d or ecx 36 | 37 | What the hell happened here? If r10d is greater, then this value is in order. Move it into our sorted array 38 | r10d wins 39 | rax do you know de wey is brilliant, but I like r10d 40 | What the hell happened here? Update ecx to feature the new largest value 41 | ecx is brilliant, but I like r10d 42 | rax units are ready, with 4 more well on the way 43 | upvote r9 44 | 45 | ecx wins 46 | rdi units are ready, with 4 more well on the way 47 | upvote r8 48 | 49 | corporate needs you to find the difference between r8 and rsi do you know de wey 50 | fuck go back 51 | 52 | What the hell happened here? If all elements were traversed, we land here 53 | they're the same picture 54 | 55 | What the hell happened here? Update the array size 56 | rsi do you know de wey is brilliant, but I like r9 57 | 58 | right back at ya, buckaroo 59 | -------------------------------------------------------------------------------- /compiler/analyser/jumpMarkers.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "jumpMarkers.h" 21 | #include "analysisHelper.h" 22 | #include "../logger/log.h" 23 | 24 | /** 25 | * Checks if the usage of all monke labels and return jumps are valid. This includes 26 | * - that no jump labels were defined twice 27 | * - that no returns were used where the label name wasn't defined 28 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 29 | * @param opcode the opcode of the "monke" command. The "return to monke" command must have the following opcode 30 | * @param compileState the current compile state 31 | */ 32 | void analyseMonkeMarkers(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 33 | printDebugMessage(compileState->logLevel, "Beginning Monke jump label validity check", 0); 34 | 35 | checkDuplicateDefinition(commandLinkedList[opcode], compileState, false, 1, "monke jump marker"); 36 | checkCompanionCommandExistence(commandLinkedList[opcode + 1], commandLinkedList[opcode], compileState, 1, false, "monke jump marker"); 37 | } 38 | 39 | /** 40 | * Checks if the jump label (upgrade + fuck go back / banana + where banana) are correctly used, i.e. if 41 | * - a marker is defined when a jump is present 42 | * - markers are only used once 43 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 44 | * @param opcode the opcode of the marker command. The marker jump must have the following opcode 45 | * @param compileState the current compile state 46 | */ 47 | void analyseJumpMarkers(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 48 | printDebugMessage(compileState->logLevel, "Starting jump label validity check for opcode %u", 1, opcode); 49 | 50 | checkDuplicateDefinition(commandLinkedList[opcode], compileState, true, 0, "jump marker"); 51 | checkCompanionCommandExistence(commandLinkedList[opcode + 1], commandLinkedList[opcode], compileState, 0, true, "jump marker"); 52 | } 53 | -------------------------------------------------------------------------------- /examples/is_nice.memeasm: -------------------------------------------------------------------------------- 1 | What the hell happened here? This program reads a decimal number from stdin and tells the user whether is is nice or not (meaning that it is equal to 69 or 420) 2 | What the hell happened here? It does not feature any error handling whatsoever, however it does not crash when providing illegal symbols (e.g. characters) or nothing as input 3 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 4 | 5 | what can I say except E 6 | what can I say except n 7 | what can I say except t 8 | what can I say except e 9 | what can I say except r 10 | what can I say except space 11 | what can I say except y 12 | what can I say except o 13 | what can I say except u 14 | what can I say except r 15 | what can I say except space 16 | what can I say except n 17 | what can I say except u 18 | what can I say except m 19 | what can I say except b 20 | what can I say except e 21 | what can I say except r 22 | what can I say except : 23 | what can I say except space 24 | 25 | sneak 100 r8 26 | 27 | upgrade 28 | sneak 100 rax 29 | let me in. LET ME IIIIIIIIN al 30 | 31 | who would win? al or 10 32 | 33 | What the hell happened here? If we land here, the user pressed enter, i.e. al = 10 (\n), meaning that we escape the loop 34 | where banana 35 | 36 | al wins 37 | 10 wins 38 | parry 48 you filthy casual al 39 | 40 | upvote r8 41 | stonks rax 42 | fuck go back 43 | 44 | banana 45 | What the hell happened here? Now, we recreate the number, which will be stored in rdx. rcx contains the factor that the current digit must be multiplied by, starting at 1, then 10, ... 46 | sneak 100 rdx 47 | rcx is brilliant, but I like 1 48 | 49 | monke uuaauauaua 50 | who would win? r8 or 0 51 | What the hell happened here? If we land here, our loop counter is 0. Escape the loop 52 | return to monke uauauauauaaa 53 | 54 | r8 wins 55 | 0 wins 56 | not stonks rax 57 | 58 | rax is getting out of hand, now there are rcx of them 59 | rdx units are ready, with rax more well on the way 60 | 61 | rcx is getting out of hand, now there are 10 of them 62 | What the hell happened here? Decrease the loop counter and jump back to the loop condition 63 | downvote r8 64 | return to monke uuaauauaua 65 | 66 | What the hell happened here? Our loop counter is now at 0, meaning we are done translating the number 67 | monke uauauauauaaa 68 | corporate needs you to find the difference between rdx and 69 69 | corporate needs you to find the difference between rdx and 420 70 | 71 | what can I say except n 72 | what can I say except o 73 | what can I say except t 74 | what can I say except space 75 | 76 | they're the same picture 77 | what can I say except n 78 | what can I say except i 79 | what can I say except c 80 | what can I say except e 81 | what can I say except ! 82 | what can I say except \n 83 | 84 | I see this as an absolute win 85 | -------------------------------------------------------------------------------- /compiler/analyser/comparisons.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include 21 | #include "comparisons.h" 22 | #include "analysisHelper.h" 23 | #include "../logger/log.h" 24 | 25 | /** 26 | * Performs parameter analysis for the "who would win?..." and "p wins" commands 27 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 28 | * @param opcode the opcode of the "who would win?" command. "p wins" must have the following opcode 29 | * @param compileState the current compile state 30 | */ 31 | void analyseWhoWouldWinCommands(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 32 | printDebugMessage(compileState->logLevel, "Starting analysis for \"who would win?\" command", 0); 33 | 34 | //First check: No comparison jump labels were defined twice 35 | checkDuplicateDefinition(commandLinkedList[opcode + 1], compileState, true, 1, "comparison jump marker"); 36 | 37 | //Second check: "p wins" was declared 38 | checkCompanionCommandExistence(commandLinkedList[opcode], commandLinkedList[opcode + 1], compileState, 2, true, "comparison jump label"); 39 | } 40 | 41 | 42 | /** 43 | * Checks that are usages of "corporate needs you to find the difference..." and "they're the same picture" are valid 44 | * Specifically, it checks that a jump label was defined if a comparison was defined 45 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 46 | * @param opcode the opcode of the "corporate needs ...?" command. "they're the same picture" must have the following opcode 47 | * @param compileState the current compile state 48 | */ 49 | void analyseTheyreTheSamePictureCommands(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 50 | printDebugMessage(compileState->logLevel, "Starting analysis for \"corporate needs you to find the difference...\" command", 0); 51 | 52 | //Check 1: Was the equality label defined twice in the same file? 53 | checkDuplicateDefinition(commandLinkedList[opcode + 1], compileState, true, 0, "\"they're the same picture\""); 54 | 55 | //Check 2: "they're the same picture" must exist if a comparison was used 56 | checkCompanionCommandExistence(commandLinkedList[opcode], commandLinkedList[opcode + 1], compileState, 0, true, "\"they're the same picture\""); 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/compilation_test.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Compilation Test 3 | 4 | # Controls when the action will run. 5 | on: 6 | # Triggers the workflow on push or pull request events but only for the main/develop branch 7 | push: 8 | branches: [ main, develop ] 9 | pull_request: 10 | branches: [ main, develop ] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | compile_linux: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - uses: actions/checkout@v4 24 | 25 | # Runs a single command using the runners shell 26 | - name: compile the project 27 | run: make debug 28 | 29 | - name: run the compiler on example.memeasm 30 | run: ./memeasm -S -d -o example.S .github/workflows/example.memeasm 31 | 32 | - name: Function parser tests 33 | run: | 34 | sudo make install 35 | cd .github/workflows/multiple_files 36 | make && exit 0 || exit 1 37 | make fail_undefined && exit 1 || exit 0 38 | make fail_no_main && exit 1 || exit 0 39 | 40 | - name: Compile all code examples 41 | run: | 42 | sudo make install 43 | cd examples 44 | make 45 | 46 | compile_windows: 47 | # The type of runner that the job will run on 48 | runs-on: windows-2019 49 | 50 | # Steps represent a sequence of tasks that will be executed as part of the job 51 | steps: 52 | - uses: actions/checkout@v4 53 | 54 | - name: Compile memeasm 55 | shell: powershell 56 | run: | 57 | $env:Path += ";C:\msys64\mingw64\bin" 58 | mingw32-make.exe debug 59 | 60 | - name: Run compiler on example.memeasm 61 | shell: powershell 62 | run: ./memeasm.exe -S -d -o example.S .github\workflows\example.memeasm 63 | 64 | - name: Check that compiling multiple input files works 65 | run: ./memeasm.exe -d -o tmp.exe .github/workflows/multiple_files/*.memeasm 66 | 67 | compile_macos: 68 | # The type of runner that the job will run on 69 | runs-on: macos-latest 70 | 71 | # Steps represent a sequence of tasks that will be executed as part of the job 72 | steps: 73 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 74 | - uses: actions/checkout@v4 75 | 76 | # Runs a single command using the runners shell 77 | - name: compile the project 78 | run: make debug 79 | 80 | - name: run the compiler on example.memeasm 81 | run: ./memeasm -S -d -o example.S .github/workflows/example.memeasm 82 | 83 | - name: Function parser tests 84 | run: | 85 | sudo make install 86 | cd .github/workflows/multiple_files 87 | make && exit 0 || exit 1 88 | make fail_undefined && exit 1 || exit 0 89 | make fail_no_main && exit 1 || exit 0 90 | - name: Compile all code examples 91 | run: | 92 | sudo make install 93 | cd examples 94 | make -------------------------------------------------------------------------------- /examples/the-link.memeasm: -------------------------------------------------------------------------------- 1 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 2 | what can I say except h 3 | 4 | sneak 100 ecx 5 | upgrade 6 | eax is brilliant, but I like 116 7 | what can I say except al 8 | upvote ecx 9 | corporate needs you to find the difference between ecx and 2 10 | fuck go back 11 | 12 | they're the same picture 13 | 14 | parry 4 you filthy casual eax 15 | what can I say except al 16 | 17 | what can I say except s 18 | 19 | what can I say except 58 20 | 21 | parry 65 you filthy casual eax 22 | what can I say except al 23 | what can I say except al 24 | stonks rax 25 | 26 | sneak 100 eax 27 | parry 135 you filthy casual eax 28 | what can I say except al 29 | parry 10 you filthy casual eax 30 | what can I say except al 31 | stonks rax 32 | 33 | upvote eax 34 | upvote eax 35 | upvote eax 36 | upvote eax 37 | upvote eax 38 | upvote eax 39 | what can I say except al 40 | stonks rax 41 | downvote eax 42 | what can I say except al 43 | not stonks rax 44 | what can I say except al 45 | 46 | eax is brilliant, but I like 101 47 | stonks rax 48 | upvote eax 49 | bitconneeeeeeect eax 4294967290 50 | what can I say except al 51 | not stonks rax 52 | what can I say except al 53 | 54 | what can I say except . 55 | 56 | what can I say except c 57 | not stonks rax 58 | what can I say except al 59 | 60 | bitconneeeeeeect eax 253 61 | what can I say except al 62 | 63 | not stonks rax 64 | what can I say except al 65 | 66 | what can I say except w 67 | what can I say except 97 68 | 69 | sneak 100 ebx 70 | parry 652 you filthy casual ebx 71 | what can I say except bl 72 | 73 | edx is brilliant, but I like ebx 74 | parry 17 you filthy casual edx 75 | what can I say except dl 76 | 77 | parry 12 you filthy casual ebx 78 | what can I say except bl 79 | 80 | what can I say except ? 81 | 82 | what can I say except 118 83 | 84 | parry 242 you filthy casual eax 85 | what can I say except al 86 | 87 | eax is brilliant, but I like 100 88 | what can I say except al 89 | parry 19 you filthy casual eax 90 | what can I say except al 91 | stonks rax 92 | 93 | sneak 100 eax or draw 25 94 | stonks rax or draw 25 95 | parry 12 you filthy casual eax 96 | what can I say except al 97 | 98 | stonks rax 99 | what can I say except 4 100 | not stonks rax 101 | what can I say except al 102 | what can I say except 9 103 | 104 | not stonks rax 105 | stonks rax 106 | 107 | upvote eax 108 | upvote eax 109 | upvote eax 110 | upvote eax 111 | upvote eax 112 | upvote eax 113 | what can I say except al 114 | 115 | downvote ebx 116 | what can I say except bl 117 | 118 | 119 | 120 | parry 15 you filthy casual ebx 121 | what can I say except bl 122 | 123 | what can I say except c 124 | 125 | not stonks rax 126 | what can I say except al 127 | 128 | what can I say except \n 129 | 130 | I see this as an absolute win 131 | -------------------------------------------------------------------------------- /compiler/analyser/functions.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "functions.h" 21 | #include "analysisHelper.h" 22 | #include "../logger/log.h" 23 | 24 | #include 25 | 26 | /** 27 | * Checks if the function definitions are valid. This includes making sure that 28 | * - no function names are used twice 29 | * - there is a main function if it is supposed to be executable 30 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 31 | * @param opcode the opcode of the function definition 32 | * @param compileState the current compile state 33 | */ 34 | void analyseFunctions(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 35 | checkDuplicateDefinition(commandLinkedList[opcode], compileState, false, 1, "function"); 36 | 37 | //Check 2: Does a main-function exist? 38 | //This check is skipped in bully mode 39 | if(compileState->outputMode == executable && compileState->compileMode != bully) { 40 | if (!mainFunctionExists(compileState)) { 41 | printError(compileState->files[0].fileName, 0, compileState,"unable to create an executable if no main-function was defined", 0); 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * Checks if a main function was defined anywhere 48 | */ 49 | bool mainFunctionExists(struct compileState* compileState) { 50 | for(unsigned fileNum = 0; fileNum < compileState->fileCount; fileNum++) { 51 | for(unsigned funcNum = 0; funcNum < compileState->files[fileNum].functionCount; funcNum++) { 52 | const char* const mainFunctionName = 53 | #ifdef MACOS 54 | "_main"; 55 | #else 56 | "main"; 57 | #endif 58 | if (strcmp(compileState->files[fileNum].functions[funcNum].commands[0].parameters[0], mainFunctionName) == 0) { 59 | return true; 60 | } 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | /** 67 | * Checks if all function-calls are valid, meaning that a function was defined. This check is skipped if we do not create an executable, to be able to call external functions 68 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 69 | * @param opcode the opcode of the function call 70 | * @param compileState the current compile state 71 | */ 72 | void analyseCall(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 73 | if(compileState->outputMode == executable) { 74 | checkCompanionCommandExistence(commandLinkedList[opcode], commandLinkedList[0], compileState, 1, false, 75 | "function"); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /compiler/analyser/analyser.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "analyser.h" 21 | #include "../logger/log.h" 22 | 23 | extern struct command commandList[NUMBER_OF_COMMANDS]; 24 | 25 | void analyseCommands(struct compileState* compileState) { 26 | struct commandLinkedList* commandLinkedList[NUMBER_OF_COMMANDS] = {NULL}; 27 | 28 | //Traverse all files 29 | for(unsigned i = 0; i < compileState->fileCount; i++) { 30 | struct file file = compileState->files[i]; 31 | 32 | //Traverse all functions 33 | for(unsigned j = 0; j < file.functionCount; j++) { 34 | struct function function = file.functions[j]; 35 | 36 | //Traverse all commands 37 | for(unsigned k = 0; k < function.numberOfCommands; k++) { 38 | struct parsedCommand* parsedCommand = &function.commands[k]; 39 | //Analyse parameters 40 | checkParameters(parsedCommand, compileState->files[i].fileName, compileState); 41 | 42 | //Add to command's linkedList 43 | //Create Linked List item 44 | struct commandLinkedList* commandLinkedListItem = malloc(sizeof(struct commandLinkedList)); 45 | CHECK_ALLOC(commandLinkedListItem); 46 | 47 | //Fill struct 48 | commandLinkedListItem->next = NULL; 49 | commandLinkedListItem->definedInFile = i; 50 | commandLinkedListItem->command = parsedCommand; 51 | 52 | //If there is no linked list yet, make this the first item 53 | //If there are items, add it to the end of the list 54 | struct commandLinkedList* lastItem = commandLinkedList[parsedCommand->opcode]; 55 | if(lastItem == NULL) { 56 | commandLinkedList[parsedCommand->opcode] = commandLinkedListItem; 57 | } else { 58 | while (lastItem->next != NULL) { 59 | lastItem = lastItem->next; 60 | } 61 | 62 | lastItem->next = commandLinkedListItem; 63 | } 64 | } 65 | } 66 | } 67 | 68 | //Now go through each command and call its analysis function - if it has one 69 | for(unsigned i = 0; i < NUMBER_OF_COMMANDS; i++) { 70 | struct command command = commandList[i]; 71 | if(command.analysisFunction != NULL) { 72 | command.analysisFunction(commandLinkedList, i, compileState); 73 | } 74 | } 75 | 76 | 77 | //Checks done, freeing memory 78 | printDebugMessage(compileState->logLevel, "Analysis done, freeing memory", 0); 79 | for(unsigned i = 0; i < NUMBER_OF_COMMANDS; i++) { 80 | struct commandLinkedList* itemToBeFreed = commandLinkedList[i]; 81 | while (itemToBeFreed != NULL) { 82 | struct commandLinkedList* toBeFreed = itemToBeFreed; 83 | itemToBeFreed = itemToBeFreed->next; 84 | free(toBeFreed); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /compiler/analyser/randomCommands.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "randomCommands.h" 21 | #include "../logger/log.h" 22 | 23 | #include 24 | #include 25 | 26 | /** 27 | * Chooses a random line of code for each input file in which a random jump marker will be inserted. This is going to be the jump point for all 28 | * instances of "confused stonks" within that file 29 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i - unused 30 | * @param opcode the opcode - unused 31 | * @param compileState the current compile state 32 | */ 33 | void setConfusedStonksJumpLabel(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 34 | (void)(commandLinkedList); 35 | (void)(opcode); 36 | 37 | srand((unsigned int) time(NULL)); 38 | 39 | for(unsigned i = 0; i < compileState->fileCount; i++) { 40 | if(compileState->files[i].loc == 0) { 41 | continue; 42 | } 43 | compileState->files[i].randomIndex = ((size_t) rand()) % (compileState->files[i].loc); 44 | printDebugMessage(compileState->logLevel, "Chose random line for jump marker: %lu", 1, compileState->files[i].randomIndex); 45 | } 46 | } 47 | 48 | /** 49 | * Checks how many times "perfectly balanced as all things should be" occurred and depending on this, marks random lines as to be ignored 50 | * @param commandLinkedList a list of all occurrences of all commands. Index i contains a linked list of all commands that have opcode i 51 | * @param opcode the opcode of this command 52 | * @param compileState the current compile state 53 | */ 54 | void chooseLinesToBeDeleted(struct commandLinkedList** commandLinkedList, unsigned opcode, struct compileState* compileState) { 55 | printDebugMessage(compileState->logLevel, "Starting analysis of the \"Perfectly balanced...\" command", 0); 56 | unsigned perfectlyBalancedUsed = 0; 57 | 58 | struct commandLinkedList* listItem = commandLinkedList[opcode]; 59 | while (listItem != NULL) { 60 | perfectlyBalancedUsed++; 61 | listItem = listItem->next; 62 | } 63 | 64 | printDebugMessage(compileState->logLevel, "\tamount of times perfectly balanced was used: %u", 1, perfectlyBalancedUsed); 65 | //Calculating the number of lines to be deleted 66 | size_t loc = 0; 67 | for(unsigned i = 0; i < compileState->fileCount; i++) { 68 | loc += compileState->files[i].loc; 69 | } 70 | 71 | size_t linesToBeKept = loc; 72 | for(unsigned i = 0; i < perfectlyBalancedUsed; i++) { 73 | linesToBeKept /= 2; 74 | } 75 | size_t linesToBeDeleted = loc - linesToBeKept; 76 | 77 | printDebugMessage(compileState->logLevel, "\tamount of lines to be deleted: %lu", 1, linesToBeDeleted); 78 | if(linesToBeDeleted > 0) { 79 | printThanosASCII(linesToBeDeleted); 80 | 81 | srand(time(NULL)); 82 | size_t selectedLines = 0; 83 | while(selectedLines < linesToBeDeleted) { 84 | //Generate a random file 85 | unsigned randomFile = rand() % compileState->fileCount; 86 | 87 | //Generate a random line within file 88 | size_t randomLine = rand() % compileState->files[randomFile].loc; 89 | printDebugMessage(compileState->logLevel, "\tGenerated random line %lu in file %u", 2, randomLine, randomFile); 90 | 91 | //Check if it was already selected 92 | if(compileState->files[randomFile].parsedCommands[randomLine].translate == false) { 93 | printDebugMessage(compileState->logLevel, "\t\tLine was already generated", 0); 94 | continue; 95 | } 96 | 97 | //If not, set the translate-field to false 98 | compileState->files[randomFile].parsedCommands[randomLine].translate = false; 99 | printDebugMessage(compileState->logLevel, "\t\tLine was added to the list", 0); 100 | 101 | selectedLines++; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /compiler/commands.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #ifndef MEMEASSEMBLY_COMMANDS_H 21 | #define MEMEASSEMBLY_COMMANDS_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define NUMBER_OF_COMMANDS 46 29 | #define MAX_PARAMETER_COUNT 2 30 | 31 | #define OR_DRAW_25_OPCODE NUMBER_OF_COMMANDS - 2; 32 | #define INVALID_COMMAND_OPCODE NUMBER_OF_COMMANDS - 1; 33 | 34 | struct commandLinkedList { 35 | struct parsedCommand* command; 36 | unsigned definedInFile; 37 | struct commandLinkedList* next; 38 | }; 39 | 40 | struct parsedCommand { 41 | uint8_t opcode; 42 | char *parameters[MAX_PARAMETER_COUNT]; 43 | uint8_t paramTypes[MAX_PARAMETER_COUNT]; 44 | uint8_t isPointer; //0 = No Pointer, 1 = first parameter, 2 = second parameter, ... 45 | size_t lineNum; 46 | bool translate; //Default is 1 (true). Is set to false in case this command is selected for deletion by "perfectly balanced as all things should be" 47 | }; 48 | 49 | struct function { 50 | char* definedInFile; 51 | size_t definedInLine; 52 | size_t numberOfCommands; 53 | struct parsedCommand* commands; 54 | }; 55 | 56 | struct file { 57 | char* fileName; 58 | size_t loc; //lines of code 59 | size_t functionCount; 60 | struct function* functions; 61 | struct parsedCommand* parsedCommands; 62 | size_t randomIndex; //A variable necessary for the "confused stonks" command 63 | }; 64 | 65 | typedef enum { noob, bully, obfuscated } compileMode; 66 | typedef enum { executable, assemblyFile, objectFile } outputMode; 67 | typedef enum { intSISD = 0, intSIMD = 1, floatSISD = 2, floatSIMD = 3, doubleSISD = 4, doubleSIMD = 5 } translateMode; 68 | typedef enum { none, o_1 = -1, o_2 = -2, o_3 = -3, o_s, o69420 = 69420} optimisationLevel; 69 | typedef enum { normal, info, debug } logLevel; 70 | 71 | struct compileState { 72 | compileMode compileMode; 73 | outputMode outputMode; 74 | uint32_t fileCount; 75 | struct file* files; 76 | 77 | bool useStabs; 78 | bool martyrdom; 79 | translateMode translateMode; 80 | optimisationLevel optimisationLevel; 81 | 82 | unsigned compilerErrors; 83 | logLevel logLevel; 84 | }; 85 | 86 | // Parameter types 87 | #define PARAM_REG64 1 88 | #define PARAM_REG32 2 89 | #define PARAM_REG16 4 90 | #define PARAM_REG8 8 91 | #define PARAM_DECIMAL 16 92 | #define PARAM_CHAR 32 93 | #define PARAM_MONKE_LABEL 64 94 | #define PARAM_FUNC_NAME 128 95 | //Helper macros for register parameter macros 96 | #define PARAM_ISREG(param) (param <= PARAM_REG8 && param > 0) 97 | #define PARAM_REG (PARAM_REG64 | PARAM_REG32 | PARAM_REG16 | PARAM_REG8) 98 | 99 | // Command types 100 | #define COMMAND_TYPE_MOV 1 101 | #define COMMAND_TYPE_FUNC_RETURN 2 102 | #define COMMAND_TYPE_FUNC_DEF 3 103 | #define COMMAND_TYPE_FUNC_CALL 4 104 | 105 | struct command { 106 | char *pattern; 107 | uint8_t usedParameters; 108 | /* 109 | * Allowed types work as follows: Each bit is assigned to a type of variable. If it is set to one, it is allowed. 110 | * That way, each parameter can allow multiple variable types. 111 | * Bit 0: 64 bit registers 112 | * Bit 1: 32 bit registers 113 | * Bit 2: 16 bit registers 114 | * Bit 3: 8 bit registers 115 | * Bit 4: decimal numbers 116 | * Bit 5: Characters (including Escape Sequences) / ASCII-code 117 | * Bit 6: Valid Monke Jump Label 118 | * Bit 7: Valid function name 119 | */ 120 | uint8_t allowedParamTypes[MAX_PARAMETER_COUNT]; 121 | /* 122 | * The command type contains a special value that can be used to identify the type of command 123 | * without specifying the index of the command. Some examples: 124 | * - return statements 125 | * - mov-command 126 | */ 127 | uint8_t commandType; 128 | void (*analysisFunction)(struct commandLinkedList**, unsigned, struct compileState*); //commandLinkedList-list, opcode (index), compileState 129 | 130 | //TODO replace with char* translationPatterns[6]; 131 | char* translationPattern; 132 | }; 133 | 134 | #define commentStart "What the hell happened here?" 135 | #define multiLineCommentStart "Why, why?" 136 | #define multiLineCommendEnd "Oh, that's why" 137 | 138 | #define orDraw25Start "or" 139 | #define orDraw25End "draw 25" 140 | 141 | #define pointerSuffix "do you know de wey" 142 | 143 | #endif //MEMEASSEMBLY_COMMANDS_H 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

or MemeASM for short, is a highly advanced x86-Assembly based programming language using only memes as commands.

4 |

5 |

6 | 7 | Build Test 8 | 9 | 10 | Compilation Test 11 | 12 | 13 | Compile and Run Test 14 | 15 | 16 | Volkswagen Build Status 17 | 18 |

19 | 20 | ## What is MemeAssembly? 21 | In short, MemeAssembly is the revolution the tech industry has been waiting for. Combining the emerging trend of minimalism with rememberable memes, Big Data, AI and co. won't be the same without it! 22 | 23 | ## What are the perks of using MemeASM? 24 | - **Execution Time: Not Stonks** \ 25 | The MemeASM-compiler converts your code into x86_64-Assembly to make it run blazingly fast! 26 | - **Fun-Factor: Stonks** \ 27 | Consider this: Your code might be simple to understand, but is it fun to understand? With MemeAssembly, your code will be much more fun to look at! 28 | - **Complexity: Not Stonks** \ 29 | Can you even remember all the commands that your 'shiny new' programming language has to offer? Are you overwhelmed and confused by all the ways a modern programming language can be used for? Well look no further than MemeAssembly! MemeASM is your best choice, since it only supports a tiny fraction of instructions. 30 | 31 | ## So it is basically Assembly? 32 | I'd just like to interject for a moment. What you're refering to as Assembly, is in fact, MemeAssembly, or as I've recently taken to calling it, Meme plus Assembly. Assembly is not a Meme operating system unto itself, but rather another free component of a fully functioning Meme system made useful by the Meme corelibs, Twitter bots and vital system components comprising a full Meme OS as defined by Reddit. 33 | 34 | Many computer users run a modified version of the Meme system every day, without realizing it. Through a peculiar turn of events, the version of Meme which is widely used today is often called Assembly, and many of its users are not aware that it is basically the Meme system, developed by the Meme Project. 35 | 36 | There really is a Assembly, and these people are using it, but it is just a part of the system they use. Assembly is the basis: the element of the system that allocates the people's resources to the other Memes that you publish. This basis is an essential part of a Meme operating system, but useless by itself; it can only function in the context of a complete Meme operating system. Assembly is normally used in combination with the Meme operating system: the whole system is basically Meme with Assembly added, or MemeAssembly. All the so-called Assembly distributions are really distributions of MemeAssembly! 37 | 38 | ## Who is it for? 39 | MemeAssembly is the best choice for... 40 | ### ...Silicon Valley Developers :iphone: 41 | When it comes to programs, speed is everything. No user wants to wait minutes for their app to start or to calculate a result. 42 | Thanks to MemeAssembly, these worries are no more! Due to MemeAssembly's high level of optimisation, your code will be blazingly fast! 43 | 44 | ### ...Game Developers :video_game: 45 | According to NVIDIA, "Frames win games". \ 46 | While high-level programming languages like Java provide a lot of pre-coded Interfaces and classes, they do have one giant drawback: *Speed*. \ 47 | Even if the game is great, low performance can be a deal-breaker for most if not all customers! So don't miss out on your potential revenue and give your gamers the highest tier performance - using MemeAssembly! 48 | 49 | ### ...children learning to code :boy: :girl: 50 | Motivating children to join Computer Science related studies or simply learning to code has been a challenge for years. The solution for this problem is simple yet effective: Learn to code using MemeAssembly. \ 51 | [Numerous studies](https://www.youtube.com/watch?v=dQw4w9WgXcQ) have alredy proven the effectivity of MemeAssembly. Parents often praise MemeAssembly's apprach of combining low-level programming languages with Memes that are easy to remember. 52 | 53 | > Convinced? Visit the ["Getting Started"-page](https://kammt.github.io/MemeAssembly/#/getting-started) to find out more about how to start coding with MemeAssembly! 54 | 55 | ## Code examples 56 | Before continuing, please keep in mind that these examples only represent a tiny fraction of what can be done with MemeAssembly. Are you ready to experience the infinite possibilities of MemeAssembly? Here we go: 57 | 1. Express your feelings during a math exam: 58 | ``` 59 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 60 | upgrade 61 | what can I say except A 62 | fuck go back 63 | 64 | I see this as an absolute win 65 | ``` 66 | 2. Add 25 to 64: 67 | ``` 68 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 69 | rbx is brilliant, but I like 64 70 | sneak 100 rax 71 | 72 | upgrade 73 | upvote rbx 74 | upvote rax 75 | corporate needs you to find the difference between rax and 25 76 | fuck go back 77 | 78 | they're the same picture 79 | I see this as an absolute win 80 | ``` 81 | 3. Print the alphabet with spaces in between and a new line at the end: 82 | ``` 83 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 84 | eax is brilliant, but I like 65 85 | 86 | upgrade 87 | what can I say except al 88 | what can I say except \s 89 | upvote eax 90 | corporate needs you to find the difference between eax and 91 91 | fuck go back 92 | 93 | they're the same picture 94 | what can I say except \n 95 | 96 | I see this as an absolute win 97 | ``` 98 | 99 | You can also check the [examples](examples/) directory to find more. 100 | 101 | ## Badge of honor 102 | Whoever dares to write their code in MemeASM shall be rewarded. Not only with a joyful coding experience, but also by being able to place this badge of honor in their GitHub ReadMe:\ 103 | ![Made with MemeAssembly Badge](https://img.shields.io/badge/made%20with-MemeAssembly-blue)\ 104 | To do so, insert this code block into your ReadMe file: \ 105 | ```![Made with MemeAssembly Badge](https://img.shields.io/badge/made%20with-MemeAssembly-blue)``` 106 | 107 | ## Contributing 108 | Contributions to this repository are welcome! Especially ideas for new (and hopefully idiotic) commands. To add a new command, either create an issue with the tag "new-command" or add it yourself and create a pull-request. 109 | 110 | ### Current Contributors: 111 | ![GitHub Contributors Image](https://contrib.rocks/image?repo=kammt/MemeAssembly) 112 | 113 | ## Copyright and License 114 | Copyright :copyright: 2021-2023 Tobias Kamm and contributors \ 115 | The MemeAssembly project is licensed under the GPLv3 license. For more information, consult the [LICENSE file](https://github.com/kammt/MemeAssembly/LICENSE) of the MemeAssembly GitHub-Repo or visit https://www.gnu.org/licenses/gpl-3.0.txt. 116 | -------------------------------------------------------------------------------- /compiler/analyser/analysisHelper.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "analysisHelper.h" 21 | #include "../logger/log.h" 22 | #include 23 | 24 | /** 25 | * This is a helper function that can be used by analysis functions. It checks for duplicate definitions of commands and prints an error if there is one 26 | * @param commandLinkedList the list of that command to be checked for duplicate definitions 27 | * @param compileState the compile state 28 | * @param oncePerFile when set to true, the same command is allowed once per file. If false, global duplicates will be checked as well 29 | * @param parametersToCheck how many parameters should be compared 30 | * @param itemName the name of the item. Will be inserted in the error message ("%s defined twice") 31 | */ 32 | void checkDuplicateDefinition(struct commandLinkedList* commandLinkedList, struct compileState* compileState, bool oncePerFile, uint8_t parametersToCheck, char* itemName) { 33 | if(parametersToCheck > 2) { 34 | parametersToCheck = 2; 35 | } 36 | 37 | struct commandLinkedList* listItem = commandLinkedList; 38 | while (listItem != NULL) { 39 | struct parsedCommand* command = listItem->command; 40 | 41 | printDebugMessage(compileState->logLevel, "\tLabel duplicity check for %s in line %lu in file %u", 3, itemName, command->lineNum, listItem->definedInFile); 42 | 43 | struct commandLinkedList* duplicateItem = listItem->next; 44 | while(duplicateItem != NULL) { 45 | printDebugMessage(compileState->logLevel, "\t\tComparing against parameter %s", 1, duplicateItem->command->parameters[0]); 46 | if((!oncePerFile || duplicateItem->definedInFile == listItem->definedInFile) && 47 | (parametersToCheck < 1 || strcmp( command->parameters[0], duplicateItem->command->parameters[0]) == 0) && 48 | (parametersToCheck != 2 || strcmp( command->parameters[1], duplicateItem->command->parameters[1]) == 0)) { 49 | if(compileState->compileMode != bully) { 50 | printError(compileState->files[duplicateItem->definedInFile].fileName, duplicateItem->command->lineNum, compileState, 51 | "%s defined twice (already defined in %s:%lu)", 2, itemName, compileState->files[listItem->definedInFile].fileName, command->lineNum); 52 | } else { 53 | //To fix this error in bully mode, only the first definition is valid, i.e. duplicateItem is removed 54 | duplicateItem->command->translate = false; 55 | } 56 | } 57 | 58 | duplicateItem = duplicateItem->next; 59 | } 60 | listItem = listItem->next; 61 | } 62 | } 63 | 64 | /** 65 | * This is a helper function that can be used by analysis functions. It checks if for a given command, a "companion command" exists within the same file. 66 | * Examples: 67 | * - When "where banana" is used, "banana" must be defined somewhere in the file 68 | * - When "who would win? rax or 8" is used, "rax wins" and "8 wins" must be defined 69 | * @param parentCommands the parent command (which would be "where banana" and "who would win?..." in this case) - a command that requires another one if used 70 | * @param childCommands the child commands ("banana" and "_ wins" in our examples) 71 | * @param compileState the compile state 72 | * @param parametersToCheck how many parameters should be checked. Note that when this is set to e.g. 2, 73 | * it will look for a command with a first parameter that matches param1 and a separate command with a first parameter that matches param2 74 | * @param sameFile whether or not the companion command has to exist within the same file. Is true for all commands except function calls 75 | * @param itemName the name of the item. Will be inserted in the error message ("%s wasn't defined [for parameter _]") 76 | */ 77 | void checkCompanionCommandExistence(struct commandLinkedList* parentCommands, struct commandLinkedList* childCommands, struct compileState* compileState, uint8_t parametersToCheck, bool sameFile, char* itemName) { 78 | if(parametersToCheck > 2) { 79 | parametersToCheck = 2; 80 | } 81 | 82 | struct commandLinkedList* parentCommand = parentCommands; 83 | while (parentCommand != NULL) { 84 | bool childFound[2] = {false}; 85 | struct parsedCommand* command = parentCommand->command; 86 | 87 | printDebugMessage(compileState->logLevel, "\tLooking for %s of parent command in line %lu in file %u", 3, itemName, command->lineNum, parentCommand->definedInFile); 88 | 89 | struct commandLinkedList* childCommand = childCommands; 90 | while(childCommand != NULL) { 91 | 92 | if(!sameFile || parentCommand->definedInFile == childCommand->definedInFile) { 93 | //The first child was found if either no parameters must match or the first parameter matches 94 | if(parametersToCheck == 0 || (parametersToCheck >= 1 && strcmp( command->parameters[0], childCommand->command->parameters[0]) == 0)) { 95 | childFound[0] = true; 96 | } else if(parametersToCheck == 2 && strcmp( command->parameters[1], childCommand->command->parameters[0]) == 0) { 97 | childFound[1] = true; 98 | } 99 | } 100 | 101 | childCommand = childCommand->next; 102 | } 103 | 104 | //We traversed all child commands, now we need to check if everything was defined properly 105 | if(parametersToCheck == 0) { 106 | if(!childFound[0]) { 107 | if(compileState->compileMode != bully) { 108 | printError(compileState->files[parentCommand->definedInFile].fileName, command->lineNum, 109 | compileState, 110 | "%s was not defined", 1, itemName); 111 | } else { 112 | //In bully mode, we just disregard the parent command 113 | parentCommand->command->translate = false; 114 | } 115 | } 116 | } else { 117 | if(!childFound[0]) { 118 | if(compileState->compileMode != bully) { 119 | printError(compileState->files[parentCommand->definedInFile].fileName, command->lineNum, 120 | compileState,"%s was not defined for parameter \"%s\"", 2, itemName, command->parameters[0]); 121 | } else { 122 | //In bully mode, we just disregard the parent command 123 | parentCommand->command->translate = false; 124 | } 125 | } 126 | if(parametersToCheck == 2 && !childFound[1]) { 127 | if(compileState->compileMode != bully) { 128 | printError(compileState->files[parentCommand->definedInFile].fileName, command->lineNum, 129 | compileState,"%s was not defined for parameter \"%s\"", 2, itemName, command->parameters[1]); 130 | } else { 131 | //In bully mode, we just disregard the parent command 132 | parentCommand->command->translate = false; 133 | } 134 | } 135 | } 136 | 137 | parentCommand = parentCommand->next; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /examples/brainfuck.memeasm: -------------------------------------------------------------------------------- 1 | What the hell happened here? This is a brainfuck interpreter written in MemeASM 2 | What the hell happened here? Usage: Provide a string of BF-Code as a single argument to this program 3 | What the hell happened here? Some notes: 4 | What the hell happened here? - The cell "band" is 10.000 Bytes large. However, there is again no error handling for a pointer moving out of bounds. 5 | What the hell happened here? meaning that you could theoretically modify stuff like the return address etc. - basically anything on the stack 6 | What the hell happened here? - Also, I know that not supplying any argument will segfault because I don't check argc 7 | 8 | I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun main 9 | What the hell happened here? rdi is a string-pointer 10 | rdi is brilliant, but I like rsi 11 | What the hell happened here? Get argv[1] 12 | rdi units are ready, with 8 more well on the way 13 | rdi is brilliant, but I like rdi do you know de wey 14 | 15 | What the hell happened here? Frame Pointer - because I'm fancy I guess 16 | stonks rbp 17 | rbp is brilliant, but I like rsp 18 | 19 | What the hell happened here? Reserve 10000B on the stack 20 | parry 10000 you filthy casual rsp 21 | What the hell happened here? rdx will be the pointer to this "execution band" 22 | rdx is brilliant, but I like rsp 23 | sneak 100 rax 24 | sneak 100 r8b 25 | monke uuuuuuuuauuuuuauauaa 26 | who would win? rax or 10000 27 | return to monke aaaaaaaaaaauaaaaaaaaaaaauu 28 | rax wins 29 | 10000 wins 30 | rdx do you know de wey is brilliant, but I like r8b 31 | upvote rdx 32 | upvote rax 33 | return to monke uuuuuuuuauuuuuauauaa 34 | monke aaaaaaaaaaauaaaaaaaaaaaauu 35 | rdx is brilliant, but I like rsp 36 | 37 | What the hell happened here? 38 | 39 | What the hell happened here? Now we do something with the bf code 40 | stonks r12 41 | stonks r13 42 | stonks r14 43 | stonks r15 44 | stonks rbx 45 | 46 | What the hell happened here? rdi will point to our current instruction 47 | 48 | monke uauauauauaaaa 49 | al is brilliant, but I like rdi do you know de wey 50 | What the hell happened here? End of String? 51 | corporate needs you to find the difference between al and 0 52 | What the hell happened here? Now we do a switch-case construct to differentiate between commands 53 | who would win? al or + 54 | r8b is brilliant, but I like rdx do you know de wey 55 | upvote r8b 56 | rdx do you know de wey is brilliant, but I like r8b 57 | upvote rdi 58 | return to monke aaaaaaaauauauaaa 59 | 60 | + wins 61 | al wins 62 | r8b is brilliant, but I like al 63 | 64 | who would win? r8b or - 65 | What the hell happened here? This is a minus 66 | r8b is brilliant, but I like rdx do you know de wey 67 | downvote r8b 68 | rdx do you know de wey is brilliant, but I like r8b 69 | upvote rdi 70 | return to monke aaaaaaaauauauaaa 71 | 72 | - wins 73 | r8b wins 74 | r9b is brilliant, but I like r8b 75 | 76 | who would win? r9b or > 77 | What the hell happened here? This is a > 78 | upvote rdx 79 | upvote rdi 80 | return to monke aaaaaaaauauauaaa 81 | 82 | > wins 83 | r9b wins 84 | r10b is brilliant, but I like r9b 85 | 86 | who would win? r10b or < 87 | downvote rdx 88 | upvote rdi 89 | return to monke aaaaaaaauauauaaa 90 | 91 | r10b wins 92 | < wins 93 | r11b is brilliant, but I like r10b 94 | 95 | who would win? r11b or , 96 | What the hell happened here? This is a , 97 | let me in. LET ME IIIIIIIIN r8b 98 | rdx do you know de wey is brilliant, but I like r8b 99 | upvote rdi 100 | return to monke aaaaaaaauauauaaa 101 | 102 | r11b wins 103 | , wins 104 | r12b is brilliant, but I like r11b 105 | 106 | who would win? r12b or . 107 | What the hell happened here? This is a . 108 | r8b is brilliant, but I like rdx do you know de wey 109 | what can I say except r8b 110 | upvote rdi 111 | return to monke aaaaaaaauauauaaa 112 | 113 | r12b wins 114 | . wins 115 | r13b is brilliant, but I like r12b 116 | 117 | who would win? r13b or [ 118 | What the hell happened here? This is a [ 119 | What the hell happened here? Jump if zero 120 | sneak 100 r9 121 | r9b is brilliant, but I like rdx do you know de wey 122 | who would win? r9 or 0 123 | What the hell happened here? Find matching ] 124 | r8 is brilliant, but I like 1 125 | upgrade 126 | upvote rdi 127 | sneak 100 r10 128 | r10b is brilliant, but I like rdi do you know de wey 129 | upvote r10 130 | who would win? r10 or 92 131 | What the hell happened here? Is another [ 132 | upvote r8 133 | fuck go back 134 | 135 | r10 wins 136 | 92 wins 137 | r11 is brilliant, but I like r10 138 | 139 | who would win? r11 or 94 140 | What the hell happened here? Is a ] 141 | What the hell happened here? Is r8 1? 142 | What the hell happened here? If so, we found the matching bracket 143 | who would win? r8 or 1 144 | upvote rdi 145 | return to monke aaaaaaaauauauaaa 146 | 147 | r8 wins 148 | 1 wins 149 | downvote r8 150 | r11 wins 151 | 94 wins 152 | fuck go back 153 | 154 | r9 wins 155 | 0 wins 156 | What the hell happened here? Don't jump 157 | upvote rdi 158 | return to monke aaaaaaaauauauaaa 159 | 160 | r13b wins 161 | [ wins 162 | r14b is brilliant, but I like r13b 163 | 164 | What the hell happened here? Handling for ] 165 | who would win? r14b or ] 166 | What the hell happened here? Should we even jump? 167 | sneak 100 r12 168 | r12b is brilliant, but I like rdx do you know de wey 169 | sneak 100 r13 170 | who would win? r12 or r13 171 | What the hell happened here? Value is zero, don't jump 172 | upvote rdi 173 | return to monke aaaaaaaauauauaaa 174 | r13 wins 175 | r12 wins 176 | What the hell happened here? We need to jump - find the matching [ 177 | rbx is brilliant, but I like 1 178 | banana 179 | downvote rdi 180 | sneak 100 r9 181 | r9b is brilliant, but I like rdi do you know de wey 182 | 183 | downvote r9 184 | who would win? r9w or 90 185 | What the hell happened here? This is a [ 186 | upvote rbx 187 | What the hell happened here? Is rbx now 2? If so, we have found the matching bracket. If no, subtract two and continue 188 | who would win? rbx or 2 189 | upvote rdi 190 | return to monke aaaaaaaauauauaaa 191 | 192 | 2 wins 193 | rbx wins 194 | downvote rbx 195 | downvote rbx 196 | where banana 197 | 198 | r9w wins 199 | 90 wins 200 | upvote r9 201 | upvote r9 202 | upvote r9 203 | r11w is brilliant, but I like r9w 204 | who would win? r11w or 95 205 | What the hell happened here? Is another ] 206 | upvote rbx 207 | 208 | r11w wins 209 | 95 wins 210 | where banana 211 | return to monke aaaaaaaauauauaaa 212 | 213 | What the hell happened here? some other character - ignore it 214 | r14b wins 215 | ] wins 216 | upvote rdi 217 | 218 | monke aaaaaaaauauauaaa 219 | 220 | return to monke uauauauauaaaa 221 | they're the same picture 222 | What the hell happened here? Clean up the stack and stuff 223 | 224 | not stonks rbx 225 | not stonks r15 226 | not stonks r14 227 | not stonks r13 228 | not stonks r12 229 | rsp is brilliant, but I like rbp 230 | not stonks rbp 231 | what can I say except \n 232 | I see this as an absolute win 233 | -------------------------------------------------------------------------------- /compiler/parser/functionParser.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include 21 | #include "functionParser.h" 22 | #include "../logger/log.h" 23 | 24 | extern const struct command commandList[]; 25 | char *functionNames[] = {"mprotect", "kill", "signal", "raise", "dump", "atoi", 26 | "generateExcellence", "isExcellent", "memeify", "uwufy", "test", 27 | "helloWorld", "snake_case_sucks", "gets", "uwu", "skillIssue"}; 28 | unsigned numFunctionNames = sizeof(functionNames) / sizeof (char*); 29 | 30 | /** 31 | * Creates a function struct by starting at the function definition and then traversing the 32 | * command array until a return statement, new function definition or end of array is found 33 | * @param commandsArray a pointer to the commands array created by the parser 34 | * @param functionStartAtIndex at which index of the array the function definition is 35 | * @param functionDeclarationOpcode the opcode of the function declaration command. The three return commands must be the three consecutive opcodes 36 | * @return a function struct containing all parsed information 37 | */ 38 | struct function parseFunction(struct commandsArray commandsArray, char* inputFileName, size_t functionStartAtIndex, struct compileState* compileState) { 39 | struct parsedCommand functionStart = *(commandsArray.arrayPointer + functionStartAtIndex); 40 | 41 | //Define the structs 42 | struct function function; 43 | function.definedInLine = (size_t) functionStart.lineNum; 44 | function.definedInFile = inputFileName; 45 | 46 | printDebugMessage(compileState->logLevel, "\tParsing function:", 1, functionStart.parameters[0]); 47 | 48 | size_t index = 1; 49 | size_t functionEndIndex = 0; //This points to the last found return-statement and is 0 if no return statement was found until now 50 | 51 | //Iterate through all commands until a return statement is found or the end of the array is reached 52 | while (functionStartAtIndex + index < commandsArray.size) { 53 | struct parsedCommand parsedCommand = *(commandsArray.arrayPointer + (functionStartAtIndex + index)); 54 | //Get the opcode 55 | uint8_t opcode = parsedCommand.opcode; 56 | 57 | //Is this a new function definition 58 | if(commandList[opcode].commandType == COMMAND_TYPE_FUNC_DEF) { 59 | //If the previous command was a return statement, everything is fine. But if not, then we have a new function 60 | //definition, while the previous function did not return yet 61 | if(functionEndIndex != functionStartAtIndex + index - 1) { 62 | if(compileState->compileMode != bully) { 63 | //Throw an error since the last statement was not a return 64 | printError(inputFileName, parsedCommand.lineNum, compileState, 65 | "expected a return statement, but got a new function definition", 0); 66 | } 67 | } 68 | break; 69 | } else if(commandList[opcode].commandType == COMMAND_TYPE_FUNC_RETURN) { //command is a return statement 70 | functionEndIndex = functionStartAtIndex + index; 71 | } 72 | index++; 73 | } 74 | printDebugMessage(compileState->logLevel, "\t\tIteration stopped at index %lu", 1, index); 75 | 76 | if(functionEndIndex == 0 && compileState->compileMode != bully) { 77 | printError(inputFileName, functionStart.lineNum, compileState, "function does not return", 0); 78 | } 79 | 80 | //Our function definition is also a command, hence there are functionEndIndex - functionStartAtIndex + 1 commands 81 | function.numberOfCommands = (functionEndIndex != 0) ? (functionEndIndex - functionStartAtIndex) + 1 : 1; 82 | return function; 83 | } 84 | 85 | void parseFunctions(struct file* fileStruct, struct commandsArray commandsArray, struct compileState* compileState) { 86 | //First, count how many function definitions there are 87 | size_t functionDefinitions = 0; 88 | for (size_t i = 0; i < commandsArray.size; ++i) { 89 | if(commandList[commandsArray.arrayPointer[i].opcode].commandType == COMMAND_TYPE_FUNC_DEF) { 90 | functionDefinitions++; 91 | } 92 | } 93 | printDebugMessage(compileState->logLevel, "Number of functions: %lu", 1, functionDefinitions); 94 | 95 | //Now we create our array of functions 96 | int functionArrayIndex = 0; 97 | struct function *functions = calloc(sizeof(struct function), functionDefinitions); 98 | CHECK_ALLOC(functions); 99 | 100 | //We now traverse the commands array again, this time parsing the functions 101 | size_t commandArrayIndex = 0; //At which command we currently are 102 | while (commandArrayIndex < commandsArray.size) { 103 | /* 104 | * Here, we have not found another function definition yet. Traverse over all commands. 105 | * - If they are not function definitions, we need to check if bully mode is on 106 | * - if not, just throw a compiler error for each command that does not belong to a function 107 | * - if so, those "orphaned" commands are added to newly created functions, with a random function name 108 | * - If they are, break and start parsing that function 109 | */ 110 | size_t startIndex = commandArrayIndex; 111 | bool orphanedCommands = false; 112 | for (; commandArrayIndex < commandsArray.size; commandArrayIndex++) { 113 | if (commandList[commandsArray.arrayPointer[commandArrayIndex].opcode].commandType != COMMAND_TYPE_FUNC_DEF) { 114 | orphanedCommands = true; 115 | if(compileState->compileMode != bully) { 116 | printError(fileStruct->fileName, commandsArray.arrayPointer[commandArrayIndex].lineNum, 117 | compileState, "command does not belong to any function", 0); 118 | } 119 | } else { 120 | break; 121 | } 122 | } 123 | 124 | //If we're in bully mode and there were orphaned commands, then they range from startIndex to commandArrayIndex - 1 125 | //Inject a fake function with those commands 126 | if(compileState->compileMode == bully && orphanedCommands) { 127 | char* funcName = functionNames[commandArrayIndex % (sizeof(functionNames) / sizeof(char*))]; 128 | 129 | //We create two extra commands (function definition and return) 130 | size_t numCommands = commandArrayIndex - startIndex + 2; 131 | struct parsedCommand *commands = calloc(numCommands, sizeof(struct parsedCommand)); 132 | //There is one more function, so resize our functions array 133 | //We cannot use reallocarray(), since it does not exist on MacOS/Windows :( 134 | size_t functionsArraySize = 0; 135 | if (__builtin_umull_overflow(++functionDefinitions, sizeof(struct function), &functionsArraySize)) { 136 | fprintf(stderr, "Critical error: Memory allocation for command parameter failed!"); 137 | exit(EXIT_FAILURE); 138 | } 139 | functions = realloc(functions, functionsArraySize); 140 | CHECK_ALLOC(functions); 141 | CHECK_ALLOC(commands); 142 | 143 | //Copy over the commands 144 | //The first one is a function definition 145 | commands[0].opcode = 0; 146 | commands[0].lineNum = 69; //That doesn't matter, there are no error messages anyway 147 | commands[0].parameters[0] = funcName; 148 | commands[0].translate = true; 149 | 150 | //memcpy the other commands 151 | //We don't check for mult-overflow here, since numCommands*sizeof(...) was performed earlier in calloc, which succeeded => no overflow 152 | memcpy(commands + 1, &commandsArray.arrayPointer[startIndex], (numCommands - 2) * sizeof(struct parsedCommand)); 153 | 154 | //The last one is a return 155 | commands[numCommands - 1].opcode = 1; 156 | commands[numCommands - 1].lineNum = 420; 157 | commands[numCommands - 1].translate = true; 158 | 159 | 160 | //Set the function-struct accordingly 161 | functions[functionArrayIndex].definedInFile = fileStruct->fileName; 162 | functions[functionArrayIndex].numberOfCommands = numCommands; 163 | functions[functionArrayIndex].commands = commands; 164 | 165 | //Increase the index 166 | functionArrayIndex++; 167 | } 168 | 169 | if(commandArrayIndex >= commandsArray.size) { 170 | break; 171 | } 172 | 173 | //Parse the function 174 | functions[functionArrayIndex] = parseFunction(commandsArray, fileStruct->fileName, commandArrayIndex, compileState); 175 | //Set the commands 176 | functions[functionArrayIndex].commands = &commandsArray.arrayPointer[commandArrayIndex]; 177 | //Increase our command index so that it points to the next unparsed command 178 | commandArrayIndex += functions[functionArrayIndex].numberOfCommands; 179 | //Increase our function array index so that it points to the next uninitialised struct 180 | functionArrayIndex++; 181 | } 182 | 183 | //Update the file struct 184 | fileStruct->functionCount = functionDefinitions; 185 | fileStruct->functions = functions; 186 | } 187 | -------------------------------------------------------------------------------- /compiler/memeasm.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "compiler.h" 28 | #include "parser/parser.h" 29 | #include "logger/log.h" 30 | extern const char* const versionString; 31 | 32 | /** 33 | * Prints the help page of this command. Launched by using the -h option in the terminal 34 | */ 35 | void printHelpPage(char* programName) { 36 | printInformationHeader(); 37 | printf("Usage:\n"); 38 | printf(" %s [options] -o outputFile [-i | -d] inputFile\t\tCompiles the specified file into an executable\n", programName); 39 | printf(" %s [options] -S -o outputFile.S [-i | -d] inputFile\tOnly compiles the specified file and saves it as x86_64 Assembly code\n", programName); 40 | printf(" %s [options] -O -o outputFile.o [-i | -d] inputFile\tOnly compiles the specified file and saves it an object file\n", programName); 41 | printf(" %s (-h | --help)\t\t\t\t\tDisplays this help page\n", programName); 42 | printf(" %s -v\t\t\t\t\t\t\tPrints version information\n\n", programName); 43 | printf("Compiler options:\n"); 44 | printf(" -O-1 \t\t- reverse optimisation stage 1: A nop is inserted after every command\n"); 45 | printf(" -O-2 \t\t- reverse optimisation stage 2: A register is moved to and from the Stack after every command\n"); 46 | printf(" -O-3 \t\t- reverse optimisation stage 3: A xmm-register is moved to and from the Stack using movups after every command\n"); 47 | printf(" -O-s \t\t- reverse storage optimisation: Intentionally increases the file size by aligning end of the compiled Assembly-code to 536870912B\n"); 48 | printf(" -O69420 \t- maximum optimisation. Reduces the execution to close to 0s by optimising out your entire code\n"); 49 | printf(" -fcompile-mode - Change the compile mode to noob (default), bully, or obfuscated\n"); 50 | printf(" -g \t\t- write debug info into the compiled file. Currently, only the STABS format is supported (Linux-only)\n"); 51 | printf(" -fno-martyrdom - Disables martyrdom\n"); 52 | printf(" -d \t\t- enables debug logs\n"); 53 | } 54 | 55 | void printExplanationMessage(char* programName) { 56 | printf("Usage: %s -o outputFile inputFile\n", programName); 57 | } 58 | 59 | int main(int argc, char* argv[]) { 60 | struct compileState compileState = { 61 | .compileMode = noob, 62 | .optimisationLevel = none, 63 | .translateMode = intSISD, 64 | .outputMode = executable, 65 | .useStabs = false, 66 | .compilerErrors = 0, 67 | .logLevel = normal 68 | }; 69 | 70 | char *outputFileString = NULL; 71 | FILE *inputFile; 72 | 73 | int optimisationLevel = 0; 74 | int martyrdom = true; 75 | const struct option long_options[] = { 76 | {"output", required_argument, 0, 'o'}, 77 | {"help", no_argument, 0, 'h'}, 78 | {"debug", no_argument, 0, 'd'}, 79 | {"fno-martyrdom", no_argument,&martyrdom, false}, 80 | {"fcompile-mode", required_argument,0, 'c'}, 81 | { 0, 0, 0, 0 } 82 | }; 83 | 84 | int opt; 85 | int option_index = 0; 86 | 87 | while ((opt = getopt_long_only(argc, argv, "o:hO::dgSv", long_options, &option_index)) != -1) { 88 | switch (opt) { 89 | case 'h': 90 | printHelpPage(argv[0]); 91 | return 0; 92 | case 'v': 93 | //Print the version number, but without the "v" at the beginning 94 | printf("%s\n", versionString+1); 95 | return 0; 96 | case 'S': 97 | compileState.outputMode = assemblyFile; 98 | break; 99 | case 'O': 100 | if(!optarg) { 101 | compileState.outputMode = objectFile; 102 | } else { 103 | if(strcmp(optarg, "-s") == 0) { 104 | compileState.optimisationLevel = o_s; 105 | } else { 106 | char *endptr; 107 | errno = 0; 108 | long res = strtol(optarg, &endptr, 10); 109 | if (errno) { 110 | perror("Invalid optimisation level specified"); 111 | return 1; 112 | } else if (endptr == optarg || *endptr != '\0' || 113 | (res != 69420 && res != -1 && res != -2 && res != -3)) { 114 | fprintf(stderr, "Invalid optimisation level specified: %s\n", optarg); 115 | return 1; 116 | } 117 | 118 | compileState.optimisationLevel = res; 119 | } 120 | } 121 | break; 122 | case 'd': 123 | compileState.logLevel = debug; 124 | break; 125 | case 'o': 126 | outputFileString = optarg; 127 | break; 128 | case 'g': 129 | #ifdef WINDOWS 130 | //If we use Windows, STABS does not work - output a warning, but don't do anything 131 | printNote("-g cannot be used on Windows-systems, this option will be ignored.", false, 0); 132 | #elif defined(MACOS) 133 | //If we use MacOS, STABS does not work - output a warning, but don't do anything 134 | printNote("-g cannot be used on MacOS-systems, this option will be ignored.", false, 0); 135 | #else 136 | compileState.useStabs = true; 137 | #endif 138 | break; 139 | case 'c': //-fcompile-mode 140 | if(strcmp(optarg, "bully") == 0) { //Bully mode 141 | compileState.compileMode = bully; 142 | } else if(strcmp(optarg, "obfuscated") == 0) { //obfuscated mode 143 | compileState.compileMode = obfuscated; 144 | } else if(strcmp(optarg, "noob") == 0) { //noob mode 145 | compileState.compileMode = noob; 146 | } else { 147 | fprintf(stderr, "Error: invalid compile mode (must be one of \"noob\", \"bully\", \"obfuscated\")\n"); 148 | return 1; 149 | } 150 | break; 151 | case '?': 152 | fprintf(stderr, "Error: Unknown option provided\n"); 153 | printExplanationMessage(argv[0]); 154 | return 1; 155 | } 156 | } 157 | compileState.martyrdom = martyrdom; 158 | if(compileState.useStabs && compileState.compileMode == bully) { 159 | printNote("-g cannot be used in bully mode, this option will be ignored.", false, 0); 160 | compileState.useStabs = false; 161 | } 162 | 163 | if(outputFileString == NULL) { 164 | fprintf(stderr, "Error: No output file specified\n"); 165 | printExplanationMessage(argv[0]); 166 | return 1; 167 | } else if(argc < optind + 1) { 168 | fprintf(stderr, "Error: No input file(s) specified\n"); 169 | printExplanationMessage(argv[0]); 170 | return 1; 171 | } else { 172 | //We have one or more input files, check how many there are 173 | //The first is at optind, the last at argc-1 174 | uint32_t fileCount = argc - optind; 175 | 176 | //Now allocate fileCount file structs on the heap 177 | struct file* fileStructs = calloc(fileCount, sizeof(struct file)); 178 | CHECK_ALLOC(fileStructs); 179 | 180 | //Open each file one by one and parse it into a "struct file" 181 | for(int i = optind; i < argc; i++) { 182 | inputFile = fopen(argv[i], "r"); 183 | //If the pointer is NULL, then the file failed to open. Print an error 184 | if (inputFile == NULL) { 185 | perror("Failed to open input file"); 186 | printExplanationMessage(argv[0]); 187 | return 1; 188 | } 189 | 190 | //Create a stat struct to check if the file is a regular file. If we did not check for this, an input file like "/dev/urandom" would pass without errors 191 | struct stat inputFileStat; 192 | fstat(fileno(inputFile), &inputFileStat); 193 | if (!S_ISREG(inputFileStat.st_mode)) { 194 | fprintf(stderr, 195 | "Error while opening input file: Your provided file name does not point to a regular file (e.g. it could be a directory, character device or a socket)\n"); 196 | fclose(inputFile); 197 | printExplanationMessage(argv[0]); 198 | return 1; 199 | } 200 | 201 | //Set the attribute "fileName" in the struct, because the parsing function uses this attribute for error printing 202 | fileStructs[i - optind].fileName = argv[i]; 203 | 204 | //Parse file 205 | printDebugMessage(compileState.logLevel, "Opening file \"%s\" successful, parsing file...", 1, argv[i]); 206 | parseFile(&fileStructs[i - optind], inputFile, &compileState); 207 | printDebugMessage(compileState.logLevel, "File parsing done, closing file...", 0); 208 | fclose(inputFile); 209 | } 210 | compileState.fileCount = fileCount; 211 | compileState.files = fileStructs; 212 | 213 | //Convert our optmisationLevel to a value that our struct can work with to make it more readable later on 214 | //If optimisationLevel == 0, then leave the value at none (default) 215 | if(optimisationLevel == -1) { 216 | compileState.optimisationLevel = o_1; 217 | } else if (optimisationLevel == -2) { 218 | compileState.optimisationLevel = o_2; 219 | } else if (optimisationLevel == -3) { 220 | compileState.optimisationLevel = o_3; 221 | } else if (optimisationLevel == -4) { 222 | compileState.optimisationLevel = o_s; 223 | } else if (optimisationLevel == 69420) { 224 | compileState.optimisationLevel = o69420; 225 | } 226 | 227 | compile(compileState, outputFileString); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /compiler/logger/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "log.h" 21 | #include 22 | #include 23 | 24 | const char* const versionString = "v1.6"; 25 | const char* const platformSuffix = 26 | #ifdef WINDOWS 27 | "Windows"; 28 | #elif defined(MACOS) 29 | "MacOS"; 30 | #else 31 | "Linux"; 32 | #endif 33 | 34 | const char* const randomErrorMessages[] = { 35 | //sudo 36 | "you are not in the sudoers file. This incident will be reported.", 37 | "you are not allowed to run sudo. This incident will be reported.", 38 | //erno - cause why not? 39 | "ENOSPC: No space left on device", 40 | "ENOENT: No such file or directory", 41 | //glibc 42 | "double free or corruption (top)", 43 | "corrupted top size", 44 | "double free or corruption (!prev)", 45 | "free: invalid pointer", 46 | //gcc 47 | "implicit declaration of function `gets'", 48 | "invalid type argument of `unary *'", 49 | "passing arg 2 of `strcpy' makes pointer from integer without a cast", 50 | "syntax error before '}' token", 51 | "ld returned exit 1 status", 52 | "no match for ‘operator==’ in ‘__first.__gnu_cxx::__normal_iterator::operator* [with _Iterator = std::vector*, _Container = std::vector >, __gnu_cxx::__normal_iterator::reference = std::vector&]() == __val’", 53 | "error: cannot bind rvalue ‘(short unsigned int)((const char*)\"\")’ to ‘short unsigned int&’", 54 | "no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream'} and 'std::array')", 55 | "invalid conversion from `int' to `std::_Rb_tree_node >*'", 56 | //ld 57 | "relocation truncated to fit: R_X86_64_PC32 against symbol `main'", 58 | "(.ARM.exidx.text._ZNSt8_Rb_treeIiSt4pairIKiSt10shared_ptrIN4SWGL7ContextEEESt10_Select1stIS6_ESt4lessIiESaIS6_EE13_Rb_tree_implISA_Lb1EED2Ev[_ZNSt8_Rb_treeIiSt4pairIKiSt10shared_ptrIN4SWGL7ContextEEESt10_Select1stIS6_ESt4lessIiESaIS6_EE13_Rb_tree_implISA_Lb1EED5Ev]+0x0): relocation truncated to fit: R_ARM_PREL31 against `.text._ZNSt8_Rb_treeIiSt4pairIKiSt10shared_ptrIN4SWGL7ContextEEESt10_Select1stIS6_ESt4lessIiESaIS6_EE13_Rb_tree_implISA_Lb1EED2Ev'", 59 | "error adding symbols: DSO missing from command line", 60 | "undefined reference to `main'", 61 | //Cobol 62 | "programmer is impolite", 63 | "programmer is excessively polite", 64 | //actual MemeASM errors 65 | "a decimal number cannot be a pointer", 66 | "a function name cannot be a pointer", 67 | "invalid parameter combination: 64 Bit arithmetic operation commands require the decimal number to be sign-extendable from 32 Bits", 68 | "function does not return", 69 | "an executable cannot be created if no main function exists" 70 | }; 71 | 72 | /** 73 | * Prints an ASCII-Art title and version information. 74 | */ 75 | void printInformationHeader() { 76 | printf(BLU" __ __ _ _ \n"); 77 | printf(" | \\/ | /\\ | | | | \n"); 78 | printf(" | \\ / | ___ _ __ ___ ___ / \\ ___ ___ ___ _ __ ___ | |__ | |_ _ \n"); 79 | printf(" | |\\/| |/ _ \\ '_ ` _ \\ / _ \\ / /\\ \\ / __/ __|/ _ \\ '_ ` _ \\| '_ \\| | | | |\n"); 80 | printf(" | | | | __/ | | | | | __// ____ \\\\__ \\__ \\ __/ | | | | | |_) | | |_| |\n"); 81 | printf(" |_| |_|\\___|_| |_| |_|\\___/_/ \\_\\___/___/\\___|_| |_| |_|_.__/|_|\\__, |\n"); 82 | printf(RESET" A Meme-based programming language. " BLU " __/ |\n"); 83 | printf(" |___/ \n\n"RESET); 84 | printf("For more information, a list of commands and code examples, please visit https://github.com/kammt/MemeAssembly.\n"); 85 | printf("This is the MemeAssembly compiler %s (%s), created by Tobias Kamm.\n\n", versionString, platformSuffix); 86 | } 87 | 88 | /** 89 | * Called if there is an error in the specified file. It prints a "Wait, that's illegal!" ASCII-Art and exits the program 90 | */ 91 | void printErrorASCII() { 92 | printf("\n"); 93 | printf("\n"); 94 | printf(YEL " __ __ _ _ _ _ _ _ _ _ _ _ _ \n"); 95 | printf(" \\ \\ / / (_| | | | | | | | ( ) (_| | | | | | \n"); 96 | printf(" \\ \\ /\\ / __ _ _| |_ | |_| |__ __ _| |_|/ ___ _| | | ___ __ _ __ _| | | \n"); 97 | printf(" \\ \\/ \\/ / _` | | __| | __| '_ \\ / _` | __| / __| | | | |/ _ \\/ _` |/ _` | | | \n"); 98 | printf(" \\ /\\ | (_| | | |_ _ | |_| | | | (_| | |_ \\__ \\ | | | | __| (_| | (_| | |_| \n"); 99 | printf(" \\/ \\/ \\__,_|_|\\__( ) \\__|_| |_|\\__,_|\\__| |___/ |_|_|_|\\___|\\__, |\\__,_|_(_) \n"); 100 | printf(" |/ __/ | \n"); 101 | printf(" |___/ \n" RESET); 102 | } 103 | 104 | /** 105 | * Called when the command 'perfectly balanced as all things should be' is used. It prints a Snap ASCII art 106 | * @param deletedLines the number of lines that got deleted 107 | */ 108 | void printThanosASCII(size_t deletedLines) { 109 | printf("\n"); 110 | printf("\n"); 111 | printf(YEL " _____ \n"); 112 | printf(" / ____| \n"); 113 | printf(" | (___ _ __ __ _ _ __ \n"); 114 | printf(" \\___ \\| '_ \\ / _` | '_ \\ \n"); 115 | printf(" ____) | | | | (_| | |_) | \n"); 116 | printf(" |_____/|_| |_|\\__,_| .__/ \n"); 117 | printf(" | | \n"); 118 | printf(" |_| \n" RESET); 119 | printf(GRN "\nDid you do it?\n" RESET); 120 | printf(MAG "Yes\n" RESET); 121 | printf(GRN "What did it cost?\n" RESET); 122 | printf(MAG "%lu lines of code\n\n" RESET, deletedLines); 123 | } 124 | 125 | /** 126 | * Called when a decimal parameter with value 420 or 69 is encountered. It prints a "Nice" ASCII art 127 | */ 128 | void printNiceASCII() { 129 | printf("\n"); 130 | printf("\n"); 131 | printf("\x1B[38;5;197m" " _ _ _ \n"); 132 | printf("\x1B[38;5;197m" " | \\ | (_) \n"); 133 | printf("\x1B[38;5;198m" " | \\| |_ ___ ___ \n"); 134 | printf("\x1B[38;5;198m" " | . ` | |/ __/ _ \\\n"); 135 | printf("\x1B[38;5;199m" " | |\\ | | (_| __/\n"); 136 | printf("\x1B[38;5;199m" " |_| \\_|_|\\___\\___|\n\n" RESET); 137 | } 138 | 139 | /** 140 | * Prints a debug message. It can be called with a variable number of arguments that will be inserted in the respective places in the format string 141 | */ 142 | void printDebugMessage(logLevel logLevel, char* message, unsigned varArgNum, ...) { 143 | if(logLevel == debug) { 144 | va_list vaList; 145 | va_start(vaList, varArgNum); 146 | 147 | vprintf(message, vaList); 148 | printf("\n"); 149 | } 150 | } 151 | 152 | /** 153 | * Prints an error message. It can be called with a variable number of arguments that will be inserted in the respective places in the format string 154 | * @param inputFileName name of the input file 155 | * @param lineNum the line number in which the error occurred 156 | * @param compileState the current compileState. Needed to increase the error count 157 | * @param message the message (with printf-like formatting) 158 | * @param varArgNum How many variable arguments were passed (important!) 159 | * @param ... variable arguments 160 | */ 161 | void printError(char* inputFileName, unsigned lineNum, struct compileState* compileState, char* message, unsigned varArgNum, ...) { 162 | compileState->compilerErrors++; 163 | 164 | //First, only print the file name and line 165 | printf("%s:%u: " RED "error: " RESET, inputFileName, lineNum); 166 | 167 | if(compileState->compileMode != obfuscated) { 168 | //Initialise va_list to pass it on to vprintf 169 | va_list vaList; 170 | va_start(vaList, varArgNum); 171 | //Now print the custom message with variable args 172 | vprintf(message, vaList); 173 | printf("\n"); 174 | } else { 175 | //Obfuscated mode: print a random error message instead 176 | uint64_t computedIndex = lineNum; 177 | for(size_t i = 0; i < strlen(inputFileName); i++) { 178 | computedIndex += inputFileName[i]; 179 | } 180 | for(size_t i = 0; i < strlen(message); i++) { 181 | computedIndex += message[i]; 182 | } 183 | computedIndex += varArgNum; 184 | 185 | printf("%s\n", randomErrorMessages[computedIndex % (sizeof(randomErrorMessages) / sizeof(char*))]); 186 | } 187 | } 188 | 189 | /** 190 | * Prints a note. It can be called with a variable number of arguments that will be inserted in the respective places in the format string 191 | * @param message the message (with printf-like formatting) 192 | * @param indent whether the entire message should be indented 193 | * @param varArgNum How many variable arguments were passed (important!) 194 | * @param ... variable arguments 195 | */ 196 | void printNote(char* message, bool indent, unsigned varArgNum, ...) { 197 | //Initialise va_list to pass it on to vprintf 198 | va_list vaList; 199 | va_start(vaList, varArgNum); 200 | 201 | //First, only print the file name and line 202 | if(indent) printf("\t"); 203 | printf(MAG "note: " RESET); 204 | //Now print the custom message with variable args 205 | vprintf(message, vaList); 206 | printf("\n"); 207 | } 208 | 209 | /** 210 | * Prints an internal compiler error, after which the compiler terminates 211 | * @param message the message (with printf-like formatting) 212 | * @param report whether to print a message telling the user to report this error 213 | * @param varArgNum How many variable arguments were passed (important!) 214 | * @param ... variable arguments 215 | */ 216 | void printInternalCompilerError(char* message, bool report, unsigned varArgNum, ...) { 217 | //Initialise va_list to pass it on to vprintf 218 | va_list vaList; 219 | va_start(vaList, varArgNum); 220 | 221 | //First, only print the file name and line 222 | fprintf(stderr, RED "Internal compiler error: " RESET); 223 | //Now print the custom message with variable args 224 | vfprintf(stderr, message, vaList); 225 | fprintf(stderr, "\n"); 226 | if(report) fprintf(stderr, "Please report this error at https://github.com/kammt/MemeAssembly/issues/new"); 227 | } 228 | -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 38 | 59 | 61 | 64 | 68 | 72 | 73 | 76 | 80 | 84 | 85 | 88 | 92 | 96 | 97 | 100 | 104 | 108 | 112 | 113 | 121 | 126 | 127 | 135 | 140 | 141 | 149 | 154 | 155 | 163 | 168 | 169 | 178 | 188 | 198 | 207 | 216 | 226 | 227 | 231 | 234 | 237 | 242 | 247 | 248 | 253 | 254 | eme 266 | ssembly 277 | 280 | 287 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /images/logo_square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 38 | 59 | 61 | 64 | 68 | 72 | 73 | 76 | 80 | 84 | 85 | 88 | 92 | 96 | 97 | 114 | 122 | 127 | 128 | 136 | 141 | 142 | 150 | 155 | 156 | 164 | 169 | 170 | 179 | 182 | 186 | 190 | 191 | 201 | 209 | 214 | 215 | 224 | 233 | 236 | 240 | 244 | 245 | 254 | 255 | 259 | 265 | 268 | 271 | 276 | 281 | 282 | 287 | 288 | 291 | 298 | 303 | 304 | 305 | 306 | -------------------------------------------------------------------------------- /compiler/parser/fileParser.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "fileParser.h" 21 | #include "parser.h" 22 | #include 23 | #include 24 | 25 | #include "../logger/log.h" 26 | 27 | #ifdef WINDOWS 28 | /* strtok_r does not exist on Windows and instead is strtok_s. Use a preprocessor directive to replace all occurrences */ 29 | # define strtok_r strtok_s 30 | #endif 31 | 32 | extern struct command commandList[]; 33 | 34 | //Used to pseudo-random generation when using bully mode 35 | uint64_t computedIndex = 69; 36 | 37 | /** 38 | * Removes the \n from a string if it is present at the end of the string 39 | */ 40 | void removeLineBreaksAndTabs(char* line) { 41 | size_t i = strlen(line) - 1; 42 | while (line[i] == '\t' || line[i] == '\n' || line[i] == ' ') { 43 | i--; 44 | } 45 | line[i + 1] = '\0'; 46 | } 47 | 48 | /** 49 | * Checks whether this line should be skipped or not 50 | * @return 1 if it is of interest (=> code), 0 if it should be skipped (e.g. it is a comment or it's empty) 51 | */ 52 | int isLineOfInterest(const char* line, ssize_t lineLength) { 53 | //To support tabbed comments, we need to determine when the text actually starts 54 | int i = 0; 55 | while (line[i] == '\t' || line[i] == ' ') { 56 | i++; //Increase our variable as long as there are only tabs or spaces 57 | } 58 | 59 | if(line[i] != '\n' && lineLength != i && strncmp((line + i), commentStart, strlen(commentStart)) != 0) { 60 | return 1; 61 | } 62 | return 0; 63 | } 64 | 65 | /** 66 | * A basic implementation of a getline-function. 67 | * Reads a full line and stores it in lineptr. If lineptr is NULL, it will be initialised and n will be set accordingly. When too little is allocated, 128 more Bytes will be added 68 | * @param lineptr a pointer to the current storage for lines. May be NULL 69 | * @param n must be the size of lineptr and will be updated by this function 70 | * @param stream from where to read 71 | * @return the number of bytes read if successful, -1 on error (e.g. EOF found, malloc/realloc failed) 72 | */ 73 | ssize_t getLine(char **restrict lineptr, size_t *restrict n, FILE *restrict stream) { 74 | if(*lineptr == NULL) { 75 | if(!(*lineptr = malloc(128))) { 76 | return -1; 77 | } 78 | *n = 128; 79 | } 80 | 81 | char* result = *lineptr; 82 | char c = 'c'; 83 | ssize_t bytesRead = 0; 84 | while(c != '\n') { 85 | size_t readRes = fread(&c, 1, 1, stream); 86 | if(readRes == 0) { 87 | if(bytesRead == 0) { 88 | return -1; 89 | } 90 | //If EOF was found somewhere while reading from file, still return this string 91 | break; 92 | } 93 | 94 | *result = c; 95 | bytesRead++; 96 | result++; 97 | if(bytesRead == (ssize_t) *n) { 98 | *n += 128; 99 | *lineptr = realloc(*lineptr, *n); 100 | result = *lineptr + bytesRead; 101 | 102 | if(!*lineptr) { 103 | return -1; 104 | } 105 | } 106 | } 107 | 108 | //On Windows, file endings are done using \r\n. This means that there will be a \r at the end of every string, breaking the entire compiler 109 | //To fix this, check if the string ends with \r\n. If so, replace it with \n 110 | //However, we first need to check that we are not reading out of bounds 111 | if(((uintptr_t) result - 2 >= (uintptr_t) *lineptr) && *(result - 2) == '\r') { 112 | *(result - 2) = '\n'; 113 | result--; 114 | } 115 | //We got to the end of a line or the end of a file, now append a '\0' 116 | *result = '\0'; 117 | return bytesRead; 118 | } 119 | 120 | /** 121 | * Counts the lines of code in a memeasm file. A line counts as a line of code if: 122 | * - it does not start with "What the hell happened here?" (a comment) 123 | * - it is not empty 124 | * @param inputFile the input file 125 | * @return the number of lines of code in the file 126 | */ 127 | size_t getLinesOfCode(FILE *inputFile) { 128 | size_t loc = 0; 129 | char* line = NULL; 130 | size_t len; 131 | ssize_t lineLength; 132 | 133 | while((lineLength = getLine(&line, &len, inputFile)) != -1) { 134 | if(isLineOfInterest(line, lineLength) == 1) { 135 | loc++; 136 | } 137 | } 138 | CHECK_ALLOC(line); 139 | 140 | rewind(inputFile); 141 | free(line); 142 | return loc; 143 | } 144 | 145 | /** 146 | * Frees the memory of variables after they are not needed anymore 147 | * @param parsedCommand the parsedCommand struct 148 | * @param numberOfParameters how many variables are currently in use (allocated) 149 | */ 150 | void freeAllocatedMemory(struct parsedCommand parsedCommand, int numberOfParameters) { 151 | for(int i = 0; i < numberOfParameters; i++) { 152 | free(parsedCommand.parameters[i]); 153 | } 154 | } 155 | 156 | /** 157 | * Parses a provided line of code and attempts to match it to a command 158 | * @param inputFileName the origin file. Required for error printing 159 | * @param line the line as a string 160 | * @param lineNum the line number in the origin file. Required for error printing 161 | * @param compileState the current compile state 162 | * @return 163 | */ 164 | struct parsedCommand parseLine(char* inputFileName, size_t lineNum, char* line, struct compileState* compileState) { 165 | struct parsedCommand parsedCommand; 166 | parsedCommand.lineNum = lineNum; //Set the line number 167 | parsedCommand.translate = 1; 168 | 169 | //Temporarily save the line on the stack to be able to restore when a comparison failed 170 | char lineCpy[strlen(line) + 1]; 171 | strcpy(lineCpy, line); 172 | 173 | //Define save pointers for strtok_r 174 | char *savePtrLine; 175 | char *savePtrPattern; 176 | 177 | //Iterate through all possible commands 178 | for(int i = 0; i < NUMBER_OF_COMMANDS - 2; i++) { 179 | strcpy(lineCpy, line); 180 | savePtrLine = NULL; 181 | savePtrPattern = NULL; 182 | 183 | //Copy the current command pattern out of read-only memory 184 | char commandString[strlen(commandList[i].pattern) + 1]; 185 | strcpy(commandString, commandList[i].pattern); 186 | 187 | //Tokenize both strings. Tabs at the beginning are allowed and should be ignored, hence they are a delimiter 188 | char *commandToken = strtok_r(commandString, " \t", &savePtrPattern); 189 | char *lineToken = strtok_r(lineCpy, " \t", &savePtrLine); 190 | 191 | int numberOfParameters = 0; 192 | parsedCommand.isPointer = 0; 193 | 194 | //Enter the comparison loop 195 | while (commandToken != NULL && lineToken != NULL) { 196 | printDebugMessage(compileState->logLevel, "\tcomparing with %s", 1, commandToken); 197 | 198 | if(strstr(commandToken, "{p}") != NULL) { 199 | //First check that everything before and after the {p} matches 200 | 201 | //The difference between the starting address of commandToken and the starting address of {p} is the number of characters before {p} 202 | size_t charsBefore = ((size_t) strstr(commandToken, "{p}") - (size_t) commandToken); 203 | //The difference between the total string length of commandToken and (charsBefore + 3) 204 | size_t charsAfter = (strlen(commandToken) - charsBefore - 3); 205 | 206 | if(strncmp(commandToken, lineToken, charsBefore) == 0 && 207 | strncmp(commandToken + charsBefore + 3, lineToken + strlen(lineToken) - charsAfter, charsAfter) == 0) { 208 | printDebugMessage(compileState->logLevel, "\t\t%s contains a parameter", 1, lineToken); 209 | 210 | size_t parameterLength = strlen(lineToken) - charsBefore - charsAfter; 211 | 212 | //When allocating space for a function name on MacOS, we need an extra _ -prefix, hence +2 213 | char *variable = malloc(parameterLength + 2); 214 | CHECK_ALLOC(variable); 215 | 216 | #ifdef MACOS 217 | if (i == 0 || i == 4) { 218 | strcpy(variable, "_"); 219 | strncat(variable, lineToken, parameterLength); 220 | variable[parameterLength + 1] = '\0'; 221 | } else { 222 | #endif 223 | //On Windows and Linux, only this line is executed 224 | strncpy(variable, lineToken, parameterLength); 225 | variable[parameterLength] = '\0'; 226 | #ifdef MACOS 227 | } 228 | #endif 229 | 230 | parsedCommand.parameters[numberOfParameters++] = variable; 231 | 232 | //If the line after this parameter contains "do you know de wey", mark it as a pointer 233 | if (savePtrLine != NULL && strlen(savePtrLine) >= strlen(pointerSuffix) && 234 | strncmp(pointerSuffix, savePtrLine, strlen(pointerSuffix)) == 0) { 235 | printDebugMessage(compileState->logLevel, 236 | "\t\t\t'do you know de wey' was found, interpreting as pointer", 0); 237 | //If another parameter is already marked as a variable, print an error 238 | //This error is skipped when bully mode is on 239 | if (parsedCommand.isPointer != 0 && compileState->compileMode != bully) { 240 | printError(inputFileName, lineNum, compileState, 241 | "Only one parameter is allowed to be a pointer", 0); 242 | } 243 | parsedCommand.isPointer = (uint8_t) numberOfParameters; 244 | //Move the save pointer so that "do you know de wey" is not tokenized by strtok_r 245 | savePtrLine += strlen(pointerSuffix); 246 | } 247 | } else { 248 | //Characters before and after parameter do not match 249 | printDebugMessage( compileState->logLevel, "\t\tMatching failed - chars before or after {p} mismatching, attempting to match next command", 0); 250 | freeAllocatedMemory(parsedCommand, numberOfParameters); 251 | break; 252 | } 253 | } else if(strcmp(commandToken, lineToken) != 0) { 254 | //If both tokens do not match, try the next command 255 | printDebugMessage( compileState->logLevel, "\t\tMatching failed, attempting to match next command", 0); 256 | freeAllocatedMemory(parsedCommand, numberOfParameters); 257 | break; 258 | } 259 | 260 | //Tokenize both strings again. This time, only spaces are allowed 261 | commandToken = strtok_r(NULL, " ", &savePtrPattern); 262 | lineToken = strtok_r(NULL, " ", &savePtrLine); 263 | } 264 | 265 | if(commandToken != NULL && lineToken != NULL) { 266 | continue; 267 | } 268 | 269 | /*Either the line or the command pattern have reached their end. We now have to check what caused the problem 270 | * - if both are NULL, then there is no problem! 271 | * - if the commandToken is NULL, then we should have been at the end of the line. Check if the rest is equal to 'or draw 25'. If not, try the next command 272 | * - if the token is NULL, then the line is too short, try the next command 273 | */ 274 | if(commandToken == NULL && lineToken == NULL) { 275 | parsedCommand.opcode = (uint8_t) i; 276 | return parsedCommand; 277 | } else if(lineToken == NULL) { 278 | printDebugMessage(compileState->logLevel, "\t\tMatching failed, lineToken is NULL while commandToken is not. Attempting to match next command", 0); 279 | freeAllocatedMemory(parsedCommand, numberOfParameters); 280 | continue; 281 | //If the current token is 'or' and the rest of the string is only 'draw 25', then set the opcode as "or draw 25" and return 282 | } else if(strcmp(lineToken, orDraw25Start) == 0 && strlen(savePtrLine) == strlen(orDraw25End) && strncmp(orDraw25End, savePtrLine, strlen(orDraw25End)) == 0) { 283 | printDebugMessage(compileState->logLevel, "\t\t'or draw 25' was found, replacing opcode", 0); 284 | //Before we replace the opcode though, we need to free memory that was allocated for parameters 285 | for(int j = 0; j < commandList[i].usedParameters; j++) { 286 | free(parsedCommand.parameters[j]); 287 | } 288 | //Now we can change the opcode and return the struct 289 | parsedCommand.opcode = OR_DRAW_25_OPCODE; 290 | return parsedCommand; 291 | } 292 | } 293 | 294 | if(compileState->compileMode == bully) { 295 | /* 296 | * In bully mode, we replace this non-working command with a valid one 297 | * But we want to make it deterministic, with a lot of possible commands, 298 | * so we take the sum of ascii characters in this line, and use this value 299 | * to create the random command 300 | * 301 | * The variable "computedIndex" is global, meaning that the value 302 | * is dependent on the previous illegal commands - *perfection* 303 | */ 304 | const char* randomParams[] = {"rax", "rcx", "rbx", "r8", "r9", "r10", "r12", "rsp", "rbp", "ax", "al", "r8b", "r9d", "r14b", "99", "1238", "12", "420", "987654321", "8", "9", "69", "8268", "2", "_", "a", "b", "d", "f", "F", "sigreturn", "uaauuaa", "uau", "uu", "main", "gets", "srand", "mprotect", "au", "uwu", "space"}; 305 | unsigned randomParamCount = sizeof randomParams / sizeof(char*); 306 | 307 | for(size_t i = 0; i < strlen(line); i++) { 308 | computedIndex += line[i]; 309 | } 310 | computedIndex = ((computedIndex * lineNum) % 420) * inputFileName[0]; 311 | 312 | parsedCommand.opcode = computedIndex % (NUMBER_OF_COMMANDS - 1); 313 | if(commandList[parsedCommand.opcode].usedParameters > 0) { 314 | parsedCommand.parameters[0] = strdup(randomParams[computedIndex % randomParamCount]); 315 | CHECK_ALLOC(parsedCommand.parameters[0]); 316 | } 317 | if (commandList[parsedCommand.opcode].usedParameters > 1) { 318 | parsedCommand.parameters[1] = strdup(randomParams[(computedIndex * inputFileName[0]) % randomParamCount]); 319 | CHECK_ALLOC(parsedCommand.parameters[1]); 320 | } 321 | } else { 322 | parsedCommand.opcode = INVALID_COMMAND_OPCODE; 323 | printError(inputFileName, lineNum, compileState, "Invalid command: \"%s\"", 1, line); 324 | //Any error will increase the "compilationErrors" variable in log.c, meaning that we can safely return something that doesn't make sense 325 | //We don't exit immediately because we want to print every error possible 326 | } 327 | return parsedCommand; 328 | } 329 | 330 | 331 | /** 332 | * Parses an input file line by line and fills a provided struct commandsArray 333 | */ 334 | void parseCommands(FILE *inputFile, char* inputFileName, struct compileState* compileState, struct commandsArray* commandsArray) { 335 | //Variable declarations 336 | char* line = NULL; 337 | size_t len = 0; 338 | ssize_t lineLength; 339 | 340 | //First, we create an array of command structs 341 | size_t loc = getLinesOfCode(inputFile); 342 | printDebugMessage(compileState->logLevel, "The number of lines are %lu", 1, loc); 343 | 344 | if(loc == 0) { 345 | if(compileState->compileMode != bully) { 346 | printError(inputFileName, 0, compileState, "file does not contain any commands", 0); 347 | } 348 | 349 | commandsArray->arrayPointer = NULL; 350 | commandsArray->size = 0; 351 | return; 352 | } 353 | 354 | struct parsedCommand *commands = calloc(sizeof(struct parsedCommand), loc); 355 | CHECK_ALLOC(commands); 356 | printDebugMessage( compileState->logLevel, "Struct array was created successfully", 0); 357 | 358 | //Iterate through the file again, this time parsing each line of interest and adding it to our command struct array 359 | int i = 0; //The number of structs in the array 360 | int lineNumber = 1; //The line number we are currently on. We differentiate between number of commands and number of lines to print the correct line number in case of an error 361 | 362 | //Parse the file line by line 363 | while((lineLength = getLine(&line, &len, inputFile)) != -1) { 364 | //Check if the line contains actual code or if it's empty/contains comments 365 | if(isLineOfInterest(line, lineLength) == 1) { 366 | //Remove \n from the end of the line 367 | removeLineBreaksAndTabs(line); 368 | printDebugMessage( compileState->logLevel, "Parsing line: %s", 1, line); 369 | //Parse the command and add the returned struct into the array 370 | *(commands + i) = parseLine(inputFileName, lineNumber, line, compileState); 371 | //Increase our number of structs in the array 372 | i++; 373 | } 374 | lineNumber++; 375 | } 376 | 377 | commandsArray->size = loc; 378 | commandsArray->arrayPointer = commands; 379 | 380 | free(line); 381 | } 382 | 383 | -------------------------------------------------------------------------------- /compiler/compiler.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | #include "compiler.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "parser/parser.h" 28 | #include "analyser/analyser.h" 29 | #include "translator/translator.h" 30 | #include "logger/log.h" 31 | 32 | const struct command commandList[NUMBER_OF_COMMANDS] = { 33 | ///Functions 34 | { 35 | .pattern = "I like to have fun, fun, fun, fun, fun, fun, fun, fun, fun, fun {p}", 36 | .commandType = COMMAND_TYPE_FUNC_DEF, 37 | .usedParameters = 1, 38 | .allowedParamTypes = {PARAM_FUNC_NAME}, 39 | .analysisFunction = &analyseFunctions, 40 | .translationPattern = "{0}:" 41 | }, 42 | { 43 | .pattern = "right back at ya, buckaroo", 44 | .commandType = COMMAND_TYPE_FUNC_RETURN, 45 | .usedParameters = 0, 46 | .analysisFunction = NULL, 47 | .translationPattern = "ret" 48 | }, 49 | { 50 | .pattern = "no, I don't think I will", 51 | .commandType = COMMAND_TYPE_FUNC_RETURN, 52 | .usedParameters = 0, 53 | .analysisFunction = NULL, 54 | .translationPattern = "mov rax, 1\n\tret" 55 | }, 56 | { 57 | .pattern = "I see this as an absolute win", 58 | .commandType = COMMAND_TYPE_FUNC_RETURN, 59 | .usedParameters = 0, 60 | .analysisFunction = NULL, 61 | .translationPattern = "xor rax, rax\n\tret" 62 | }, 63 | { 64 | .pattern = "{p}: whomst has summoned the almighty one", 65 | .commandType = COMMAND_TYPE_FUNC_CALL, 66 | .usedParameters = 1, 67 | .allowedParamTypes = {PARAM_FUNC_NAME}, 68 | .analysisFunction = &analyseCall, 69 | .translationPattern = "call {0}" 70 | }, 71 | 72 | ///Stack operations 73 | { 74 | .pattern = "stonks {p}", 75 | .usedParameters = 1, 76 | .allowedParamTypes = {PARAM_REG64 | PARAM_DECIMAL | PARAM_CHAR}, 77 | .analysisFunction = NULL, 78 | .translationPattern = "push {0}" 79 | }, 80 | { 81 | .pattern = "not stonks {p}", 82 | .usedParameters = 1, 83 | .allowedParamTypes = {PARAM_REG64}, 84 | .analysisFunction = NULL, 85 | .translationPattern = "pop {0}" 86 | }, 87 | 88 | ///Logical Operations 89 | { 90 | .pattern = "bitconneeeeeeect {p} {p}", 91 | .usedParameters = 2, 92 | .analysisFunction = NULL, 93 | .allowedParamTypes = {PARAM_REG, PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 94 | .translationPattern = "and {0}, {1}" 95 | }, 96 | { 97 | .pattern = "{p} \\s", 98 | .usedParameters = 1, 99 | .analysisFunction = NULL, 100 | .allowedParamTypes = {PARAM_REG}, 101 | .translationPattern = "not {0}" 102 | }, 103 | 104 | ///Register Manipulation 105 | { 106 | .pattern = "sneak 100 {p}", 107 | .usedParameters = 1, 108 | .allowedParamTypes = {PARAM_REG}, 109 | .analysisFunction = NULL, 110 | .translationPattern = "xor {0}, {0}" 111 | }, 112 | { 113 | .pattern = "{p} is brilliant, but I like {p}", 114 | .commandType = COMMAND_TYPE_MOV, 115 | .usedParameters = 2, 116 | .allowedParamTypes = {PARAM_REG, PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 117 | .analysisFunction = NULL, 118 | .translationPattern = "mov {0}, {1}" 119 | }, 120 | 121 | ///Arithmetic operations 122 | { 123 | .pattern = "upvote {p}", 124 | .usedParameters = 1, 125 | .allowedParamTypes = {PARAM_REG}, 126 | .analysisFunction = NULL, 127 | .translationPattern = "add {0}, 1" 128 | }, 129 | { 130 | .pattern = "downvote {p}", 131 | .usedParameters = 1, 132 | .allowedParamTypes = {PARAM_REG}, 133 | .analysisFunction = NULL, 134 | .translationPattern = "sub {0}, 1" 135 | }, 136 | { 137 | .pattern = "parry {p} you filthy casual {p}", 138 | .usedParameters = 2, 139 | .allowedParamTypes = {PARAM_REG | PARAM_DECIMAL | PARAM_CHAR, PARAM_REG}, 140 | .analysisFunction = NULL, 141 | .translationPattern = "sub {1}, {0}" 142 | }, 143 | { 144 | .pattern = "{p} units are ready, with {p} more well on the way", 145 | .usedParameters = 2, 146 | .allowedParamTypes = {PARAM_REG, PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 147 | .analysisFunction = NULL, 148 | .translationPattern = "add {0}, {1}" 149 | }, 150 | { 151 | .pattern = "upgrades, people. Upgrades {p}", 152 | .usedParameters = 1, 153 | .allowedParamTypes = {PARAM_REG}, 154 | .analysisFunction = NULL, 155 | .translationPattern = "shl {0}, 1" 156 | }, 157 | { 158 | .pattern = "they had us in the first half, not gonna lie {p}", 159 | .usedParameters = 1, 160 | .allowedParamTypes = {PARAM_REG}, 161 | .analysisFunction = NULL, 162 | .translationPattern = "shr {0}, 1" 163 | }, 164 | { 165 | .pattern = "{p} is getting out of hand, now there are {p} of them", 166 | .usedParameters = 2, 167 | .allowedParamTypes = {PARAM_REG64 | PARAM_REG32, PARAM_REG64 | PARAM_REG32 | PARAM_DECIMAL | PARAM_CHAR}, 168 | .analysisFunction = NULL, 169 | .translationPattern = "imul {0}, {1}" 170 | }, 171 | { 172 | .pattern = "look at what {p} needs to mimic a fraction of {p}", 173 | .usedParameters = 2, 174 | .allowedParamTypes = {PARAM_REG64 | PARAM_DECIMAL | PARAM_CHAR, PARAM_REG64}, 175 | .analysisFunction = NULL, 176 | .translationPattern = "mov QWORD PTR [rip + .Ltmp64], {0}\n\t" 177 | "push rdx\n\t" 178 | "cqo\n\t" 179 | "push rax\n\t" 180 | "mov rax, {1}\n\t" 181 | "idiv QWORD PTR [rip + .Ltmp64]\n\t" 182 | "push rax\n\t" 183 | "mov rax, [rsp + 8]\n\t" 184 | "pop {1}\n\t" 185 | "add rsp, 8\n\t" 186 | "pop rdx\n\t" 187 | }, 188 | { 189 | .pattern = "{p} UNLIMITED POWER {p}", 190 | .usedParameters = 2, 191 | .allowedParamTypes = {PARAM_REG64, PARAM_REG64 | PARAM_DECIMAL | PARAM_CHAR}, 192 | .analysisFunction = NULL, 193 | .translationPattern = "mov QWORD PTR [rip + .Ltmp64], {1}\n\t" 194 | "cmp QWORD PTR [rip + .Ltmp64], 0\n\t" 195 | "jne 2f\n\t" //Jump forward to 2 if not zero 196 | //y is zero, load 1 and jump to the end (numeric label 4) 197 | "xor {0}, {0}\n\t" 198 | "inc {0}\n\t" 199 | "jmp 4f\n\t" 200 | //Now loop until our y is zero 201 | "2: push {0}\n\t" //Preparation: push x to the stack to remember it for later 202 | "dec QWORD PTR [rip + .Ltmp64]\n\t" 203 | "3: imul {0}, [rsp]\n\t" 204 | "dec QWORD PTR [rip + .Ltmp64]\n\t" 205 | "jnz 3b\n\t" //If the result was not zero (ZF set from subtraction), jump back 206 | "add rsp, 8\n\t" 207 | "4:\n\t" 208 | }, 209 | 210 | 211 | ///Jumps and Jump Markers 212 | { 213 | .pattern = "upgrade", 214 | .usedParameters = 0, 215 | .analysisFunction = &analyseJumpMarkers, 216 | .translationPattern = ".LUpgradeMarker_{F}:" 217 | }, 218 | { 219 | .pattern = "fuck go back", 220 | .usedParameters = 0, 221 | .analysisFunction = NULL, 222 | .translationPattern = "jmp .LUpgradeMarker_{F}" 223 | }, 224 | { 225 | .pattern = "banana", 226 | .usedParameters = 0, 227 | .analysisFunction = &analyseJumpMarkers, 228 | .translationPattern = ".LBananaMarker_{F}:" 229 | }, 230 | { 231 | .pattern = "where banana", 232 | .usedParameters = 0, 233 | .analysisFunction = NULL, 234 | .translationPattern = "jmp .LBananaMarker_{F}" 235 | }, 236 | { 237 | .pattern = "monke {p}", 238 | .usedParameters = 1, 239 | .allowedParamTypes = {PARAM_MONKE_LABEL}, 240 | .analysisFunction = &analyseMonkeMarkers, 241 | .translationPattern = ".L{0}:" 242 | }, 243 | { 244 | .pattern = "return to monke {p}", 245 | .usedParameters = 1, 246 | .allowedParamTypes = {PARAM_MONKE_LABEL}, 247 | .analysisFunction = NULL, 248 | .translationPattern = "jmp .L{0}" 249 | }, 250 | { 251 | .pattern = "who would win? {p} or {p}", 252 | .usedParameters = 2, 253 | .allowedParamTypes = {PARAM_REG, PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 254 | .analysisFunction = &analyseWhoWouldWinCommands, 255 | .translationPattern = "cmp {0}, {1}\n\tjg .L{0}Wins_{F}\n\tjl .L{1}Wins_{F}" 256 | }, 257 | { 258 | .pattern = "{p} wins", 259 | .usedParameters = 1, 260 | .allowedParamTypes = {PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 261 | .analysisFunction = NULL, 262 | .translationPattern = ".L{0}Wins_{F}:" 263 | }, 264 | { 265 | .pattern = "corporate needs you to find the difference between {p} and {p}", 266 | .usedParameters = 2, 267 | .allowedParamTypes = {PARAM_REG, PARAM_REG | PARAM_DECIMAL | PARAM_CHAR}, 268 | .analysisFunction = &analyseTheyreTheSamePictureCommands, 269 | .translationPattern = "cmp {0}, {1}\n\tje .LSamePicture_{F}" 270 | }, 271 | { 272 | .pattern = "they're the same picture", 273 | .usedParameters = 0, 274 | .analysisFunction = NULL, 275 | .translationPattern = ".LSamePicture_{F}:" 276 | }, 277 | 278 | ///IO-Operations 279 | { 280 | .pattern = "what can I say except {p}", 281 | .usedParameters = 1, 282 | .analysisFunction = NULL, 283 | .allowedParamTypes = {PARAM_REG8 | PARAM_CHAR}, 284 | .translationPattern = "mov BYTE PTR [rip + .LCharacter], {0}\n\t" 285 | "test rsp, 0xF\n\t" 286 | "jz 1f\n\t" 287 | "sub rsp, 8\n\t" 288 | "call writechar\n\t" 289 | "add rsp, 8\n\t" 290 | "jmp 2f\n\t" 291 | "1: call writechar\n\t" 292 | "2:\n\t" 293 | }, 294 | { 295 | .pattern = "let me in. LET ME IIIIIIIIN {p}", 296 | .usedParameters = 1, 297 | .analysisFunction = NULL, 298 | .allowedParamTypes = {PARAM_REG8}, 299 | .translationPattern = "test rsp, 0xF\n\t" 300 | "jz 1f\n\t" 301 | "sub rsp, 8\n\t" 302 | "call readchar\n\t" 303 | "add rsp, 8\n\t" 304 | "jmp 2f\n\t" 305 | "1: call readchar\n\t" 306 | "2:\n\t" 307 | "mov {0}, BYTE PTR [rip + .LCharacter]\n\t" 308 | }, 309 | 310 | ///Random commands 311 | { 312 | .pattern = "guess I'll die", 313 | .usedParameters = 0, 314 | .analysisFunction = NULL, 315 | .translationPattern = "mov rax, [69]" 316 | }, 317 | { 318 | .pattern = "confused stonks", 319 | .usedParameters = 0, 320 | .analysisFunction = &setConfusedStonksJumpLabel, 321 | .translationPattern = "jmp .LConfusedStonks_{F}" 322 | }, 323 | { 324 | .pattern = "perfectly balanced as all things should be", 325 | .usedParameters = 0, 326 | .analysisFunction = &chooseLinesToBeDeleted, 327 | .translationPattern = "" 328 | }, 329 | { 330 | .pattern = "wait, that's illegal", 331 | .usedParameters = 0, 332 | .analysisFunction = NULL, 333 | .translationPattern = "xor rbx, rbx\n\txor rbp, rbp\n\txor r12, r12\n\txor r13 r13" 334 | }, 335 | { 336 | .pattern = "oh no! anyway", 337 | .usedParameters = 0, 338 | .analysisFunction = NULL, 339 | .translationPattern = "nop" 340 | }, 341 | { 342 | .pattern = "it's over 9000 {p}", 343 | .usedParameters = 1, 344 | .allowedParamTypes = {PARAM_REG}, 345 | .analysisFunction = NULL, 346 | .translationPattern = "cmp {0}, 9000\n\tjg 1f\n\thlt\n\t1:" 347 | }, 348 | { 349 | .pattern = "refuses to elaborate and leaves", 350 | .usedParameters = 0, 351 | .analysisFunction = NULL, 352 | .translationPattern = "mov rbp, rsp\n\tpop rsp" 353 | }, 354 | { 355 | .pattern = "you shall not pass!", 356 | .usedParameters = 0, 357 | .analysisFunction = NULL, 358 | .translationPattern = "1: xor rax, rax\n\tjmp 1b" 359 | }, 360 | { 361 | .pattern = "Houston, we have a problem", 362 | .usedParameters = 0, 363 | .analysisFunction = NULL, 364 | .translationPattern = "xor rsp, rsp" 365 | }, 366 | { 367 | .pattern = "it's dangerous to go alone, take {p}", 368 | .usedParameters = 1, 369 | .allowedParamTypes = {PARAM_REG64 | PARAM_REG32 | PARAM_REG16}, 370 | .analysisFunction = NULL, 371 | .translationPattern = "rdrand {0}" 372 | }, 373 | { 374 | .pattern = "we need air support", 375 | .usedParameters = 0, 376 | .analysisFunction = NULL, 377 | .translationPattern = "syscall" 378 | }, 379 | 380 | ///Debug commands 381 | { 382 | .pattern = "it's a trap", 383 | .usedParameters = 0, 384 | .analysisFunction = NULL, 385 | .translationPattern = "int3" 386 | }, 387 | //Insert commands above this one 388 | { 389 | .pattern = "or draw 25", 390 | .usedParameters = 0, 391 | .analysisFunction = NULL, 392 | .translationPattern = "add eax, 25" 393 | }, 394 | { 395 | .pattern = "", 396 | .usedParameters = 0, 397 | .analysisFunction = NULL, 398 | .translationPattern = "0" 399 | } 400 | }; 401 | 402 | 403 | 404 | /** 405 | * 406 | * @param compileState a struct containing all necessary infos. Most notably, it contains the outputMode, optimisation level and all parsed input files 407 | * @param outputFileName the name of the output file 408 | */ 409 | void compile(struct compileState compileState, char* outputFileName) { 410 | ///Analysis 411 | analyseCommands(&compileState); 412 | 413 | //Analysis done. If any errors occurred until now, print to stderr and exit 414 | if(compileState.compilerErrors > 0) { 415 | printErrorASCII(); 416 | fprintf(stderr, "Compilation failed with %u error(s), please check your code and try again.\n", compileState.compilerErrors); 417 | exit(EXIT_FAILURE); 418 | } 419 | 420 | ///Translation 421 | FILE* output; 422 | int gccResult = 0; 423 | //When generating an assembly file, we open the output file in writing mode directly 424 | if(compileState.outputMode == assemblyFile) { 425 | output = fopen(outputFileName, "w") ; 426 | if(output == NULL) { 427 | perror("Failed to open output file"); 428 | exit(EXIT_FAILURE); 429 | } 430 | //When letting gcc do the work for us (object file or executable), we just pipe the code into gcc via stdin 431 | } else { 432 | char* commandPrefix; 433 | if(compileState.outputMode == objectFile) { 434 | #ifndef LINUX 435 | commandPrefix = "gcc -w -O -c -x assembler - -o"; 436 | #else 437 | commandPrefix = "gcc -z execstack -w -O -c -x assembler - -o"; 438 | #endif 439 | } else { 440 | #ifndef LINUX 441 | commandPrefix = "gcc -w -O -x assembler - -o"; 442 | #else 443 | commandPrefix = "gcc -z execstack -w -O -no-pie -x assembler - -o"; //-no-pie is only defined because for some reason, the generated stabs info does not work when a PIE object is generated 444 | #endif 445 | } 446 | 447 | char command[strlen(commandPrefix) + strlen(outputFileName) + 1]; 448 | strcpy(command, commandPrefix); 449 | strcat(command, outputFileName); 450 | 451 | // Pipe assembler code directly to GCC via stdin 452 | output = popen(command, "w"); 453 | } 454 | 455 | writeToFile(&compileState, output); 456 | 457 | if(compileState.outputMode == assemblyFile) { 458 | fclose(output); 459 | } else { 460 | gccResult = pclose(output); 461 | } 462 | 463 | if(gccResult != 0) { 464 | fprintf(stderr, "gcc exited unexpectedly with exit code %d. If you did not expect this to happen, please report this issue at https://github.com/kammt/MemeAssembly/issues so that it can be fixed\n", gccResult); 465 | exit(EXIT_FAILURE); 466 | } else { 467 | exit(EXIT_SUCCESS); 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /compiler/translator/translator.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the MemeAssembly compiler. 3 | 4 | Copyright © 2021-2023 Tobias Kamm and contributors 5 | 6 | MemeAssembly is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | MemeAssembly is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with MemeAssembly. If not, see . 18 | */ 19 | 20 | #include "translator.h" 21 | #include "../logger/log.h" 22 | #include "../analyser/functions.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | ///STABS flags 30 | #define N_SO 100 31 | #define N_SLINE 68 32 | #define N_FUN 36 33 | #define N_LBRAC 0xc0 34 | #define N_RBRAC 0xe0 35 | 36 | extern const char* const versionString; 37 | extern const struct command commandList[]; 38 | 39 | //Used to pseudo-random generation when using bully mode 40 | extern uint64_t computedIndex; 41 | 42 | const char* const martyrdomCode = "push rax\n" 43 | " push rdi\n" 44 | " push rsi\n" 45 | " push rdx\n" 46 | " push r10\n" 47 | " push rcx\n" 48 | " push r11\n" 49 | " \n" 50 | " lea rax, [rip + killParent]\n" 51 | " mov [rip + .Lsa_handler], rax\n" 52 | #ifdef MACOS 53 | //For some reason, signaling SIGINT on MacOS leads to a segmentation fault if the second qword of the sigaction struct 54 | //doesn't contain the address as well 55 | " mov [rip + .Lsa_handler_2], rax\n" 56 | #endif 57 | "\n" 58 | #ifdef LINUX 59 | " mov rax, 13\n" 60 | #else 61 | " mov rax, 0x200002E\n" 62 | #endif 63 | " mov rdi, 2\n" 64 | " lea rsi, [rip + .LsigStruct]\n" 65 | " xor rdx, rdx\n" 66 | " mov r10, 8\n" 67 | " syscall\n" 68 | " \n" 69 | " pop r11\n" 70 | " pop rcx\n" 71 | " pop r10\n" 72 | " pop rdx\n" 73 | " pop rsi\n" 74 | " pop rdi\n" 75 | " pop rax\n\n"; 76 | 77 | /** 78 | * Creates the first STABS entry in which the origin file is stored 79 | * @param outputFile the output file 80 | */ 81 | void stabs_writeFileInfo(FILE *outputFile, char* inputFileString) { 82 | //Check if the input file string starts with a /. If it does, it is an absolute path 83 | char cwd[PATH_MAX + 1]; 84 | if(inputFileString[0] == '/') { 85 | fprintf(outputFile, ".stabs \"%s\", %d, 0, 0, .Ltext0\n", inputFileString, N_SO); 86 | } else { 87 | fprintf(outputFile, ".stabs \"%s/%s\", %d, 0, 0, .Ltext0\n", getcwd(cwd, PATH_MAX), inputFileString, N_SO); 88 | } 89 | } 90 | 91 | /** 92 | * Creates a function info STABS of a given function 93 | * @param outputFile the output file 94 | * @param functionName the name of the function 95 | */ 96 | void stabs_writeFunctionInfo(FILE *outputFile, char* functionName) { 97 | fprintf(outputFile, ".stabs \"%s:F1\", %d, 0, 0, %s\n", functionName, N_FUN, functionName); 98 | fprintf(outputFile, ".stabn %d, 0, 0, %s\n", N_LBRAC, functionName); 99 | fprintf(outputFile, ".stabn %d, 0, 0, .Lret_%s\n", N_RBRAC, functionName); 100 | } 101 | 102 | /** 103 | * Is called after a function return command is found. Creates a label for the function info stab to use 104 | * @param outputFile the output file 105 | */ 106 | void stabs_writeFunctionEndLabel(FILE *outputFile, char* currentFunctionName) { 107 | fprintf(outputFile, "\t.Lret_%s:\n", currentFunctionName); 108 | } 109 | 110 | /** 111 | * Creates a label for the line number STABS to use 112 | * @param outputFile the output file 113 | * @param parsedCommand the command that requires a line number info 114 | */ 115 | void stabs_writeLineLabel(FILE *outputFile, struct parsedCommand parsedCommand) { 116 | fprintf(outputFile, "\t.Lcmd_%lu:\n", parsedCommand.lineNum); 117 | } 118 | 119 | /** 120 | * Creates a line number STABS of the provided command 121 | * @param outputFile the output file 122 | * @param parsedCommand the command that requires a line number info 123 | */ 124 | void stabs_writeLineInfo(FILE *outputFile, struct parsedCommand parsedCommand) { 125 | fprintf(outputFile, "\t.stabn %d, 0, %lu, .Lcmd_%lu\n", N_SLINE, parsedCommand.lineNum, parsedCommand.lineNum); 126 | } 127 | 128 | /** 129 | * Receives a command and writes its assembly translation into the output file 130 | * @param compileState the current compile state 131 | * @param currentFunctionName the name of the current function. Needed for writing some stabs debugging info 132 | * @param parsedCommand the command to be translated 133 | * @param fileNum the id of the current file 134 | * @param outputFile the file where the translation should be written to 135 | */ 136 | void translateToAssembly(struct compileState* compileState, char* currentFunctionName, struct parsedCommand parsedCommand, unsigned fileNum, bool lastCommand, FILE *outputFile) { 137 | if(commandList[parsedCommand.opcode].commandType != COMMAND_TYPE_FUNC_DEF && compileState->optimisationLevel == o69420) { 138 | printDebugMessage(compileState->logLevel, "\tCommand is not a function declaration, abort.", 0); 139 | return; 140 | } 141 | 142 | //If we are supposed to create STABS info, we now need to create labels 143 | if(compileState->useStabs) { 144 | //If this is a function declaration, update the current function name 145 | if(commandList[parsedCommand.opcode].commandType != COMMAND_TYPE_FUNC_DEF) { 146 | stabs_writeLineLabel(outputFile, parsedCommand); 147 | } 148 | } 149 | 150 | struct command command = commandList[parsedCommand.opcode]; 151 | char *translationPattern = command.translationPattern; 152 | 153 | if(commandList[parsedCommand.opcode].commandType != COMMAND_TYPE_FUNC_DEF) { 154 | fprintf(outputFile, "\t"); 155 | } 156 | for(size_t i = 0; i < strlen(translationPattern); i++) { 157 | 158 | //Check if this is a format specifier 159 | if(translationPattern[i] == '{' && translationPattern[i + 2] == '}') { 160 | char formatSpecifier = translationPattern[i + 1]; 161 | //If the format_specifier is F, we need to add the value of the current file's index to the string 162 | if(formatSpecifier == 'F') { 163 | fprintf(outputFile, "%u", fileNum); 164 | //Is it a parameter? 165 | } else if(formatSpecifier >= '0' && formatSpecifier < command.usedParameters + '0') { 166 | uint8_t index = formatSpecifier - 48; 167 | char *parameter = parsedCommand.parameters[index]; 168 | if(parsedCommand.isPointer == index + 1) { 169 | /* 170 | * If we are in bully mode, we first need to check if the operand size is unknown (e.g. a pointer 171 | * and a decimal number are used). This is because this check is skipped in parameters.c 172 | */ 173 | if(compileState->compileMode == bully && commandList[parsedCommand.opcode].usedParameters == 2 && !PARAM_ISREG(parsedCommand.paramTypes[index + 1 % 2])) { 174 | const char* operandSizes[] = {"BYTE PTR", "WORD PTR", "DWORD PTR", "QWORD PTR"}; 175 | fprintf(outputFile, "%s [%s]", operandSizes[computedIndex % 4], parameter); 176 | } else { 177 | fprintf(outputFile, "[%s]", parameter); 178 | } 179 | } else { 180 | /* 181 | * If the parameter is a decimal number, write it as a hex string. Fixes issue #73 182 | * The check is only needed here, as a decimal number cannot be a pointer 183 | */ 184 | if(parsedCommand.paramTypes[index] == PARAM_DECIMAL) { 185 | fprintf(outputFile, "0x%llX", strtoll(parameter, NULL, 10)); 186 | } else { 187 | fprintf(outputFile, "%s", parameter); 188 | } 189 | } 190 | } else { 191 | printInternalCompilerError("Invalid translation format specifier '%c' for opcode %u", true, 2, formatSpecifier, parsedCommand.opcode); 192 | exit(EXIT_FAILURE); 193 | } 194 | 195 | //move our pointer along by three characters instead of one, as we just parsed three characters 196 | i += 2; 197 | } else { 198 | fprintf(outputFile, "%c", translationPattern[i]); 199 | } 200 | } 201 | fprintf(outputFile, "\n"); 202 | 203 | //Now, we need to insert more commands based on the current optimisation level 204 | if (compileState->optimisationLevel == o_1) { 205 | //Insert a nop 206 | fprintf(outputFile, "\tnop\n"); 207 | } else if (compileState->optimisationLevel == o_2) { 208 | //Push and pop rax 209 | fprintf(outputFile, "\tpush rax\n\tpop rax\n"); 210 | } else if (compileState->optimisationLevel == o_3) { 211 | //Save and restore xmm0 on the stack using movups 212 | fprintf(outputFile, "\tmovups [rsp + 8], xmm0\n\tmovups xmm0, [rsp + 8]\n"); 213 | } else if(compileState->optimisationLevel == o69420) { 214 | //If we get here, then this was a function declaration. Insert a ret-statement and exit 215 | fprintf(outputFile, "\txor rax, rax\n\tret\n"); 216 | } 217 | 218 | if(compileState->useStabs && commandList[parsedCommand.opcode].commandType != COMMAND_TYPE_FUNC_DEF) { 219 | //If this was a return statement and this is the end of file or a function definition is followed by it, we reached the end of the function. Define the label for the N_RBRAC stab 220 | if(lastCommand) { 221 | stabs_writeFunctionEndLabel(outputFile, currentFunctionName); 222 | } 223 | //In any case, we now need to write the line info to the file 224 | stabs_writeLineInfo(outputFile, parsedCommand); 225 | } 226 | } 227 | 228 | void writeToFile(struct compileState* compileState, FILE *outputFile) { 229 | time_t t = time(NULL); 230 | struct tm tm = *localtime(&t); 231 | 232 | fprintf(outputFile, "#\n# Generated by the MemeAssembly compiler %s on %s#\n", versionString, asctime(&tm)); 233 | fprintf(outputFile, ".intel_syntax noprefix\n"); 234 | 235 | //Define all functions as global 236 | for(unsigned i = 0; i < compileState->fileCount; i++) { 237 | for(size_t j = 0; j < compileState->files[i].functionCount; j++) { 238 | //Only write if the function definition is to be translated 239 | if(compileState->files[i].functions[j].commands[0].translate) { 240 | //Write the function name with the prefix ".global" to the file 241 | fprintf(outputFile, ".global %s\n", compileState->files[i].functions[j].commands[0].parameters[0]); 242 | } 243 | } 244 | } 245 | 246 | #ifdef WINDOWS 247 | //To interact with the Windows API, we need to reference the needed functions 248 | fprintf(outputFile, "\n.extern GetStdHandle\n.extern WriteFile\n.extern ReadFile\n"); 249 | #endif 250 | 251 | fprintf(outputFile, "\n.data\n\t"); 252 | fprintf(outputFile, ".LCharacter: .ascii \"a\"\n\t.Ltmp64: .byte 0, 0, 0, 0, 0, 0, 0, 0\n"); 253 | 254 | //Struct for martyrdom command 255 | #ifdef LINUX 256 | fprintf(outputFile, "\t.LsigStruct:\n" 257 | "\t\t.Lsa_handler: .quad 0\n" 258 | "\t\t.quad 0x04000000\n" 259 | "\t\t.quad 0, 0\n\n"); 260 | #elif defined(MACOS) 261 | fprintf(outputFile, "\t.LsigStruct:\n" 262 | "\t\t.Lsa_handler: .quad 0\n" 263 | "\t\t.Lsa_handler_2: .quad 0\n" 264 | "\t\t.quad 0, 0\n\n"); 265 | #endif 266 | 267 | fprintf(outputFile, "\n\n.text\n\t"); 268 | fprintf(outputFile, "\n\n.Ltext0:\n"); 269 | 270 | #ifndef WINDOWS 271 | fprintf(outputFile, "killParent:\n" 272 | #ifdef LINUX 273 | " mov rax, 110\n" 274 | #else 275 | " mov rax, 0x2000027\n" 276 | #endif 277 | " syscall\n" 278 | "\n" 279 | " mov rdi, rax\n" 280 | " mov rsi, 9\n" 281 | #ifdef LINUX 282 | " mov rax, 62\n" 283 | #else 284 | " mov rax, 0x2000025\n" 285 | #endif 286 | " syscall\n" 287 | "\n" 288 | " mov rdi, 0\n" 289 | " mov rax, 60\n" 290 | " syscall\n" 291 | " ret\n\n"); 292 | 293 | #endif 294 | 295 | /* 296 | * If we're in bully mode and an executable is to be generated, we omitted the check 297 | * if there was a main-function 298 | * We do that check now. If no main function exists, the first function in the file becomes the main function 299 | */ 300 | if(compileState->compileMode == bully && compileState->outputMode == executable && !mainFunctionExists(compileState)) { 301 | fprintf(outputFile, "\n.global main\n\t"); 302 | fprintf(outputFile, "\nmain:\n\t"); 303 | fprintf(outputFile, "%s", martyrdomCode); 304 | } 305 | 306 | for(unsigned i = 0; i < compileState->fileCount; i++) { 307 | struct file currentFile = compileState->files[i]; 308 | //Write the file info if we are using stabs 309 | if(compileState->useStabs) { 310 | stabs_writeFileInfo(outputFile, currentFile.fileName); 311 | } 312 | 313 | size_t line = 0; 314 | for(size_t j = 0; j < currentFile.functionCount; j++) { 315 | struct function currentFunction = currentFile.functions[j]; 316 | char* functionName = currentFunction.commands[0].parameters[0]; 317 | 318 | for(size_t k = 0; k < currentFunction.numberOfCommands; k++) { 319 | #ifndef WINDOWS 320 | const char *const mainFuncName = 321 | #ifdef MACOS 322 | "_main"; 323 | #else 324 | "main"; 325 | #endif 326 | 327 | if (compileState->martyrdom && k == 1 && strcmp(functionName, mainFuncName) == 0) { 328 | fprintf(outputFile, "%s", martyrdomCode); 329 | } 330 | #endif 331 | 332 | struct parsedCommand currentCommand = currentFunction.commands[k]; 333 | 334 | //Print the confused stonks label now if it should be at this position 335 | if (line == currentFile.randomIndex) { 336 | fprintf(outputFile, "\t.LConfusedStonks_%u: \n", i); 337 | } 338 | 339 | //If it should be translated, translate it 340 | if (currentCommand.translate) { 341 | translateToAssembly(compileState, functionName, currentCommand, i, 342 | (k == currentFunction.numberOfCommands - 1), outputFile); 343 | } 344 | 345 | //Insert STABS function-info 346 | if (compileState->useStabs) { 347 | stabs_writeFunctionInfo(outputFile, functionName); 348 | } 349 | line++; 350 | } 351 | } 352 | } 353 | 354 | //If the optimisation level is 42069, then this function will not be used as all commands are optimised out 355 | if(compileState->optimisationLevel != o69420) { 356 | #ifdef WINDOWS 357 | //Using Windows API 358 | fprintf(outputFile, 359 | "\n\nwritechar:\n" 360 | "\tpush rcx\n" 361 | "\tpush rax\n" 362 | "\tpush rdx\n" 363 | "\tpush r8\n" 364 | "\tpush r9\n" 365 | //Get Handle of stdout 366 | "\tsub rsp, 32\n" 367 | "\tmov rcx, -11\n" //-11=stdout 368 | "\tcall GetStdHandle\n"//return value is in rax 369 | //Prepare the parameters for output 370 | "\tmov rcx, rax\n" //move Handle of stdout into rcx 371 | "\tlea rdx, [rip + .LCharacter]\n" 372 | "\tmov r8, 1\n" //Length of message = 1 character 373 | "\tlea r9, [rip + .Ltmp64]\n" //Number of bytes written, just discard that value 374 | "\tmov QWORD PTR [rsp + 32], 0\n" 375 | "\tcall WriteFile\n" 376 | "\tadd rsp, 32\n" 377 | 378 | //Restore all registers 379 | "\tpop r9\n" 380 | "\tpop r8\n" 381 | "\tpop rdx\n" 382 | "\tpop rax\n" 383 | "\tpop rcx\n" 384 | "\tret\n"); 385 | 386 | fprintf(outputFile, 387 | "\n\nreadchar:\n" 388 | "\tpush rcx\n" 389 | "\tpush rax\n" 390 | "\tpush rdx\n" 391 | "\tpush r8\n" 392 | "\tpush r9\n" 393 | //Get Handle of stdin 394 | "\tsub rsp, 32\n" 395 | "\tmov rcx, -10\n" //-10=stdin 396 | "\tcall GetStdHandle\n"//return value is in rax 397 | //Prepare the parameters for reading from input 398 | "\tmov rcx, rax\n" //move Handle of stdin into rcx 399 | "\tlea rdx, [rip + .LCharacter]\n" 400 | "\tmov r8, 1\n" //Bytes to read = 1 character 401 | "\tlea r9, [rip + .Ltmp64]\n" //Number of bytes read, just discard that value 402 | //Parameter 5 and then 4 Bytes of emptiness on the stack 403 | "\tmov QWORD PTR [rsp + 32], 0\n" 404 | "\tcall ReadFile\n" 405 | "\tadd rsp, 32\n" 406 | 407 | //Restore all registers 408 | "\tpop r9\n" 409 | "\tpop r8\n" 410 | "\tpop rdx\n" 411 | "\tpop rax\n" 412 | "\tpop rcx\n" 413 | "\tret\n"); 414 | #else 415 | //Using Linux syscalls 416 | fprintf(outputFile, "\n\nwritechar:\n\t" 417 | "push rcx\n\t" 418 | "push r11\n\t" 419 | "push rax\n\t" 420 | "push rdi\n\t" 421 | "push rsi\n\t" 422 | "push rdx\n\t" 423 | "mov rdx, 1\n\t" 424 | "lea rsi, [rip + .LCharacter]\n\t" 425 | "mov rdi, 1\n\t" 426 | #ifdef LINUX 427 | "mov rax, 1\n\t" 428 | #else 429 | "mov rax, 0x2000004\n\t" 430 | #endif 431 | "syscall\n\t" 432 | "pop rdx\n\t" 433 | "pop rsi\n\t" 434 | "pop rdi\n\t" 435 | "pop rax\n\t" 436 | "pop r11\n\t" 437 | "pop rcx\n\t\n\t" 438 | "ret\n"); 439 | 440 | fprintf(outputFile, "\n\nreadchar:\n\t" 441 | "push rcx\n\t" 442 | "push r11\n\t" 443 | "push rax\n\t" 444 | "push rdi\n\t" 445 | "push rsi\n\t" 446 | "push rdx\n\n\t" 447 | "mov rdx, 1\n\t" 448 | "lea rsi, [rip + .LCharacter]\n\t" 449 | "mov rdi, 0\n\t" 450 | #ifdef LINUX 451 | "mov rax, 0\n\t" 452 | #else 453 | "mov rax, 0x2000003\n\t" 454 | #endif 455 | "syscall\n\n\t" 456 | "pop rdx\n\t" 457 | "pop rsi\n\t" 458 | "pop rdi\n\t" 459 | "pop rax\n\t" 460 | "pop r11\n\t" 461 | "pop rcx\n\t" 462 | "ret\n"); 463 | #endif 464 | } 465 | 466 | //Add an "end marker" if we are using stabs 467 | if(compileState->useStabs) { 468 | fprintf(outputFile, "\n.LEOF:\n"); 469 | fprintf(outputFile, ".stabs \"\", %d, 0, 0, .LEOF\n", N_SO); 470 | } 471 | 472 | if(compileState->optimisationLevel == o_s) { 473 | fprintf(outputFile, ".align 536870912\n"); 474 | } 475 | } 476 | --------------------------------------------------------------------------------