├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json ├── configurationCache.log ├── dryrun.log ├── settings.json └── targets.log ├── LICENSE ├── Makefile ├── README.md ├── evi.bnf ├── finderr.sh ├── include ├── ast.hpp ├── codegen.hpp ├── common.hpp ├── debug.hpp ├── error.hpp ├── lint.hpp ├── macros.hpp ├── parser.hpp ├── pch.h ├── preprocessor.hpp ├── scanner.hpp ├── tools.hpp ├── typechecker.hpp ├── types.hpp ├── visualizer.hpp └── x86_64-linux-gnu │ ├── ld_args.h │ └── size.h ├── src ├── codegen.cpp ├── debug.cpp ├── error.cpp ├── lint.cpp ├── macros.cpp ├── main.cpp ├── parser.cpp ├── preprocessor.cpp ├── scanner.cpp ├── tools.cpp ├── typechecker.cpp ├── types.cpp └── visualizer.cpp ├── stdlib ├── Makefile ├── headers │ └── std │ │ ├── __std_header_defs.hevi │ │ ├── all.hevi │ │ ├── io.hevi │ │ ├── math.hevi │ │ ├── mem.hevi │ │ └── str.hevi └── src │ └── std │ ├── __std_header_defs.hevi.h │ ├── io.evi.c │ ├── math.evi.c │ ├── mem.evi.c │ └── str.evi.c ├── test ├── .vscode │ ├── settings.json │ └── tasks.json ├── arrays_and_pointers.evi ├── calclang │ └── main.evi ├── fib.evi ├── hello_world.evi ├── memory.evi └── test.evi └── tools ├── debian-package ├── control ├── control_replacings.py ├── copied-files.txt └── generate-deb.py ├── evi-bash-completion.sh ├── evi-man ├── sublime-package ├── Comments.tmPreferences ├── Evi.sublime-build ├── Evi.sublime-completions └── Evi.sublime-syntax └── vscode-extension ├── .vscode ├── launch.json └── tasks.json ├── .vscodeignore ├── README.md ├── assets └── readme │ ├── features.gif │ └── screenshot1.png ├── build-grammar.js ├── package-lock.json ├── package.json ├── src ├── eviMain.ts └── features │ ├── completionItemProvider.ts │ ├── definitionProvider.ts │ ├── diagnosticsUpdater.ts.unused │ ├── eviSymbols.ts │ ├── hoverProvider.ts │ ├── signatureHelpProvider.ts │ ├── taskProvider.ts │ ├── utils │ ├── async.ts │ ├── backwardIterator.ts │ ├── eviLintUtil.ts │ └── markedTextUtil.ts │ └── validationProvider.ts ├── syntaxes ├── evi.code-snippets ├── evi.generated.tmLanguage ├── evi.old.tmLanguage.json ├── evi.tmLanguage.yaml └── language-configuration.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | ./.vscode/ 3 | **/*.phc 4 | **/*.gch 5 | **/*.svg 6 | **/*.o 7 | **/*.evii 8 | **/__pycache__/ 9 | tools/vscode-extension/node_modules/ 10 | tools/vscode-extension/out 11 | 12 | tools/vscode-extension/evi.generated.tmLanguage 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wiki"] 2 | path = wiki 3 | url = https://github.com/SjVer/Evi-Lang.wiki.git 4 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${default}", 7 | "${workspaceFolder}/include", 8 | "", 9 | "${workspaceFolder}/include/x86_64-linux-gnu" 10 | ], 11 | "defines": [ 12 | "DEBUG", 13 | "LD_PATH=\"/usr/bin/ld\"", 14 | "STATICLIB_DIR=\"/usr/lib/\"", 15 | "STDLIB_DIR=\"/usr/share/evi/\"", 16 | "LIBC_VERSION=\"8\"" 17 | ], 18 | "compilerPath": "/usr/bin/gcc", 19 | "cStandard": "gnu17", 20 | "cppStandard": "gnu++14", 21 | "intelliSenseMode": "linux-gcc-x64" 22 | } 23 | ], 24 | "version": 4 25 | } -------------------------------------------------------------------------------- /.vscode/configurationCache.log: -------------------------------------------------------------------------------- 1 | {"buildTargets":["all","bin/evi","bin/obj/typechecker.o","clean","clean-stdlib","debug","git","makedirs","newfile","no-fold","pch","printdebug","remake","routine","stdlib","test","test-debug","valgrind"],"launchTargets":["/home/sjoerd/Coding/Languages/Evi-Lang/bin>evi()"],"customConfigurationProvider":{"workspaceBrowse":{"browsePath":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch","/home/sjoerd/Coding/Languages/Evi-Lang/include","/home/sjoerd/Coding/Languages/Evi-Lang/src","/home/sjoerd/Coding/Languages/Evi-Lang/stdlib/src"],"compilerArgs":["-o","bin/obj/stdlib/std-io.o","-c","stdlib/src/std-io.evi.c"],"compilerPath":"/usr/bin/clang","windowsSdkVersion":""},"fileIndex":[["/home/sjoerd/Coding/Languages/Evi-Lang/src/typechecker.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/typechecker.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/typechecker.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/typechecker.o -c src/typechecker.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/typechecker.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/scanner.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/scanner.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/scanner.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/scanner.o -c src/scanner.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/scanner.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/types.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/types.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/types.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/types.o -c src/types.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/types.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/error.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/error.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/error.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/error.o -c src/error.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/error.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/parser.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/parser.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/parser.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/parser.o -c src/parser.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/parser.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/tools.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/tools.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/tools.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/tools.o -c src/tools.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/tools.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/main.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/main.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/main.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/main.o -c src/main.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/main.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/debug.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/debug.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/debug.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/debug.o -c src/debug.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/debug.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/src/codegen.cpp",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/src/codegen.cpp","path":"/home/sjoerd/Coding/Languages/Evi-Lang/src/codegen.cpp","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang++\\\""],"includePath":["/home/sjoerd/Coding/Languages/Evi-Lang/include"],"forcedInclude":["/home/sjoerd/Coding/Languages/Evi-Lang/-pch"],"compilerPath":"/usr/bin/clang++","compilerArgs":[],"windowsSdkVersion":""},"compileCommand":{"command":"clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\\\"clang++\\\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/codegen.o -c src/codegen.cpp","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/src/codegen.cpp"}}],["/home/sjoerd/Coding/Languages/Evi-Lang/stdlib/src/std-io.evi.c",{"uri":{"$mid":1,"fsPath":"/home/sjoerd/Coding/Languages/Evi-Lang/stdlib/src/std-io.evi.c","path":"/home/sjoerd/Coding/Languages/Evi-Lang/stdlib/src/std-io.evi.c","scheme":"file"},"configuration":{"defines":["COMPILER=\\\"clang\\\"","LLVM_VERSION="],"includePath":[],"forcedInclude":[],"compilerPath":"/usr/bin/clang","compilerArgs":["-o","bin/obj/stdlib/std-io.o","-c","stdlib/src/std-io.evi.c"],"windowsSdkVersion":""},"compileCommand":{"command":"clang -D COMPILER=\\\"clang\\\" -D LLVM_VERSION= -o bin/obj/stdlib/std-io.o -c stdlib/src/std-io.evi.c","directory":"/home/sjoerd/Coding/Languages/Evi-Lang","file":"/home/sjoerd/Coding/Languages/Evi-Lang/stdlib/src/std-io.evi.c"}}]]}} -------------------------------------------------------------------------------- /.vscode/dryrun.log: -------------------------------------------------------------------------------- 1 | make --dry-run --always-make --keep-going --print-directory 2 | make: Entering directory '/home/sjoerd/Coding/Languages/Evi-Lang' 3 | mkdir -p bin 4 | mkdir -p bin/obj 5 | 6 | printf "[01/9] compiling typechecker.cpp into typechecker.o..." 7 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/typechecker.o -c src/typechecker.cpp 8 | printf "\b\b done!\n" 9 | printf "[02/9] compiling scanner.cpp into scanner.o..." 10 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/scanner.o -c src/scanner.cpp 11 | printf "\b\b done!\n" 12 | printf "[03/9] compiling types.cpp into types.o..." 13 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/types.o -c src/types.cpp 14 | printf "\b\b done!\n" 15 | printf "[04/9] compiling error.cpp into error.o..." 16 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/error.o -c src/error.cpp 17 | printf "\b\b done!\n" 18 | printf "[05/9] compiling parser.cpp into parser.o..." 19 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/parser.o -c src/parser.cpp 20 | printf "\b\b done!\n" 21 | printf "[06/9] compiling tools.cpp into tools.o..." 22 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/tools.o -c src/tools.cpp 23 | printf "\b\b done!\n" 24 | printf "[07/9] compiling main.cpp into main.o..." 25 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/main.o -c src/main.cpp 26 | printf "\b\b done!\n" 27 | printf "[08/9] compiling debug.cpp into debug.o..." 28 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/debug.o -c src/debug.cpp 29 | printf "\b\b done!\n" 30 | printf "[09/9] compiling codegen.cpp into codegen.o..." 31 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -include-pch include/pch.h.gch -I include -o bin/obj/codegen.o -c src/codegen.cpp 32 | printf "\b\b done!\n" 33 | printf "[final] compiling final product evi..." 34 | clang++ -Wall -Wno-varargs -Wno-write-strings -Wno-sign-compare -Wno-unused-function -DCOMPILER=\"clang++\" `llvm-config-12 --cxxflags` -o bin/evi bin/obj/typechecker.o bin/obj/scanner.o bin/obj/types.o bin/obj/error.o bin/obj/parser.o bin/obj/tools.o bin/obj/main.o bin/obj/debug.o bin/obj/codegen.o `llvm-config-12 --cxxflags --ldflags --system-libs --libs` 35 | printf "\b\b done!\n" 36 | make --no-print-directory -f stdlib/Makefile stdlib 37 | mkdir -p bin/obj/stdlib 38 | printf "[01/1] compiling std-io.evi.c into std-io.o..." 39 | clang -D COMPILER=\"clang\" -D LLVM_VERSION= -o bin/obj/stdlib/std-io.o -c stdlib/src/std-io.evi.c 40 | printf "\b\b done!\n" 41 | ar -rc -c bin/libevi.a bin/obj/stdlib/std-io.o 42 | echo "[stdlib] standard library compiled!" 43 | make: Leaving directory '/home/sjoerd/Coding/Languages/Evi-Lang' 44 | 45 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "makefile.extensionOutputFolder": "./.vscode", 3 | "files.associations": { 4 | "iostream": "cpp", 5 | "bitset": "cpp", 6 | "memory_resource": "cpp", 7 | "*.def": "cpp", 8 | "optional": "cpp", 9 | "istream": "cpp", 10 | "ostream": "cpp", 11 | "ratio": "cpp", 12 | "system_error": "cpp", 13 | "array": "cpp", 14 | "functional": "cpp", 15 | "tuple": "cpp", 16 | "type_traits": "cpp", 17 | "utility": "cpp", 18 | "*.tcc": "cpp", 19 | "random": "cpp", 20 | "fstream": "cpp", 21 | "limits": "cpp", 22 | "new": "cpp", 23 | "stdexcept": "cpp", 24 | "streambuf": "cpp", 25 | "typeinfo": "cpp", 26 | "Evi.sublime-syntax": "yaml", 27 | "sstream": "cpp", 28 | "cstdio": "cpp", 29 | "numeric": "cpp", 30 | "shared_mutex": "cpp", 31 | "memory": "cpp", 32 | "algorithm": "cpp", 33 | "filesystem": "cpp", 34 | "atomic": "cpp", 35 | "bit": "cpp", 36 | "cctype": "cpp", 37 | "chrono": "cpp", 38 | "clocale": "cpp", 39 | "cmath": "cpp", 40 | "codecvt": "cpp", 41 | "condition_variable": "cpp", 42 | "cstdarg": "cpp", 43 | "cstddef": "cpp", 44 | "cstdint": "cpp", 45 | "cstdlib": "cpp", 46 | "cstring": "cpp", 47 | "ctime": "cpp", 48 | "cwchar": "cpp", 49 | "cwctype": "cpp", 50 | "deque": "cpp", 51 | "list": "cpp", 52 | "map": "cpp", 53 | "set": "cpp", 54 | "unordered_map": "cpp", 55 | "vector": "cpp", 56 | "exception": "cpp", 57 | "iterator": "cpp", 58 | "regex": "cpp", 59 | "string": "cpp", 60 | "string_view": "cpp", 61 | "initializer_list": "cpp", 62 | "iomanip": "cpp", 63 | "iosfwd": "cpp", 64 | "mutex": "cpp", 65 | "thread": "cpp", 66 | "cinttypes": "cpp", 67 | "evi-man": "plain-text", 68 | "*.inc": "cpp", 69 | "unordered_set": "cpp" 70 | }, 71 | "makefile.launchConfigurations": [ 72 | { 73 | "cwd": "/home/sjoerd/Coding/Languages/Evi-Lang", 74 | "binaryPath": "bin/evi", 75 | "binaryArgs": [ 76 | "test/test.evi", 77 | "-o", 78 | "bin/test.ll" 79 | ] 80 | } 81 | ], 82 | "C_Cpp.errorSquiggles": "Enabled" 83 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sjoerd Vermeulen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | ####################### Makefile Template ############################## 3 | ######################################################################## 4 | 5 | # Compiler settings - Can be customized. 6 | CC = clang++ 7 | LLVMVERSION = 12 8 | CC_PATH = /usr/bin/clang 9 | LD_PATH = /usr/bin/ld 10 | STATICLIB_DIR = /usr/lib/ 11 | STDLIB_DIR = /usr/share/evi/stdlib 12 | # STATICLIB_DIR = $(PWD)/bin/ 13 | # STDLIB_DIR = $(PWD)/stdlib/headers/ 14 | TARGET = x86_64-linux-gnu 15 | 16 | MUTE = varargs write-strings sign-compare unused-function comment dangling-gsl unknown-warning-option c++17-extensions 17 | LLVMFLAGS = llvm-config-$(LLVMVERSION) --cxxflags 18 | DEFS = COMPILER=\"$(CC)\" LD_PATH=\"$(LD_PATH)\" STATICLIB_DIR=\"$(STATICLIB_DIR)\" STDLIB_DIR=\"$(STDLIB_DIR)\" LIBC_VERSION=\"$(shell gcc -dumpversion)\" TARGET=\"$(TARGET)\" 19 | CXXFLAGS = -Wall $(addprefix -Wno-,$(MUTE)) $(addprefix -D,$(DEFS)) `$(LLVMFLAGS)` 20 | LDFLAGS = `$(LLVMFLAGS) --ldflags --system-libs --libs` 21 | 22 | # Makefile settings - Can be customized. 23 | APPNAME = evi 24 | EXT = .cpp 25 | SRCDIR = src 26 | HEADERDIR = include 27 | BINDIR = bin 28 | OBJDIR = $(BINDIR)/obj 29 | 30 | STDLIB_SRC_DIR = stdlib 31 | export LLVMVERSION BINDIR STDLIB_SRC_DIR 32 | 33 | ############## Do not change anything from here downwards! ############# 34 | SRC = $(wildcard $(SRCDIR)/*$(EXT)) 35 | # HEADERS = $(wildcard $(HEADERDIR)/*.h) $(wildcard $(HEADERDIR)/*.hpp) 36 | OBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o) 37 | APP = $(BINDIR)/$(APPNAME) 38 | DEP = $(OBJ:$(OBJDIR)/%.o=%.d) 39 | 40 | PCH = $(HEADERDIR)/pch.h 41 | PCHFLAGS = $(CXXFLAGS) -x c++-header $(PCH) 42 | # INC_PCH_FLAG = -include $(PCH) 43 | INC_PCH_FLAG = -include-pch $(PCH).gch 44 | 45 | DEBUGDEFS = -DDEBUG -ggdb 46 | 47 | OBJCOUNT_NOPAD = $(shell v=`echo $(OBJ) | wc -w`; echo `seq 1 $$(expr $$v)`) 48 | OBJCOUNT = $(foreach v,$(OBJCOUNT_NOPAD),$(shell printf '%02d' $(v))) 49 | 50 | 51 | # UNIX-based OS variables & settings 52 | RM = rm 53 | MKDIR = mkdir 54 | DELOBJ = $(OBJ) 55 | SHELL := /bin/bash 56 | 57 | ######################################################################## 58 | ####################### Targets beginning here ######################### 59 | ######################################################################## 60 | 61 | .MAIN: $(APP) 62 | all: $(APP) stdlib man deb 63 | .DEFAULT_GOAL := $(APP) 64 | 65 | # Builds the app 66 | $(APP): $(OBJ) | makedirs 67 | @printf "[final] compiling final product $(notdir $@)..." 68 | @$(CC) $(CXXFLAGS) -I$(HEADERDIR)/$(TARGET) -o $@ $^ $(LDFLAGS) 69 | @printf "\b\b done!\n" 70 | 71 | # Building rule for .o files and its .c/.cpp in combination with all .h 72 | # $(OBJDIR)/%.o: $(SRCDIR)/%$(EXT) | makedirs 73 | $(OBJDIR)/%.o: $(SRCDIR)/%$(EXT) | makedirs 74 | @printf "[$(word 1,$(OBJCOUNT))/$(words $(OBJ))] compiling $(notdir $<) into $(notdir $@)..." 75 | @$(CC) $(CXXFLAGS) -I$(HEADERDIR)/$(TARGET) $(INC_PCH_FLAG) -I $(HEADERDIR) -o $@ -c $< 76 | @printf "\b\b done!\n" 77 | $(eval OBJCOUNT = $(filter-out $(word 1,$(OBJCOUNT)),$(OBJCOUNT))) 78 | 79 | # Builds phc's 80 | pch: $(PCH) 81 | @printf "[pch] compiling $(PCH)..." 82 | @$(CC) $(PCHFLAGS) 83 | @printf "\b\b done!\n" 84 | 85 | stdlib: makedirs 86 | @$(MAKE) --no-print-directory -f $(STDLIB_SRC_DIR)/Makefile stdlib 87 | 88 | cp-stdlib: 89 | sudo mkdir -p $(STDLIB_DIR) 90 | sudo cp -r $(STDLIB_SRC_DIR)/headers/* $(STDLIB_DIR) 91 | 92 | .PHONY: clean-stdlib 93 | clean-stdlib: 94 | @$(MAKE) --no-print-directory -f $(STDLIB_SRC_DIR)/Makefile clean 95 | 96 | ############################################################################ 97 | 98 | # Cleans complete project 99 | .PHONY: clean 100 | clean: 101 | @# $(RM) $(DELOBJ) $(DEP) $(APP) 102 | @# $(RM) -rf $(OBJDIR) 103 | @$(RM) -rf $(BINDIR) 104 | 105 | .PHONY: makedirs 106 | makedirs: 107 | @$(MKDIR) -p $(BINDIR) 108 | @$(MKDIR) -p $(OBJDIR) 109 | 110 | .PHONY: remake 111 | remake: clean $(APP) 112 | 113 | ############################################################################ 114 | 115 | .PHONY: test 116 | test: $(APP) 117 | @printf "============= Running \"$(APP)\" =============\n\n" 118 | @$(APP) test/test.evi -o bin/test $(args) && \ 119 | \ 120 | printf "============= Running \"bin/test\" ===========\n\n" && \ 121 | bin/test && \ 122 | \ 123 | printf "\n\n============ Exited with code $$? ============\n" && \ 124 | rm bin/test \ 125 | || echo "============ Test failed with code $$? ============" 126 | 127 | @rm test/test.evi.*.o 2>/dev/null && echo "Object file left over! (now cleaned)" || true 128 | 129 | .PHONY: test-debug 130 | test-debug: debug $(APP) 131 | @printf "============ Running \"valgrind $(APP) test/test.evi -o bin/test.ll\" ============\n\n" 132 | @valgrind $(APP) test/test.evi -o bin/test.ll $(args) 133 | 134 | .PHONY: routine 135 | routine: $(APP) run clean 136 | 137 | ############################################################################ 138 | 139 | .PHONY: printdebug 140 | printdebug: 141 | @echo "debug mode set!" 142 | 143 | debug: CXXFLAGS += $(DEBUGDEFS) 144 | debug: printdebug 145 | debug: $(APP) 146 | 147 | .PHONY: debug-no-fold 148 | debug-no-fold: CXXFLAGS += -D DEBUG_NO_FOLD 149 | debug-no-fold: debug 150 | 151 | .PHONY: valgrind 152 | valgrind: debug $(APP) 153 | @valgrind bin/evi test/test.evi -o bin/test $(args) 154 | 155 | ############################################################################ 156 | 157 | git: 158 | @cd wiki && $(MAKE) --no-print-directory git || true 159 | 160 | git add --all 161 | git commit -m $$(test "$(msg)" && echo '$(msg)' || echo upload) 162 | git push origin main 163 | 164 | newfile: 165 | @test $(name) || ( echo "basename not given! ('make newfile name=BASENAME')"; false ) 166 | touch $(SRCDIR)/$(name).cpp 167 | touch $(HEADERDIR)/$(name).hpp 168 | 169 | .PHONY: man 170 | man: 171 | @cp tools/evi-man tools/evi-man.tmp 172 | @DATE=$$(date +"%a %d, %Y") && sed -i -e "s/<<>>/$$DATE/g" tools/evi-man.tmp 173 | @gzip tools/evi-man.tmp 174 | @mv tools/evi-man.tmp.gz $(BINDIR)/evi-man.gz 175 | 176 | 177 | .PHONY: maybe-pch 178 | maybe-pch: 179 | ifeq ($(wildcard $(PCH).gch),) 180 | @$(MAKE) --no-print-directory pch 181 | endif 182 | 183 | deb: maybe-pch $(APP) stdlib man 184 | @test $(target) || ( echo "target not given! ('make newfile target=TARGET')"; false ) 185 | @python3 tools/debian-package/generate-deb.py $(target) $(BINDIR) 186 | 187 | ############################################################################ 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Evi 2 | 3 | A statically typed programming language using the llvm project. \ 4 | \ 5 | Personally I've lost interest in this project. It works as a minimum viable product and I'm genuinly proud of what it has become and what I've learned from making it, but it's time to move to the next project. \ 6 | \ 7 | Documentation is unfinished but available at this github page's wiki. 8 | 9 | 10 | ### Building 11 | In order to build the evi compiler and its standard library run the following commands in the root folder of this repository (after you've cloned it): 12 | 13 | ```sh 14 | make deb target=amd64 # generate debian package 15 | ``` 16 | 17 | and 18 | 19 | ```sh 20 | sudo apt install ./bin/evi*.deb # install the debian package 21 | ``` 22 | 23 | PS: llvm is required 24 | 25 | (This README is a w.i.p. obviously) 26 | -------------------------------------------------------------------------------- /evi.bnf: -------------------------------------------------------------------------------- 1 | program : (func_decl | var_decl)* 2 | 3 | 4 | declaration : var_decl 5 | | statement 6 | 7 | func_decl : "@" "!"? IDENT type "(" type* "..."? ")" (statement | ";") 8 | var_decl : "%" "!"? IDENT ("," IDENT)* type (expression ("," expression)*)? ";" 9 | 10 | 11 | statement : assignment 12 | | if_stmt 13 | | loop 14 | | return 15 | | block 16 | | expression ";" 17 | | ";" 18 | 19 | assignment : "=" IDENT ("[" expression "]")* expression ";" 20 | if_stmt : "??" "(" expression ")" statement ("::" statement)? 21 | loop : "!!" "(" declaration? expression ";" declaration? ")" statement 22 | return : "~" expression? ";" 23 | block : "{" declaration* "}" 24 | 25 | 26 | expression : ternary 27 | 28 | ternary : logical_or ("?" expression ":" ternary)? 29 | logical_or : logical_xor ("||" logical_xor)* 30 | logical_or : logical_and ("^^" logical_and)* 31 | logical_and : bitwise_or ("&&" bitwise_or)* 32 | bitwise_or : bitwise_xor ("|" bitwise_xor)* 33 | bitwise_xor : bitwise_and ("^" bitwise_and)* 34 | bitwise_and : equality ("&" equality)* 35 | equality : comparison (("!=" | "==") comparison)* 36 | comparison : bitwise_shift ((">" | ">=" | "<" | "<=") bitwise_shift)* 37 | bitwise_shift : term (("<<" | ">>") term)* 38 | term : factor (("-" | "+") factor)* 39 | factor : cast (("/" | "*") cast)* 40 | cast : unary ("->" type)* 41 | unary : ("*" | "&" | "!" | "-" | "++" | "--") unary | subscript 42 | subscript : primary ("[" expression "]")* 43 | primary : NUMBER | CHAR | STRING | "(" expression ")" 44 | | array | size_of | reference | call 45 | 46 | array : "{" (expression ("," expression)*)? "}" 47 | size_of : "?" ( "(" type ")" | type ) 48 | reference : "$" (IDENT | INTEGER) 49 | call : IDENT "(" (expression ("," expression)*)? ")" 50 | 51 | type : ("!")? BASIC_TYPE ("*")* -------------------------------------------------------------------------------- /finderr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # finds location of internal error using error ID 3 | 4 | # check for argument 5 | if [ -z "$1" ]; then 6 | echo "No error ID given!"; 7 | exit 1; 8 | fi 9 | 10 | # check if id is valid 11 | if ! [[ "$1" =~ ^([A-Z][A-Z])([0-9]+)([A-Z])$ ]]; then 12 | echo "Invalid error ID given!"; 13 | exit 1; 14 | fi 15 | 16 | id="$1"; 17 | echo "Error ID: $id"; 18 | 19 | name_start="${BASH_REMATCH[1]}"; 20 | echo " - File ID: $name_start"; 21 | 22 | line="${BASH_REMATCH[2]}"; 23 | echo " - Line: $line"; 24 | 25 | ext_start="${BASH_REMATCH[3]}"; 26 | echo " - File Type ID: $ext_start"; 27 | 28 | echo; 29 | 30 | # find potentional files 31 | IFS=' 32 | ' 33 | files=($(find ./src ./include -iname ${name_start,,}*.${ext_start,,}*)) 34 | IFS=' ' 35 | 36 | # if file doesnt have enough lines leave it out 37 | to_discard=() 38 | for (( i = 0; i < ${#files[@]}; i++ )); do 39 | f=${files[$i]}; 40 | l=$(wc -l < "$f") 41 | if (( $l == 0 )) || (( $l < $line )); then 42 | echo "File $f discarded (too short)"; 43 | to_discard+=( $i ); 44 | fi 45 | done 46 | for i in "${to_discard[@]}"; do unset 'files[$i]'; done 47 | 48 | if ! [ -z "${to_discard[@]}" ]; then echo; fi 49 | 50 | # check if we found files 51 | if [ -z "$files" ]; then 52 | echo "Could not find file!"; 53 | exit 1; 54 | fi 55 | 56 | # if its just one file, choose that 57 | if [ ${#files[@]} -eq "1" ]; then 58 | file=${files[0]}; 59 | else 60 | echo "Files found:" 61 | i=1 62 | for f in "${files[@]}"; do 63 | found=$(echo "$(sed -n $((line ))p "$f")" | grep "INTERNAL_ERROR"); 64 | foundmsg=$([ -z "$found" ] && echo "" || echo "(found \"INTERNAL_ERROR\")"); 65 | printf " %d: %s %s\n" $i $f "$foundmsg"; 66 | i=$((i + 1)); 67 | done 68 | 69 | printf "Enter correct file number: " 70 | read index 71 | while ! ((index >= 1 && index <= ${#files[@]})) 2> /dev/null; do 72 | echo "Integers from 1 to ${#files[@]} only!" 73 | printf "Enter correct file number: " 74 | read index 75 | done 76 | 77 | file=${files[(($index - 1))]} 78 | echo; 79 | fi 80 | 81 | 82 | echo "File: $file"; 83 | 84 | max=$((line + 2)); 85 | l=${#max}; 86 | 87 | echo "Source:" 88 | printf " %${l}d│ %s\n" $((line - 2)) "$(sed -n $((line - 2))p "$file")"; 89 | printf " %${l}d│ %s\n" $((line - 1)) "$(sed -n $((line - 1))p "$file")"; 90 | printf " -> %${l}d│ %s\n" $((line )) "$(sed -n $((line ))p "$file")"; 91 | printf " %${l}d│ %s\n" $((line + 1)) "$(sed -n $((line + 1))p "$file")"; 92 | printf " %${l}d│ %s\n" $((line + 2)) "$(sed -n $((line + 2))p "$file")"; -------------------------------------------------------------------------------- /include/codegen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_CODEGEN_H 2 | #define EVI_CODEGEN_H 3 | 4 | #include "common.hpp" 5 | #include "error.hpp" 6 | #include "debug.hpp" 7 | #include "ast.hpp" 8 | #include "pch.h" 9 | 10 | #include 11 | 12 | typedef enum 13 | { 14 | OPTIMIZE_O0 = '0', 15 | OPTIMIZE_O1 = '1', 16 | OPTIMIZE_O2 = '2', 17 | OPTIMIZE_O3 = '3', 18 | OPTIMIZE_On = 'n', 19 | OPTIMIZE_Os = 's', 20 | OPTIMIZE_Oz = 'z', 21 | } OptimizationType; 22 | 23 | class CodeGenerator: public Visitor 24 | { 25 | public: 26 | CodeGenerator(); 27 | Status generate(ccp infile, ccp outfile, ccp source, 28 | AST* astree, OptimizationType opt, bool debug_info); 29 | 30 | Status emit_llvm(ccp filename); 31 | Status emit_object(ccp filename); 32 | Status emit_binary(ccp filename, ccp* linked, int linkedc); 33 | 34 | #pragma region visitors 35 | #define VISIT(_node) void visit(_node* node) 36 | VISIT(FuncDeclNode); 37 | VISIT(VarDeclNode); 38 | VISIT(AssignNode); 39 | VISIT(IfNode); 40 | VISIT(LoopNode); 41 | VISIT(ReturnNode); 42 | VISIT(BlockNode); 43 | VISIT(LogicalNode); 44 | VISIT(BinaryNode); 45 | VISIT(CastNode); 46 | VISIT(UnaryNode); 47 | VISIT(GroupingNode); 48 | VISIT(SubscriptNode); 49 | VISIT(LiteralNode); 50 | VISIT(ArrayNode); 51 | VISIT(SizeOfNode); 52 | VISIT(ReferenceNode); 53 | VISIT(CallNode); 54 | #undef VISIT 55 | #pragma endregion 56 | 57 | private: 58 | void prepare(); 59 | void optimize(); 60 | void finish(); 61 | 62 | char* _infile; 63 | char* _outfile; 64 | ErrorDispatcher _error_dispatcher; 65 | llvm::raw_os_ostream* _errstream; 66 | 67 | #ifdef DEBUG_NO_FOLD 68 | unique_ptr> _builder; 69 | #else 70 | unique_ptr> _builder; 71 | #endif 72 | llvm::TargetMachine* _target_machine; 73 | string _target_triple; 74 | unique_ptr _top_module; 75 | 76 | stack* _value_stack; 77 | map _functions; 78 | map> _named_values; 79 | uint _string_literal_count; 80 | 81 | DebugInfoBuilder* _debug_info_builder; 82 | bool _build_debug_info; 83 | OptimizationType _opt_level; 84 | 85 | void error_at(Token *token, string message); 86 | void warning_at(Token *token, string message); 87 | 88 | void push(llvm::Value* value); 89 | llvm::Value* pop(); 90 | 91 | llvm::AllocaInst* create_entry_block_alloca(llvm::Type* ty, string name); 92 | llvm::Value* to_bool(llvm::Value* value); 93 | llvm::Value* create_cast(llvm::Value* srcval, bool srcsigned, 94 | llvm::Type* desttype, bool destsigned); 95 | 96 | ParsedType* from_token_type(TokenType type); 97 | }; 98 | 99 | #endif -------------------------------------------------------------------------------- /include/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_COMMON_H 2 | #define EVI_COMMON_H 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "tools.hpp" 11 | 12 | using namespace std; 13 | 14 | // c++ compiler name 15 | #ifndef COMPILER 16 | #define COMPILER "unknown" 17 | #endif 18 | 19 | // OS_NAME specific 20 | #pragma region 21 | #ifdef _WIN32 22 | #define OS_NAME "Windows 32-bit" 23 | #define PATH_SEPARATOR '\\' 24 | #elif _WIN64 25 | #define OS_NAME "Windows 64-bit" 26 | #define PATH_SEPARATOR '\\' 27 | #elif __APPLE__ || __MACH__ 28 | #define OS_NAME "Mac OS_NAMEX" 29 | #define PATH_SEPARATOR '/' 30 | #elif __linux__ 31 | #define OS_NAME "Linux" 32 | #define PATH_SEPARATOR '/' 33 | #elif __unix || __unix__ 34 | #define OS_NAME "Unix" 35 | #define PATH_SEPARATOR '/' 36 | #else 37 | #define OS_NAME "Unknown OS" 38 | #define PATH_SEPARATOR '/' 39 | #endif 40 | #pragma endregion 41 | 42 | #define COLOR_RED "\x1b[1;31m" 43 | #define COLOR_GREEN "\x1b[0;32m" 44 | #define COLOR_PURPLE "\x1b[1;35m" 45 | #define COLOR_NONE "\x1b[0m" 46 | #define COLOR_BOLD "\x1b[1m" 47 | 48 | // app info 49 | #pragma region 50 | #define APP_NAME "evi" 51 | #define APP_NAME_INTERNAL "evi (official)" 52 | #define APP_VERSION "0.0.1" 53 | #define APP_DOC "%s -- The Evi compiler.\nWritten by Sjoerd Vermeulen (%s)\v\ 54 | More information at %s.\nBuild: %s %s on %s (%s)." 55 | // format: APP_NAME, EMAIL, LINK, __DATE__, __TIME__, OS_NAME, COMPILER 56 | #define EMAIL "sjoerd@marsenaar.com" 57 | #define LINK "https://github.com/SjVer/Evi-Lang" 58 | 59 | // version info 60 | #define EVI_VERSION_0 0 61 | #define EVI_VERSION_1 0 62 | #define EVI_VERSION_2 1 63 | 64 | // build target 65 | #ifndef TARGET 66 | #pragma warning "TARGET not set! Defaulting to \"unkown\"." 67 | #define TARGET "unkown" 68 | #endif 69 | 70 | #pragma endregion 71 | 72 | // macros 73 | #pragma region 74 | #define STRINGIFY(value) #value 75 | #define LINE_MARKER_REGEX " ([0-9]+) \"(.+)\"" 76 | // #define FLAG_MARKER_REGEX " f ([0-9]+)" 77 | 78 | #ifdef DEBUG 79 | #define __DEBUG_MARKER(file, line) "[debug:" file ":" STRINGIFY(line) "]" 80 | #define DEBUG_MARKER __DEBUG_MARKER(__FILE__, __LINE__) 81 | #define DEBUG_PRINT_LINE() cout << tools::fstr(\ 82 | DEBUG_MARKER " line %d passed!",__LINE__) << endl 83 | #define DEBUG_PRINT_VAR(value, formatspec) cout << tools::fstr(\ 84 | DEBUG_MARKER " var %s = " #formatspec, #value, value) << endl 85 | #define DEBUG_PRINT_MSG(msg) cout << DEBUG_MARKER " " msg << endl; 86 | #define DEBUG_PRINT_F_MSG(format, ...) cout << tools::fstr( \ 87 | DEBUG_MARKER " " format, __VA_ARGS__) << endl 88 | #else 89 | #define DEBUG_MARKER {} 90 | #define DEBUG_PRINT_LINE() {} 91 | #define DEBUG_PRINT_VAR(value, formatspec) {} 92 | #define DEBUG_PRINT_MSG(msg) {} 93 | #define DEBUG_PRINT_F_MSG(format, ...) {} 94 | #endif 95 | 96 | #define ABORT(status) { cerr << tools::fstr("[evi] Aborted with code %d.\n", status); exit(status); } 97 | #define THROW_INTERNAL_ERROR(where) { \ 98 | ccp bn = basename(__FILE__); \ 99 | const char ext = strrchr(bn, '.')[1]; \ 100 | cerr << tools::fstr("[evi:!!!] Internal error %c%c%d%c occurred " where ".", \ 101 | toupper(bn[0]), toupper(bn[1]), __LINE__, toupper(ext)) << endl; \ 102 | cerr << "[evi:!!!] If this error occurs repeatedly please report it on github" << endl; \ 103 | cerr << "[evi:!!!] at https://github.com/SjVer/Evi-Lang/issues/new."; \ 104 | raise(SIGINT); \ 105 | } 106 | #define ASSERT_OR_THROW_INTERNAL_ERROR(condition, where) { if(!(condition)) THROW_INTERNAL_ERROR(where) } 107 | 108 | #pragma endregion 109 | 110 | // llvm stuff 111 | #define LLVM_MODULE_TOP_NAME "top" 112 | #define TEMP_OBJ_FILE_TEMPLATE "%s.%d.o" // format: sourcefile name and timestamp 113 | 114 | // internal shit 115 | #pragma region 116 | // path of c compiler used for linking 117 | #ifndef LD_PATH 118 | #error "LD_PATH must be defined! (e.g. \"/usr/bin/ld\")" 119 | #endif 120 | // directory of evi static library 121 | #ifndef STATICLIB_DIR 122 | #error "STATICLIB_DIR must be defined! (e.g. \"/usr/lib/\")" 123 | #endif 124 | // ld args 125 | #include "ld_args.h" 126 | // stdlib headers directory 127 | #ifndef STDLIB_DIR 128 | #error "STDLIB_DIR must be defined! (e.g. \"/usr/share/evi/\")" 129 | #endif 130 | 131 | #pragma endregion 132 | 133 | #define POINTER_ALIGNMENT 16 134 | #define MAX_APPLY_DEPTH 255 135 | 136 | #define MAX_INCLUDE_PATHS 0xff 137 | 138 | // status enum 139 | typedef enum 140 | { 141 | STATUS_SUCCESS = 0, 142 | STATUS_CLI_ERROR = 64, 143 | STATUS_PREPROCESS_ERROR = 65, 144 | STATUS_PARSE_ERROR = 66, 145 | STATUS_TYPE_ERROR = 67, 146 | STATUS_CODEGEN_ERROR = 68, 147 | STATUS_OUTPUT_ERROR = 69, 148 | 149 | STATUS_INTERNAL_ERROR = -1 150 | } Status; 151 | 152 | typedef const char* ccp; 153 | 154 | #endif -------------------------------------------------------------------------------- /include/debug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_DEBUG_H 2 | #define EVI_DEBUG_H 3 | 4 | #include "common.hpp" 5 | #include "types.hpp" 6 | #include "ast.hpp" 7 | #include "pch.h" 8 | 9 | class DebugInfoBuilder 10 | { 11 | unique_ptr _dbuilder; 12 | llvm::DICompileUnit* _cunit; 13 | vector _scopes; 14 | 15 | #ifdef DEBUG_NO_FOLD 16 | llvm::IRBuilder* _ir_builder; 17 | #define IRBUILDER llvm::IRBuilder* 18 | #else 19 | llvm::IRBuilder<>* _ibuilder; 20 | #define IRBUILDER llvm::IRBuilder<>* 21 | #endif 22 | 23 | string _directory; 24 | 25 | public: 26 | 27 | DebugInfoBuilder(llvm::Module*, IRBUILDER, string, bool); 28 | void finish(); 29 | 30 | llvm::DIFile* create_file_unit(string filename); 31 | llvm::DISubprogram* create_subprogram(FuncDeclNode* node, llvm::DIFile* file_unit); 32 | llvm::DILocalVariable* create_variable(string ident, int line_no, ParsedType* type, bool global); 33 | llvm::DILocalVariable* create_parameter(int index, int line_no, ParsedType* type); 34 | 35 | void emit_location(uint line, uint col); 36 | void insert_declare(llvm::Value* storage, llvm::DILocalVariable* varinfo); 37 | void push_subprogram(llvm::DISubprogram* sub_program); 38 | void pop_subprogram(); 39 | 40 | llvm::DIType* get_type(ParsedType* type); 41 | llvm::DISubroutineType* get_function_type(FuncDeclNode* node, llvm::DIFile* file_unit); 42 | }; 43 | 44 | #endif -------------------------------------------------------------------------------- /include/error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_ERROR_H 2 | #define EVI_ERROR_H 3 | 4 | #include "scanner.hpp" 5 | 6 | class ErrorDispatcher 7 | { 8 | private: 9 | 10 | void __dispatch(ccp color, ccp prompt, ccp message); 11 | void __dispatch_at_token(ccp color, Token *token, ccp prompt, ccp message); 12 | void __dispatch_at_line(ccp color, uint line, ccp filename, ccp prompt, ccp message); 13 | 14 | public: 15 | 16 | ErrorDispatcher() {} 17 | 18 | void print_token_marked(Token *token, ccp color); 19 | void print_line_marked(uint line_no, string line, ccp color); 20 | 21 | void error(ccp prompt, ccp message); 22 | void error_at_token(Token *token, ccp prompt, ccp message); 23 | void error_at_line(uint line, ccp filename, ccp prompt, ccp message); 24 | 25 | void warning(ccp prompt, ccp message); 26 | void warning_at_token(Token *token, ccp prompt, ccp message); 27 | void warning_at_line(uint line, ccp filename, ccp prompt, ccp message); 28 | 29 | void note(ccp message); 30 | void note_at_token(Token *token, ccp message); 31 | void note_at_line(uint line, ccp filename, ccp message); 32 | }; 33 | 34 | #endif -------------------------------------------------------------------------------- /include/lint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_LINT_H 2 | #define EVI_LINT_H 3 | 4 | #include 5 | #include "scanner.hpp" 6 | 7 | typedef enum 8 | { 9 | LINT_GET_DECLARATION, 10 | LINT_GET_DIAGNOSTICS, 11 | LINT_GET_FUNCTIONS, 12 | LINT_GET_VARIABLES, 13 | 14 | LINT_NONE 15 | } LintType; 16 | 17 | static ccp get_lint_type_string(LintType type) 18 | { 19 | #define CASE(type, str) case type: return str 20 | switch(type) 21 | { 22 | CASE(LINT_GET_DECLARATION, "get-declaration"); 23 | CASE(LINT_GET_DIAGNOSTICS, "get-diagnostics"); 24 | CASE(LINT_GET_FUNCTIONS, "get-functions"); 25 | CASE(LINT_GET_VARIABLES, "get-variables"); 26 | 27 | default: return nullptr; 28 | } 29 | #undef CASE 30 | } 31 | 32 | static LintType get_lint_type(ccp str) 33 | { 34 | for(int i = 0; i < LINT_NONE; i++) 35 | if(!strcmp(get_lint_type_string((LintType)i), str)) 36 | return (LintType)i; 37 | return LINT_NONE; 38 | } 39 | 40 | #define LINT_POS_REGEX "^([0-9]+):([0-9]+)$" 41 | 42 | typedef struct 43 | { 44 | LintType type; 45 | int tab_width; 46 | uint pos[2]; 47 | } lint_args_t; 48 | 49 | extern lint_args_t lint_args; 50 | extern std::string lint_output; 51 | 52 | #define WRONG_END (lint_output.length() >= 2 && lint_output.substr(lint_output.length() - 2) == ", ") 53 | 54 | #define LINT_OUTPUT_START_PLAIN_OBJECT() { lint_output += "{ "; } 55 | #define LINT_OUTPUT_END_PLAIN_OBJECT() { if(WRONG_END) lint_output.erase(lint_output.end() - 2); lint_output += "}"; } 56 | #define LINT_OUTPUT_OBJECT_START(key) { lint_output += '"' + string(key) + "\": { "; } 57 | #define LINT_OUTPUT_OBJECT_END() { if(WRONG_END) lint_output.erase(lint_output.end() - 2); lint_output += "}, "; } 58 | #define LINT_OUTPUT_PAIR(key, value) { lint_output += '"' + string(key) + "\": \"" + value + "\", "; } 59 | #define LINT_OUTPUT_PAIR_F(key, value, fspec) { lint_output += '"' + string(key) + tools::fstr("\": " #fspec ", ", value); } 60 | 61 | #define LINT_OUTPUT_START_PLAIN_ARRAY() { lint_output += "[ "; } 62 | #define LINT_OUTPUT_END_PLAIN_ARRAY() { if(WRONG_END) lint_output.erase(lint_output.end() - 2); lint_output += "]"; } 63 | #define LINT_OUTPUT_ARRAY_START(key) { lint_output += '"' + string(key) + "\": [ "; } 64 | #define LINT_OUTPUT_ARRAY_END() { if(WRONG_END) lint_output.erase(lint_output.end() - 2); lint_output += "], "; } 65 | #define LINT_OUTPUT_ARRAY_ITEM(value) { lint_output += '"' + value + "\", "; } 66 | 67 | void lint_output_diagnostic_object(Token* token, string message, ccp type); 68 | void lint_output_diagnostic_object_end(); 69 | 70 | #endif -------------------------------------------------------------------------------- /include/macros.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_MACROS_H 2 | #define EVI_MACROS_H 3 | 4 | #include "preprocessor.hpp" 5 | 6 | class State 7 | { 8 | friend void initialize_state_singleton(Preprocessor* p); 9 | 10 | State() {} 11 | 12 | inline static Preprocessor* _p; 13 | inline static bool _initialized; 14 | 15 | public: 16 | 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wreturn-type" 19 | 20 | #define FN(signature, code) static signature { \ 21 | if(!_initialized) THROW_INTERNAL_ERROR("during dynamic macro expansion") \ 22 | else code } 23 | 24 | FN(uint get_current_line_no(), { return _p->_current_line_no; }) 25 | FN(string get_current_file(), { return _p->_current_file; }) 26 | FN(uint get_apply_depth(), { return _p->_apply_depth; }) 27 | 28 | #pragma clang diagnostic pop 29 | }; 30 | 31 | #endif -------------------------------------------------------------------------------- /include/parser.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_PARSER_H 2 | #define EVI_PARSER_H 3 | 4 | #include "scanner.hpp" 5 | #include "ast.hpp" 6 | #include "error.hpp" 7 | #include "common.hpp" 8 | #include "lint.hpp" 9 | 10 | #include "pch.h" 11 | 12 | using namespace std; 13 | 14 | // ==== ============= ==== 15 | 16 | class Parser 17 | { 18 | public: 19 | Parser(): _scope_stack() {} 20 | Status parse(string infile, ccp source, AST* astree); 21 | 22 | private: 23 | 24 | // types 25 | 26 | typedef struct 27 | { 28 | ParsedType* ret_type; 29 | vector params; 30 | bool variadic; 31 | bool defined; 32 | bool invalid = false; 33 | Token token; 34 | } FuncProperties; 35 | 36 | typedef struct 37 | { 38 | ParsedType* type; 39 | Token token; 40 | } VarProperties; 41 | 42 | typedef struct 43 | { 44 | int depth; 45 | map variables; 46 | FuncProperties func_props; 47 | map functions; 48 | 49 | enum ScopeType 50 | { 51 | SCOPE_NORMAL, 52 | SCOPE_FUNCTION, 53 | SCOPE_LOOP 54 | } scope_type; 55 | } Scope; 56 | 57 | // methods 58 | 59 | void error_at(Token *token, string message); 60 | void error(string message); 61 | void error_at_current(string message); 62 | void note_declaration(string type, string name, Token* token); 63 | 64 | void advance(bool can_trigger_lint = true); 65 | bool check(TokenType type); 66 | bool consume(TokenType type, string message); 67 | ParsedType* consume_type(string msg = "Expected type."); 68 | bool match(TokenType type); 69 | bool is_at_end(); 70 | 71 | #define CONSUME_OR_RET_NULL(type, msg) if(!consume(type, msg)) return nullptr; 72 | 73 | void generate_lint(); 74 | 75 | VarProperties get_variable_props(string name); 76 | FuncProperties get_function_props(string name); 77 | bool check_variable(string name); 78 | bool check_function(string name); 79 | void add_variable(Token* identtoken, ParsedType* type); 80 | void add_function(Token* identtoken, FuncProperties properties); 81 | void scope_up(); 82 | void scope_down(); 83 | void synchronize(bool toplevel); 84 | 85 | StmtNode* statement(); 86 | StmtNode* declaration(); 87 | StmtNode* function_declaration(); 88 | StmtNode* variable_declaration(); 89 | StmtNode* assign_statement(); 90 | StmtNode* if_statement(); 91 | StmtNode* loop_statement(); 92 | StmtNode* return_statement(); 93 | StmtNode* block_statement(); 94 | StmtNode* expression_statement(); 95 | ExprNode* expression(bool = false); 96 | ExprNode* ternary(bool); 97 | ExprNode* logical_or(bool); 98 | ExprNode* logical_xor(bool); 99 | ExprNode* logical_and(bool); 100 | ExprNode* bitwise_or(bool); 101 | ExprNode* bitwise_xor(bool); 102 | ExprNode* bitwise_and(bool); 103 | ExprNode* equality(bool); 104 | ExprNode* comparison(bool); 105 | ExprNode* bitwise_shift(bool); 106 | ExprNode* term(bool); 107 | ExprNode* factor(bool); 108 | ExprNode* cast(bool); 109 | ExprNode* unary(bool); 110 | ExprNode* subscript(bool); 111 | ExprNode* primary(bool); 112 | 113 | LiteralNode* literal(); 114 | ArrayNode* array(bool); 115 | SizeOfNode* size_of(); 116 | ReferenceNode* reference(); 117 | CallNode* call(); 118 | 119 | // members 120 | 121 | Scanner _scanner; 122 | Token _current; 123 | Token _previous; 124 | 125 | AST* _astree; 126 | vector _scope_stack; 127 | Scope _current_scope; 128 | 129 | bool _had_error; 130 | bool _panic_mode; 131 | ErrorDispatcher _error_dispatcher; 132 | 133 | #define HOLD_PANIC() bool _old_panic_mode_from_macro_hold_panic = _panic_mode 134 | #define PANIC_HELD (_old_panic_mode_from_macro_hold_panic) 135 | 136 | string _main_file; 137 | // Token _current_call_token; 138 | 139 | #define PREV_TOKEN_STR std::string(_previous.start, _previous.length) 140 | }; 141 | 142 | #endif -------------------------------------------------------------------------------- /include/pch.h: -------------------------------------------------------------------------------- 1 | #ifndef EVI_PCH_H 2 | #define EVI_PCH_H 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include "llvm/Passes/PassBuilder.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #endif -------------------------------------------------------------------------------- /include/preprocessor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_PREPROCESSOR_H 2 | #define EVI_PREPROCESSOR_H 3 | 4 | #include "common.hpp" 5 | #include "error.hpp" 6 | #include "lint.hpp" 7 | #include "scanner.hpp" 8 | 9 | extern int include_paths_count; 10 | extern char* include_paths[MAX_INCLUDE_PATHS]; 11 | 12 | // ==== ============= ==== 13 | 14 | class Preprocessor 15 | { 16 | public: 17 | Preprocessor(): 18 | _current_file(), 19 | _had_error(false), 20 | _error_dispatcher() {} 21 | Status preprocess(string infile, ccp* source); 22 | 23 | private: 24 | 25 | friend class State; 26 | 27 | // types 28 | #pragma region types 29 | 30 | typedef enum 31 | { 32 | DIR_APPLY, 33 | DIR_INFO, 34 | 35 | DIR_FILE, 36 | DIR_LINE, 37 | 38 | DIR_MACRO, 39 | DIR_UNDEF, 40 | 41 | DIR_FLAG, 42 | DIR_UNSET, 43 | 44 | DIR_IFSET, 45 | DIR_IFNSET, 46 | DIR_IFDEF, 47 | DIR_IFNDEF, 48 | DIR_ELSE, 49 | DIR_ENDIF, 50 | 51 | DIR_INVALID 52 | } DirectiveType; 53 | typedef void (Preprocessor::*DirectiveHandler)(string); 54 | 55 | typedef string (*BuiltinMacroGetter)(); 56 | typedef struct 57 | { 58 | bool has_getter = false; 59 | string format; 60 | BuiltinMacroGetter getter; 61 | } MacroProperties; 62 | 63 | typedef enum 64 | { 65 | PRAGMA_SUCCESS, 66 | PRAGMA_ERROR_HANDLED, 67 | PRAGMA_NONEXISTENT, 68 | PRAGMA_INVALID_ARGS, 69 | PRAGMA_NO_NEWLINE, 70 | } PragmaStatus; 71 | 72 | #pragma endregion 73 | 74 | // macros 75 | #pragma region macros 76 | #define ERROR(lineno, line, msg) { error_at_line(lineno, msg, line); } 77 | #define ERROR_F(lineno, line, format, ...) { error_at_line(lineno, tools::fstr(format, __VA_ARGS__).c_str(), line); } 78 | #define WARNING(lineno, line, msg) { warning_at_line(lineno, msg, line); } 79 | #define WARNING_F(lineno, line, format, ...) { warning_at_line(lineno, tools::fstr(format, __VA_ARGS__).c_str(), line); } 80 | #define MACRO_INVOKE_REGEX "([a-zA-Z_][a-zA-Z0-9_]*)#" 81 | #pragma endregion 82 | 83 | // methods 84 | #pragma region methods 85 | void initialize_builtin_macros(); 86 | 87 | void process_lines(vector lines); 88 | string handle_plain_line(string line); 89 | vector remove_comments(vector lines); 90 | 91 | void error_at_line(uint line, ccp message, string whole_line = ""); 92 | void error_at_token(Token* token, ccp message); 93 | void warning_at_line(uint line, ccp message, string whole_line = ""); 94 | void warning_at_token(Token* token, ccp message); 95 | Token generate_token(string line, string token); 96 | 97 | string strip_start(string str); 98 | bool consume_identifier(string* str, string* dest, uint line, 99 | ccp errformat = "Expected identifier, not '%s'."); 100 | bool consume_string(string* str, string* dest, uint line); 101 | bool consume_integer(string* str, uint* dest, uint line); 102 | 103 | DirectiveType get_directive_type(string str); 104 | DirectiveHandler get_directive_handler(DirectiveType type); 105 | void handle_directive(string line, uint line_no); 106 | 107 | string find_header(string name); 108 | PragmaStatus handle_pragma(string pragma, string args, uint line_no); 109 | #pragma endregion 110 | 111 | // directive handlers 112 | #pragma region handlers 113 | #define HANDLER(name) void handle_directive_##name(string line) 114 | HANDLER(apply); 115 | HANDLER(info); 116 | 117 | HANDLER(file); 118 | HANDLER(line); 119 | 120 | HANDLER(macro); 121 | HANDLER(undef); 122 | 123 | HANDLER(flag); 124 | HANDLER(unset); 125 | 126 | HANDLER(ifset); 127 | HANDLER(ifnset); 128 | HANDLER(ifdef); 129 | HANDLER(ifndef); 130 | HANDLER(else); 131 | HANDLER(endif); 132 | #undef HANDLER 133 | #pragma endregion 134 | 135 | // members 136 | #pragma region members 137 | ccp _source; 138 | vector _lines; 139 | string _current_file; 140 | uint _current_line_no; 141 | string _current_original_line; 142 | 143 | vector _flags; 144 | map* _macros; 145 | stack* _branches; 146 | 147 | vector _blocked_files; 148 | uint _apply_depth; 149 | 150 | bool _had_error; 151 | ErrorDispatcher _error_dispatcher; 152 | #pragma endregion 153 | }; 154 | 155 | // ==== ============= ==== 156 | 157 | extern void initialize_state_singleton(Preprocessor* p); 158 | 159 | #endif -------------------------------------------------------------------------------- /include/scanner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_SCANNER_H 2 | #define EVI_SCANNER_H 3 | 4 | #include "types.hpp" 5 | 6 | #include 7 | #include 8 | 9 | typedef enum 10 | { 11 | // Single-character tokens. 12 | TOKEN_LEFT_PAREN, 13 | TOKEN_RIGHT_PAREN, 14 | TOKEN_LEFT_BRACE, 15 | TOKEN_RIGHT_BRACE, 16 | TOKEN_LEFT_B_BRACE, 17 | TOKEN_RIGHT_B_BRACE, 18 | TOKEN_SLASH, 19 | TOKEN_COMMA, 20 | TOKEN_SEMICOLON, 21 | TOKEN_STAR, 22 | TOKEN_MODULO, 23 | // TOKEN_DOLLAR, 24 | TOKEN_TILDE, 25 | TOKEN_AT, 26 | TOKEN_HASHTAG, 27 | TOKEN_DOT, 28 | 29 | // One or two character tokens. 30 | TOKEN_PLUS, 31 | TOKEN_PLUS_PLUS, 32 | TOKEN_MINUS, 33 | TOKEN_MINUS_MINUS, 34 | TOKEN_GREATER, 35 | TOKEN_GREATER_GREATER, 36 | TOKEN_LESS, 37 | TOKEN_LESS_LESS, 38 | TOKEN_EQUAL, 39 | TOKEN_EQUAL_EQUAL, 40 | TOKEN_PIPE, 41 | TOKEN_PIPE_PIPE, 42 | TOKEN_CARET, 43 | TOKEN_CARET_CARET, 44 | TOKEN_AND, 45 | TOKEN_AND_AND, 46 | TOKEN_QUESTION, 47 | TOKEN_QUESTION_QUESTION, 48 | TOKEN_COLON, 49 | TOKEN_COLON_COLON, 50 | TOKEN_BANG, 51 | TOKEN_BANG_BANG, 52 | 53 | // Multi-character tokens 54 | TOKEN_SLASH_EQUAL, 55 | TOKEN_GREATER_EQUAL, 56 | TOKEN_LESS_EQUAL, 57 | TOKEN_ARROW, 58 | TOKEN_ELIPSES, 59 | 60 | // Literals. 61 | TOKEN_VARIABLE_REF, 62 | TOKEN_PARAMETER_REF, 63 | TOKEN_IDENTIFIER, 64 | TOKEN_INTEGER, 65 | TOKEN_FLOAT, 66 | TOKEN_CHARACTER, 67 | TOKEN_STRING, 68 | TOKEN_TYPE, 69 | 70 | // misc. 71 | TOKEN_LINE_MARKER, 72 | TOKEN_ERROR, 73 | TOKEN_EOF 74 | } TokenType; 75 | 76 | char *get_tokentype_str(TokenType type); 77 | 78 | typedef struct 79 | { 80 | TokenType type; 81 | const char *source; 82 | const char *start; 83 | int length; 84 | int line; 85 | string* file; 86 | } Token; 87 | 88 | class Scanner 89 | { 90 | public: 91 | Scanner(); 92 | Scanner(const char *source); 93 | Token scanToken(); 94 | int getScannedLength(); 95 | 96 | private: 97 | const char *_src_start; 98 | const char *_start; 99 | const char *_current; 100 | int _line; 101 | 102 | string* _filename; 103 | 104 | bool isAtEnd(); 105 | bool isDigit(char c); 106 | bool isAlpha(char c); 107 | bool match(char expected); 108 | char advance(); 109 | char peek(); 110 | char peekNext(); 111 | Token makeToken(TokenType type); 112 | Token errorToken(const char *message); 113 | Token string(); 114 | Token character(); 115 | Token number(); 116 | Token reference(); 117 | Token directive(); 118 | Token type_or_identifier(); 119 | void skipWhitespaces(); 120 | }; 121 | 122 | void print_tokens_from_src(const char *src); 123 | 124 | static uint get_token_col(Token* token, int tab_width = -1) 125 | { 126 | if(token->type == TOKEN_ERROR) return 0; 127 | 128 | // get offset of token (first char) 129 | ptrdiff_t token_offset = token->start - token->source; 130 | 131 | // find first newline before token 132 | ptrdiff_t tok_ln_begin = token_offset; 133 | while(tok_ln_begin > 0 && token->source[tok_ln_begin] != '\n') tok_ln_begin--; 134 | tok_ln_begin++; // skip newline itself 135 | 136 | ptrdiff_t col = (token_offset - tok_ln_begin); 137 | 138 | // if tab width set account for that 139 | for(int i = -col; tab_width >= 0 && i < 0; i++) 140 | if(token->start[i] == '\t') col += tab_width; 141 | 142 | return col >= 0 ? col : 0; 143 | } 144 | 145 | #endif -------------------------------------------------------------------------------- /include/tools.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TOOLS_H 2 | #define TOOLS_H 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace tools { 10 | 11 | // string ops 12 | vector split_string(const string& str, const string& delimiter); 13 | string fstr(string format, ...); 14 | string replacestr(string source, string oldstring, string newstring); 15 | char escchr(char ogchar); 16 | const char* unescchr(char escchar); 17 | string escstr(string str); 18 | string unescstr(string str, bool ign_s_quotes = false, bool ign_d_quotes = false); 19 | 20 | // file ops 21 | int execbin(const char* executable, const char** argv); 22 | string readf(string path); 23 | void writef(string path, string text); 24 | } 25 | #endif -------------------------------------------------------------------------------- /include/typechecker.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_TYPECHECKER_H 2 | #define EVI_TYPECHECKER_H 3 | 4 | #include "common.hpp" 5 | #include "error.hpp" 6 | #include "ast.hpp" 7 | #include "lint.hpp" 8 | 9 | #include 10 | 11 | class TypeChecker: public Visitor 12 | { 13 | public: 14 | Status check(string path, ccp source, AST* astree); 15 | 16 | #define VISIT(_node) void visit(_node* node) 17 | VISIT(FuncDeclNode); 18 | VISIT(VarDeclNode); 19 | VISIT(AssignNode); 20 | VISIT(IfNode); 21 | VISIT(LoopNode); 22 | VISIT(ReturnNode); 23 | VISIT(BlockNode); 24 | VISIT(LogicalNode); 25 | VISIT(BinaryNode); 26 | VISIT(CastNode); 27 | VISIT(UnaryNode); 28 | VISIT(GroupingNode); 29 | VISIT(SubscriptNode); 30 | VISIT(LiteralNode); 31 | VISIT(ArrayNode); 32 | VISIT(SizeOfNode); 33 | VISIT(ReferenceNode); 34 | VISIT(CallNode); 35 | #undef VISIT 36 | 37 | private: 38 | string _infile; 39 | ErrorDispatcher _error_dispatcher; 40 | 41 | void error_at(Token *token, string message); 42 | void warning_at(Token *token, string message, bool print_token = false); 43 | 44 | // bc the visitor methods return void we instead 45 | // just use a stack for the stuff 46 | stack _type_stack; 47 | void push(ParsedType* type); 48 | ParsedType* pop(); 49 | 50 | ParsedType* resolve_types(ParsedType* left, ParsedType* right); 51 | bool can_cast_types(ParsedType* from, ParsedType* to); 52 | 53 | bool _panic_mode; 54 | bool _had_error; 55 | }; 56 | 57 | #endif -------------------------------------------------------------------------------- /include/types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_TYPES_H 2 | #define EVI_TYPES_H 3 | 4 | #include "common.hpp" 5 | #include "pch.h" 6 | #include "size.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // ================================ 13 | 14 | typedef enum 15 | { 16 | TYPE_BOOL, 17 | TYPE_CHARACTER, 18 | TYPE_INTEGER, 19 | TYPE_FLOAT, 20 | TYPE_VOID, 21 | 22 | TYPE_NONE 23 | } LexicalType; 24 | 25 | extern ccp lexical_type_strings[TYPE_NONE]; 26 | #define GET_LEX_TYPE_STR(type) (lexical_type_strings[type]) 27 | 28 | // ================================ 29 | 30 | class EviType 31 | { 32 | public: 33 | llvm::Type* _llvm_type; 34 | LexicalType _default_type; 35 | 36 | string _name; 37 | int _alignment; 38 | bool _is_signed; 39 | 40 | bool eq(EviType* rhs); 41 | string to_string(); 42 | }; 43 | 44 | #define EVI_INT_TYPE(name, bitsnum, issigned) \ 45 | (new EviType{llvm::IntegerType::get(__context, bitsnum), \ 46 | TYPE_INTEGER, name, 4, issigned}) 47 | 48 | // ================================ 49 | 50 | extern llvm::LLVMContext __context; 51 | extern map __evi_types; 52 | static bool __evi_builtin_types_initialized = false; 53 | 54 | // ================================ 55 | 56 | class ParsedType 57 | { 58 | private: 59 | EviType* _evi_type; 60 | ParsedType* _subtype = nullptr; 61 | 62 | bool _invalid = false; 63 | 64 | public: 65 | 66 | ParsedType() {}; 67 | ParsedType(LexicalType lexical_type, 68 | EviType* evi_type = nullptr, 69 | bool is_reference = false, 70 | ParsedType* subtype = nullptr); 71 | static ParsedType* new_invalid(); 72 | 73 | ParsedType* copy(); 74 | ParsedType* copy_change_lex(LexicalType type); 75 | ParsedType* copy_pointer_to(); 76 | ParsedType* copy_element_of(); 77 | void set_lex_type(LexicalType type); 78 | 79 | string to_string(bool __first = true); 80 | ccp to_c_string(); 81 | llvm::Type* get_llvm_type(); 82 | 83 | bool eq(ParsedType* rhs, bool simple = false); 84 | uint get_alignment(); 85 | uint get_depth(); 86 | bool is_pointer(); 87 | bool is_signed(); 88 | bool is_constant(); 89 | 90 | bool is_invalid(); 91 | 92 | LexicalType _lexical_type; 93 | bool _is_constant = false; 94 | bool _is_reference = false; 95 | bool _keep_as_reference = false; 96 | }; 97 | 98 | #define PTYPE(...) (new ParsedType(__VA_ARGS__)) 99 | #define AS_LEX(ptype) (ptype->_lexical_type) 100 | 101 | // ================================ 102 | 103 | #define ADD_EVI_TYPE(name, type) __evi_types.insert(pair(name, type)) 104 | #define IS_EVI_TYPE(name) (__evi_types.find(name) != __evi_types.end()) 105 | #define GET_EVI_TYPE(name) (_get_evi_type(name)) 106 | 107 | static EviType* _get_evi_type(string name) 108 | { 109 | ASSERT_OR_THROW_INTERNAL_ERROR(IS_EVI_TYPE(name), "in type retrieval"); 110 | return __evi_types.at(name); 111 | } 112 | 113 | static void init_builtin_evi_types() 114 | { 115 | // prevent multiple initializations 116 | if(__evi_builtin_types_initialized) return; 117 | __evi_builtin_types_initialized = true; 118 | 119 | lexical_type_strings[TYPE_INTEGER] = "integer"; 120 | lexical_type_strings[TYPE_FLOAT] = "float"; 121 | lexical_type_strings[TYPE_BOOL] = "boolean"; 122 | lexical_type_strings[TYPE_CHARACTER] = "character"; 123 | lexical_type_strings[TYPE_VOID] = "void"; 124 | 125 | // ======================= Integers ======================= 126 | ADD_EVI_TYPE("i1", EVI_INT_TYPE("i1", 1, true)); 127 | 128 | ADD_EVI_TYPE("i4", EVI_INT_TYPE("i4", 4, true)); 129 | ADD_EVI_TYPE("ui4", EVI_INT_TYPE("ui4", 4, false)); 130 | 131 | ADD_EVI_TYPE("i8", EVI_INT_TYPE("i8", 8, true)); 132 | ADD_EVI_TYPE("ui8", EVI_INT_TYPE("ui8", 8, false)); 133 | 134 | ADD_EVI_TYPE("i16", EVI_INT_TYPE("i16", 16, true)); 135 | ADD_EVI_TYPE("ui16",EVI_INT_TYPE("ui16",16, false)); 136 | 137 | ADD_EVI_TYPE("i32", EVI_INT_TYPE("i32", 32, true)); 138 | ADD_EVI_TYPE("ui32",EVI_INT_TYPE("ui32",32, false)); 139 | 140 | ADD_EVI_TYPE("i64", EVI_INT_TYPE("i64", 64, true)); 141 | ADD_EVI_TYPE("ui64",EVI_INT_TYPE("ui64",64, false)); 142 | 143 | ADD_EVI_TYPE("i128", EVI_INT_TYPE("i128", 128, true)); 144 | ADD_EVI_TYPE("ui128",EVI_INT_TYPE("ui128",128, false)); 145 | 146 | // ======================== Floats ======================== 147 | ADD_EVI_TYPE("flt", (new EviType{llvm::Type::getFloatTy(__context), TYPE_FLOAT, "flt", 4, true})); 148 | ADD_EVI_TYPE("dbl", (new EviType{llvm::Type::getDoubleTy(__context), TYPE_FLOAT, "dbl", 4, true})); 149 | 150 | // ======================== Others ======================== 151 | ADD_EVI_TYPE("sze", EVI_INT_TYPE("sze", SIZE_TYPE_SIZE, false)); 152 | ADD_EVI_TYPE("bln", (new EviType{llvm::Type::getInt1Ty(__context), TYPE_BOOL, "bln", 1, false})); 153 | ADD_EVI_TYPE("chr", (new EviType{llvm::Type::getInt8Ty(__context), TYPE_CHARACTER, "chr", 8, false})); 154 | ADD_EVI_TYPE("nll", (new EviType{llvm::Type::getVoidTy(__context), TYPE_VOID, "nll"})); 155 | } 156 | 157 | #endif -------------------------------------------------------------------------------- /include/visualizer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EVI_VISUALIZER_H 2 | #define EVI_VISUALIZER_H 3 | 4 | #include "ast.hpp" 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | class ASTVisualizer: public Visitor 11 | { 12 | public: 13 | void visualize(string path, AST* astree); 14 | 15 | #define VISIT(_node) void visit(_node* node) 16 | VISIT(FuncDeclNode); 17 | VISIT(VarDeclNode); 18 | VISIT(AssignNode); 19 | VISIT(IfNode); 20 | VISIT(LoopNode); 21 | VISIT(ReturnNode); 22 | VISIT(BlockNode); 23 | VISIT(LogicalNode); 24 | VISIT(BinaryNode); 25 | VISIT(CastNode); 26 | VISIT(UnaryNode); 27 | VISIT(GroupingNode); 28 | VISIT(SubscriptNode); 29 | VISIT(LiteralNode); 30 | VISIT(ArrayNode); 31 | VISIT(SizeOfNode); 32 | VISIT(ReferenceNode); 33 | VISIT(CallNode); 34 | #undef VISIT 35 | 36 | private: 37 | stringstream _stream; 38 | int _nodecount; 39 | }; 40 | 41 | #define HEADER "digraph astgraph {\n\ 42 | node [shape=rect, fontsize=12, fontname=\"Courier\", height=.1];\n\ 43 | ranksep=.4;\n\ 44 | edge [arrowsize=.5, arrowhead=\"none\"]\n\ 45 | rankdir=\"UD\"\n\ 46 | node0 [label=\"Program\"]\n\ 47 | \n\ 48 | " 49 | #define FOOTER "}" 50 | 51 | #endif -------------------------------------------------------------------------------- /include/x86_64-linux-gnu/ld_args.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBC_VERSION 2 | #error "LIBC_VERSION not defined! (e.g. \"8\")" 3 | #endif 4 | 5 | #define LD_ARGS LD_PATH, \ 6 | "-dynamic-linker", "/lib64/ld-linux-x86-64.so.2", \ 7 | "/usr/lib/gcc/x86_64-linux-gnu/" LIBC_VERSION "/crtbegin.o", \ 8 | "/usr/lib/gcc/x86_64-linux-gnu/" LIBC_VERSION "/crtend.o", \ 9 | "/usr/lib/x86_64-linux-gnu/crt1.o", \ 10 | "/usr/lib/x86_64-linux-gnu/crti.o", \ 11 | "/usr/lib/x86_64-linux-gnu/crtn.o", \ 12 | "-L/usr/lib/gcc/x86_64-linux-gnu/" LIBC_VERSION "/", \ 13 | "-L/usr/lib/gcc/x86_64-linux-gnu/" LIBC_VERSION "/", \ 14 | "-L" STATICLIB_DIR, \ 15 | infile, \ 16 | "-levi", \ 17 | "-lc", \ 18 | "-lgcc", \ 19 | "-lm", \ 20 | "-o", outfile 21 | 22 | #define LD_ARGC 18 -------------------------------------------------------------------------------- /include/x86_64-linux-gnu/size.h: -------------------------------------------------------------------------------- 1 | #define SIZE_TYPE_SIZE 64 -------------------------------------------------------------------------------- /src/debug.cpp: -------------------------------------------------------------------------------- 1 | #include "debug.hpp" 2 | #include 3 | 4 | DebugInfoBuilder::DebugInfoBuilder(llvm::Module* module, IRBUILDER builder, string filepath, bool optimized) 5 | { 6 | // get current directory 7 | _directory = string(getcwd(nullptr, 0)); 8 | 9 | // create the builder and compile unit 10 | _dbuilder = make_unique(*module); 11 | 12 | _cunit = _dbuilder->createCompileUnit( 13 | llvm::dwarf::DW_LANG_C, // closest thing to Evi 14 | create_file_unit(filepath), 15 | APP_NAME_INTERNAL " version " APP_VERSION, optimized, "", 0 16 | ); 17 | 18 | // current debug info version 19 | module->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION); 20 | 21 | // Darwin only supports dwarf2. 22 | if(llvm::Triple(llvm::sys::getProcessTriple()).isOSDarwin()) 23 | module->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 2); 24 | 25 | _ibuilder = builder; 26 | } 27 | 28 | void DebugInfoBuilder::finish() 29 | { 30 | // finalize the DIBuilder 31 | _dbuilder->finalize(); 32 | DEBUG_PRINT_MSG("Debug info builder finalized."); 33 | } 34 | 35 | // =================================================== 36 | 37 | llvm::DIFile* DebugInfoBuilder::create_file_unit(string filename) 38 | { 39 | // just create the file and return it 40 | return _dbuilder->createFile(filename, _directory); 41 | } 42 | 43 | llvm::DISubprogram* DebugInfoBuilder::create_subprogram(FuncDeclNode* node, llvm::DIFile* file_unit) 44 | { 45 | // llvm::DITemplateParameterArray params = llvm::DITemplateParameterArray(); 46 | 47 | llvm::DISubprogram* subprog = _dbuilder->createFunction( 48 | file_unit, node->_identifier, node->_identifier, 49 | file_unit, node->_token.line, get_function_type(node, file_unit), node->_token.line, 50 | llvm::DINode::FlagPrototyped, llvm::DISubprogram::SPFlagDefinition); //, params); 51 | 52 | return subprog; 53 | } 54 | 55 | llvm::DILocalVariable* DebugInfoBuilder::create_variable(string ident, int line_no, ParsedType* type, bool global) 56 | { 57 | string name = '$' + ident; 58 | llvm::DIScope* scope = _scopes.back(); 59 | 60 | return _dbuilder->createAutoVariable(scope, name, scope->getFile(), line_no, get_type(type)); 61 | } 62 | 63 | llvm::DILocalVariable* DebugInfoBuilder::create_parameter(int index, int line_no, ParsedType* type) 64 | { 65 | string name = '$' + to_string(index); 66 | llvm::DIScope* scope = _scopes.back(); 67 | 68 | return _dbuilder->createParameterVariable(scope, name, index, 69 | scope->getFile(), line_no, get_type(type)); 70 | } 71 | 72 | // =================================================== 73 | 74 | void DebugInfoBuilder::emit_location(uint line, uint col) 75 | { 76 | llvm::DIScope *scope = _scopes.empty() ? _cunit : _scopes.back(); 77 | _ibuilder->SetCurrentDebugLocation(llvm::DILocation::get(scope->getContext(), line, col, scope)); 78 | } 79 | 80 | void DebugInfoBuilder::insert_declare(llvm::Value* storage, llvm::DILocalVariable* varinfo) 81 | { 82 | _dbuilder->insertDeclare(storage, varinfo, 83 | _dbuilder->createExpression(), 84 | _ibuilder->getCurrentDebugLocation(), 85 | _ibuilder->GetInsertBlock() 86 | ); 87 | } 88 | 89 | void DebugInfoBuilder::push_subprogram(llvm::DISubprogram* sub_program) 90 | { 91 | _scopes.push_back(sub_program); 92 | emit_location(0, 0); 93 | } 94 | 95 | void DebugInfoBuilder::pop_subprogram() 96 | { 97 | // just a lil pop 98 | _scopes.pop_back(); 99 | } 100 | 101 | // =================================================== 102 | 103 | llvm::DIType* DebugInfoBuilder::get_type(ParsedType* type) 104 | { 105 | if(type->is_pointer()) 106 | { 107 | llvm::DIType* elementtype = get_type(type->copy_element_of()); 108 | return _dbuilder->createPointerType(elementtype, elementtype->getSizeInBits()); 109 | } 110 | 111 | // get size in bits 112 | uint64_t size = type->get_alignment() * 8; 113 | // uint64_t size = _ir_builder->GetInsertPoint()->getModule()->getDataLayout() 114 | // .getTypeAllocSize(type->get_llvm_type()).getFixedSize(); 115 | 116 | // get encoding 117 | llvm::dwarf::TypeKind encoding = (llvm::dwarf::TypeKind)0; 118 | switch(type->_lexical_type) 119 | { 120 | case TYPE_BOOL: encoding = llvm::dwarf::DW_ATE_boolean; break; 121 | case TYPE_CHARACTER: encoding = llvm::dwarf::DW_ATE_unsigned_char; break; 122 | case TYPE_FLOAT: encoding = llvm::dwarf::DW_ATE_float; break; 123 | case TYPE_VOID: encoding = (llvm::dwarf::TypeKind)0; break; 124 | case TYPE_INTEGER: encoding = type->is_signed() ? 125 | llvm::dwarf::DW_ATE_signed : 126 | llvm::dwarf::DW_ATE_unsigned; break; 127 | default: THROW_INTERNAL_ERROR("during debuging info generation"); 128 | } 129 | 130 | // create and return type 131 | return _dbuilder->createBasicType(type->to_string(), size, encoding); 132 | 133 | } 134 | 135 | llvm::DISubroutineType* DebugInfoBuilder::get_function_type(FuncDeclNode* node, llvm::DIFile* file_unit) 136 | { 137 | vector types; 138 | 139 | types.push_back(get_type(node->_ret_type)); 140 | 141 | for (int i = 0; i < node->_params.size(); i++) 142 | types.push_back(get_type(node->_params[i])); 143 | 144 | return _dbuilder->createSubroutineType(_dbuilder->getOrCreateTypeArray(types)); 145 | } -------------------------------------------------------------------------------- /src/error.cpp: -------------------------------------------------------------------------------- 1 | #include "error.hpp" 2 | 3 | void ErrorDispatcher::print_token_marked(Token *token, ccp color) 4 | { 5 | string tokenline = ""; 6 | string markerline = ""; 7 | 8 | // get offset of token (first char) 9 | ptrdiff_t token_offset = token->start - token->source; 10 | 11 | // find first newline before token 12 | ptrdiff_t tok_ln_begin; 13 | { 14 | tok_ln_begin = token_offset; 15 | while(tok_ln_begin > 0 && token->source[tok_ln_begin] != '\n') tok_ln_begin--; 16 | tok_ln_begin++; // skip newline itself 17 | } 18 | 19 | // find first newline after token 20 | ptrdiff_t tok_ln_end = token_offset + token->length; 21 | while(token->source[tok_ln_end] != '\n' && token->source[tok_ln_end] != '\0') tok_ln_end++; 22 | 23 | ptrdiff_t tok_ln_before_tok_len; // keep for later use 24 | 25 | // get line with marked token 26 | { 27 | /* 28 | Inserting the escape codes for coloring won't work, so instead we find 29 | length A and B as seen below: 30 | 31 | this is an example line with a WRONG token in it. 32 | <------------- A -------------> <---- B ----> 33 | 34 | And use those and the info we have of the token to merge them, 35 | and the escape codes together in a new string. 36 | */ 37 | 38 | // find string on the token's line before and after the token itself 39 | tok_ln_before_tok_len = token_offset - tok_ln_begin; 40 | string tok_ln_before_tok = string(token->source + tok_ln_begin, tok_ln_before_tok_len); 41 | 42 | ptrdiff_t tok_ln_after_tok_len = tok_ln_end - token_offset - token->length; 43 | string tok_ln_after_tok = string(token->source + token_offset + token->length, tok_ln_after_tok_len); 44 | 45 | // get token and its full line 46 | string tok = string(token->start, token->length); 47 | string tok_ln = tok_ln_before_tok + color + tok + COLOR_NONE + tok_ln_after_tok; 48 | tokenline = tools::fstr(" %3d| %s", token->line, tok_ln.c_str()); 49 | } 50 | 51 | // get the marker line 52 | { 53 | /* 54 | Example: 55 | 56 | 69| @this i32 WRONG (); 57 | ^~~~~ 58 | */ 59 | 60 | int prefixlen = tools::fstr(" %3d| ", token->line).length(); 61 | markerline += string(prefixlen, ' '); 62 | 63 | for(int i = 0; i < tok_ln_before_tok_len; i++) 64 | markerline += token->source[tok_ln_begin + i] == '\t' ? '\t' : ' '; 65 | 66 | markerline += string(color) + "^"; 67 | markerline += string(token->length - 1, '~'); 68 | 69 | markerline += COLOR_NONE; 70 | } 71 | 72 | // print it all out 73 | cerr << tokenline << endl; 74 | cerr << markerline << endl; 75 | } 76 | 77 | void ErrorDispatcher::print_line_marked(uint line_no, string line, ccp color) 78 | { 79 | string prefix = tools::fstr(" %3d| ", line_no); 80 | 81 | cerr << prefix + line << endl; 82 | 83 | cerr << COLOR_RED; 84 | cerr << string(prefix.length(), ' ') + ('^' + string(line.length() - 1, '~')); 85 | cerr << COLOR_NONE; 86 | cerr << endl; 87 | } 88 | 89 | void ErrorDispatcher::__dispatch(ccp color, ccp prompt, ccp message) 90 | { 91 | cerr << tools::fstr("[evi] %s%s" COLOR_NONE ": %s", 92 | color, prompt, message) << endl; 93 | } 94 | 95 | void ErrorDispatcher::__dispatch_at_token(ccp color, Token* token, ccp prompt, ccp message) 96 | { 97 | fprintf(stderr, "[%s:%d] %s%s" COLOR_NONE ": %s\n", 98 | token->file->c_str(), token->line, color, prompt, message); 99 | } 100 | 101 | // if line == 0 lineno is omitted. likewise with filename 102 | void ErrorDispatcher::__dispatch_at_line(ccp color, uint line, ccp filename, ccp prompt, ccp message) 103 | { 104 | if(line) fprintf(stderr, "[%s:%d] %s%s" COLOR_NONE ": %s\n", 105 | filename ? filename : "???", line, color, prompt, message); 106 | 107 | else fprintf(stderr, "[%s] %s%s" COLOR_NONE ": %s\n", 108 | filename ? filename : "???", color, prompt, message); 109 | } 110 | 111 | 112 | void ErrorDispatcher::error(ccp prompt, ccp message) 113 | { __dispatch(COLOR_RED, prompt, message); } 114 | 115 | void ErrorDispatcher::error_at_line(uint line, ccp filename, ccp prompt, ccp message) 116 | { __dispatch_at_line(COLOR_RED, line, filename, prompt, message); } 117 | 118 | void ErrorDispatcher::error_at_token(Token* token, ccp prompt, ccp message) 119 | { __dispatch_at_token(COLOR_RED, token, prompt, message); } 120 | 121 | void ErrorDispatcher::warning(ccp prompt, ccp message) 122 | { __dispatch(COLOR_PURPLE, prompt, message); } 123 | 124 | void ErrorDispatcher::warning_at_line(uint line, ccp filename, ccp prompt, ccp message) 125 | { __dispatch_at_line(COLOR_PURPLE, line, filename, prompt, message); } 126 | 127 | void ErrorDispatcher::warning_at_token(Token* token, ccp prompt, ccp message) 128 | { __dispatch_at_token(COLOR_PURPLE, token, prompt, message); } 129 | 130 | void ErrorDispatcher::note(ccp message) 131 | { __dispatch(COLOR_GREEN, "Note", message); } 132 | 133 | void ErrorDispatcher::note_at_line(uint line, ccp filename, ccp message) 134 | { __dispatch_at_line(COLOR_GREEN, line, filename, "Note", message); } 135 | 136 | void ErrorDispatcher::note_at_token(Token* token, ccp message) 137 | { __dispatch_at_token(COLOR_GREEN, token, "Note", message); } 138 | -------------------------------------------------------------------------------- /src/lint.cpp: -------------------------------------------------------------------------------- 1 | #include "lint.hpp" 2 | #include "tools.hpp" 3 | 4 | lint_args_t lint_args = { LINT_NONE, -1, {0, 0} }; 5 | std::string lint_output; 6 | 7 | void lint_output_diagnostic_object(Token* token, string message, ccp type) 8 | { 9 | LINT_OUTPUT_START_PLAIN_OBJECT(); 10 | 11 | if(token->type == TOKEN_ERROR) 12 | { 13 | LINT_OUTPUT_PAIR_F("invalid", "true", %s); 14 | LINT_OUTPUT_ARRAY_START("related"); 15 | return; 16 | } 17 | 18 | LINT_OUTPUT_PAIR("file", *token->file); 19 | LINT_OUTPUT_PAIR_F("line", token->line, %d); 20 | LINT_OUTPUT_PAIR_F("column", get_token_col(token), %u); 21 | LINT_OUTPUT_PAIR_F("length", token->length, %d); 22 | LINT_OUTPUT_PAIR("message", tools::replacestr(message, "\"", "\\\"")); 23 | LINT_OUTPUT_PAIR("type", type); 24 | 25 | LINT_OUTPUT_ARRAY_START("related"); 26 | } 27 | 28 | void lint_output_diagnostic_object_end() 29 | { 30 | LINT_OUTPUT_ARRAY_END(); 31 | LINT_OUTPUT_OBJECT_END(); 32 | } -------------------------------------------------------------------------------- /src/macros.cpp: -------------------------------------------------------------------------------- 1 | #include "macros.hpp" 2 | #include 3 | 4 | // ===================================================== 5 | 6 | static uint counter_value = 0; 7 | 8 | // ===================================================== 9 | 10 | #define MACRO_GETTER(name) string macro_getter_##name() 11 | 12 | MACRO_GETTER(line) 13 | { 14 | return to_string(State::get_current_line_no()); 15 | } 16 | 17 | MACRO_GETTER(file) 18 | { 19 | return '"' + State::get_current_file() + '"'; 20 | } 21 | 22 | MACRO_GETTER(time_) 23 | { 24 | time_t t = time(nullptr); 25 | struct tm* tm = localtime(&t); 26 | 27 | // make sure that when 32 bytes isn't enough we try again and again 28 | size_t max_size = 32; 29 | size_t size = max_size; 30 | while(size == max_size) 31 | { 32 | size = strftime(nullptr, max_size, "%H:%M:%S", tm) + 1; 33 | max_size += 8; 34 | } 35 | 36 | // get the actual string 37 | char* date = (char*)malloc(size); 38 | strftime(date, size, "%H:%M:%S", tm); 39 | 40 | return '"' + string(date) + '"'; 41 | } 42 | 43 | MACRO_GETTER(date) 44 | { 45 | time_t t = time(nullptr); 46 | struct tm* tm = localtime(&t); 47 | 48 | // make sure that when 32 bytes isn't enough we try again and again 49 | size_t max_size = 32; 50 | size_t size = max_size; 51 | while(size == max_size) 52 | { 53 | size = strftime(nullptr, max_size, "%b %d %Y", tm) + 1; 54 | max_size += 8; 55 | } 56 | 57 | // get the actual string 58 | char* date = (char*)malloc(size); 59 | strftime(date, size, "%b %d %Y", tm); 60 | 61 | return '"' + string(date) + '"'; 62 | } 63 | 64 | MACRO_GETTER(counter) 65 | { 66 | return to_string(counter_value++); 67 | } 68 | 69 | MACRO_GETTER(apply_depth) 70 | { 71 | return to_string(State::get_apply_depth()); 72 | } 73 | 74 | // ===================================================== 75 | 76 | void initialize_state_singleton(Preprocessor* p) 77 | { 78 | State::_p = p; 79 | State::_initialized = true; 80 | } 81 | 82 | void Preprocessor::initialize_builtin_macros() 83 | { 84 | #define ADD_DYNAMIC_MACRO(name, getter) _macros->insert(pair(\ 85 | name, {true, "", ¯o_getter_##getter})) 86 | #define ADD_STATIC_MACRO(name, format) _macros->insert(pair(\ 87 | name, {false, format, nullptr})) 88 | 89 | // compiler information 90 | ADD_STATIC_MACRO("__COMPILER_NAME__", "\"" APP_NAME_INTERNAL "\""); 91 | ADD_STATIC_MACRO("__COMPILER_VERSION__", "\"" APP_VERSION "\""); 92 | ADD_STATIC_MACRO("__COMPILER_BUILD_TARGET__", "\"" TARGET "\""); 93 | ADD_STATIC_MACRO("__COMPILER_BUILD_DATE__", "\"" __DATE__ "\""); 94 | ADD_STATIC_MACRO("__COMPILER_BUILD_TIME__", "\"" __TIME__ "\""); 95 | ADD_STATIC_MACRO("__OPERATING_SYSTEM__", "\"" OS_NAME "\""); 96 | 97 | // basic macros 98 | ADD_DYNAMIC_MACRO("__LINE__", line); 99 | ADD_DYNAMIC_MACRO("__FILE__", file); 100 | ADD_DYNAMIC_MACRO("__TIME__", time_); 101 | ADD_DYNAMIC_MACRO("__DATE__", date); 102 | ADD_DYNAMIC_MACRO("__COUNTER__", counter); 103 | ADD_DYNAMIC_MACRO("__APPLY_DEPTH__", apply_depth); 104 | } 105 | -------------------------------------------------------------------------------- /src/tools.cpp: -------------------------------------------------------------------------------- 1 | #include "tools.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | // ========= string ops ========= 20 | 21 | // split string by delimeter 22 | vector tools::split_string(const string& str, const string& delimiter) 23 | { 24 | vector strings; 25 | 26 | string::size_type pos = 0; 27 | string::size_type prev = 0; 28 | while ((pos = str.find(delimiter, prev)) != string::npos) 29 | { 30 | strings.push_back(str.substr(prev, pos - prev)); 31 | prev = pos + 1; 32 | } 33 | 34 | // To get the last substring (or only, if delimiter is not found) 35 | strings.push_back(str.substr(prev)); 36 | 37 | return strings; 38 | } 39 | 40 | // replace substring 41 | string tools::replacestr(string source, string oldstring, string newstring) 42 | { 43 | // https://stackoverflow.com/a/7724536 44 | string retval; 45 | string::const_iterator end = source.end(); 46 | string::const_iterator current = source.begin(); 47 | string::const_iterator next = search( current, end, oldstring.begin(), oldstring.end() ); 48 | while (next != end) 49 | { 50 | retval.append(current, next); 51 | retval.append(newstring); 52 | current = next + oldstring.size(); 53 | next = search( current, end, oldstring.begin(), oldstring.end() ); 54 | } 55 | retval.append(current, next); 56 | return retval; 57 | } 58 | 59 | // format the given strign 60 | string tools::fstr(string format, ...) 61 | { 62 | va_list args; 63 | va_start(args, format.c_str()); 64 | 65 | int smallSize = sizeof(char) * 1024; 66 | char *smallBuffer = (char*)malloc(smallSize); 67 | 68 | int size = vsnprintf(smallBuffer, smallSize, format.c_str(), args); 69 | 70 | va_end(args); 71 | 72 | if (size < sizeof smallBuffer) return string(smallBuffer); 73 | 74 | int bigSize = sizeof(char) * (size + 1); 75 | char *buffer = (char*)malloc(bigSize); 76 | 77 | va_start(args, format.c_str()); 78 | vsnprintf(buffer, bigSize, format.c_str(), args); 79 | va_end(args); 80 | 81 | return string(buffer); 82 | } 83 | 84 | // e.g. turns 'n' into an actual newline (returns -1 if invalid) 85 | char tools::escchr(char ogchar) 86 | { 87 | switch(ogchar) 88 | { 89 | case 'a': return '\a'; 90 | case 'b': return '\b'; 91 | case 'e': return '\e'; 92 | case 'f': return '\f'; 93 | case 'n': return '\n'; 94 | case 'r': return '\r'; 95 | case 't': return '\t'; 96 | case 'v': return '\v'; 97 | case '\\': return '\\'; 98 | case '\'': return '\''; 99 | case '\"': return '\"'; 100 | case '0': return '\0'; 101 | default: return -1; 102 | } 103 | } 104 | 105 | // turns e.g. a newline into "\\n" 106 | const char* tools::unescchr(char escchar) 107 | { 108 | switch(escchar) 109 | { 110 | case '\a': return "\\a"; 111 | case '\b': return "\\b"; 112 | case '\e': return "\\e"; 113 | case '\f': return "\\f"; 114 | case '\n': return "\\n"; 115 | case '\r': return "\\r"; 116 | case '\t': return "\\t"; 117 | case '\v': return "\\v"; 118 | case '\\': return "\\\\"; 119 | case '\'': return "\\\'"; 120 | case '\"': return "\\\""; 121 | case '\0': return "\\0"; 122 | default: return new char[2]{escchar, '\0'}; 123 | } 124 | } 125 | 126 | // turn shit like '\n' into '\\n' 127 | string tools::unescstr(string str, bool ign_s_quotes, bool ign_d_quotes) 128 | { 129 | stringstream ret; 130 | for(int i = 0; i < str.length(); i++) 131 | { 132 | #define CASE(ch, res) case ch: ret << res; break 133 | switch(str[i]) 134 | { 135 | CASE('\a', "\\a"); 136 | CASE('\b', "\\b"); 137 | CASE('\e', "\\e"); 138 | CASE('\f', "\\f"); 139 | CASE('\n', "\\n"); 140 | CASE('\r', "\\r"); 141 | CASE('\t', "\\t"); 142 | CASE('\v', "\\v"); 143 | CASE('\\', "\\\\"); 144 | CASE('\'', (ign_s_quotes ? "\'" : "\\\'")); 145 | CASE('\"', (ign_d_quotes ? "\"" : "\\\"")); 146 | // CASE('\0', "\\0"); 147 | default: ret << str[i]; 148 | } 149 | #undef CASE 150 | } 151 | return ret.str(); 152 | } 153 | 154 | string tools::escstr(string str) 155 | { 156 | stringstream ret; 157 | bool isesc = false; 158 | for(char& ch : str) 159 | { 160 | if(ch == '\\') isesc = true; 161 | else if(isesc) 162 | { 163 | ret << escchr(ch); 164 | isesc = false; 165 | } 166 | else ret << ch; 167 | } 168 | return ret.str(); 169 | } 170 | 171 | // ========== file ops ========== 172 | 173 | // read contents of file to string 174 | string tools::readf(string path) 175 | { 176 | FILE *file = fopen(path.c_str(), "rb"); 177 | if (file == NULL) 178 | { 179 | fprintf(stderr, "Could not open file \"%s\".\n", path.c_str()); 180 | exit(74); 181 | } 182 | 183 | fseek(file, 0L, SEEK_END); 184 | size_t fileSize = ftell(file); 185 | rewind(file); 186 | 187 | char *buffer = (char *)malloc(fileSize + 1); 188 | if (buffer == NULL) 189 | { 190 | fprintf(stderr, "Not enough memory to read \"%s\".\n", path.c_str()); 191 | exit(74); 192 | } 193 | size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); 194 | if (bytesRead < fileSize) 195 | { 196 | fprintf(stderr, "Could not read file \"%s\".\n", path.c_str()); 197 | exit(74); 198 | } 199 | buffer[bytesRead] = '\0'; 200 | 201 | fclose(file); 202 | return buffer; 203 | } 204 | 205 | // write string to file 206 | void tools::writef(string path, string text) 207 | { 208 | fstream file_out(path, ios_base::out); 209 | 210 | if (!file_out.is_open()) 211 | { 212 | cerr << "failed to open " << path << '\n'; 213 | exit(74); 214 | } 215 | 216 | file_out << text; 217 | file_out.close(); 218 | } 219 | -------------------------------------------------------------------------------- /src/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.hpp" 2 | 3 | ParsedType::ParsedType(LexicalType lexical_type, EviType* evi_type, 4 | bool is_reference, ParsedType* subtype) 5 | { 6 | _lexical_type = lexical_type; 7 | 8 | if(evi_type) _evi_type = evi_type; 9 | else switch(lexical_type) 10 | { 11 | case TYPE_BOOL: _evi_type = GET_EVI_TYPE("bln"); break; 12 | case TYPE_CHARACTER: _evi_type = GET_EVI_TYPE("chr"); break; 13 | case TYPE_INTEGER: _evi_type = GET_EVI_TYPE("i32"); break; 14 | case TYPE_FLOAT: _evi_type = GET_EVI_TYPE("dbl"); break; 15 | case TYPE_VOID: _evi_type = GET_EVI_TYPE("nll"); break; 16 | case TYPE_NONE: // break; 17 | default: THROW_INTERNAL_ERROR("in type construction"); 18 | } 19 | 20 | _is_reference = is_reference; 21 | _keep_as_reference = false; 22 | _invalid = false; 23 | _subtype = subtype; 24 | } 25 | 26 | ParsedType* ParsedType::new_invalid() 27 | { 28 | ParsedType* type = new ParsedType(); 29 | type->_invalid = true; 30 | return type; 31 | } 32 | 33 | ParsedType* ParsedType::copy() 34 | { 35 | ASSERT_OR_THROW_INTERNAL_ERROR(!is_invalid(), "during llvm type generation"); 36 | 37 | ParsedType* ret = new ParsedType(_lexical_type, _evi_type, false, nullptr); 38 | ret->_is_constant = _is_constant; 39 | if(_subtype) ret->_subtype = _subtype->copy(); 40 | return ret; 41 | } 42 | 43 | ParsedType* ParsedType::copy_change_lex(LexicalType type) 44 | { 45 | ParsedType* ret = this->copy(); 46 | set_lex_type(type); 47 | return ret; 48 | } 49 | 50 | ParsedType* ParsedType::copy_pointer_to() 51 | { 52 | ParsedType* ret = this->copy(); 53 | ret->_subtype = this->copy(); 54 | return ret; 55 | } 56 | 57 | ParsedType* ParsedType::copy_element_of() 58 | { 59 | ASSERT_OR_THROW_INTERNAL_ERROR(_subtype, "in type clone"); 60 | return _subtype->copy(); 61 | } 62 | 63 | void ParsedType::set_lex_type(LexicalType type) 64 | { 65 | _lexical_type = type; 66 | if(_subtype) _subtype->set_lex_type(type); 67 | } 68 | 69 | 70 | string ParsedType::to_string(bool __first) 71 | { 72 | if(is_invalid()) return "???"; 73 | 74 | string str = _subtype ? _subtype->to_string(false) + '*' : _evi_type->_name; 75 | if(_is_constant && __first) return '!' + str; 76 | else return str; 77 | } 78 | 79 | ccp ParsedType::to_c_string() 80 | { 81 | // ez 82 | return strdup(to_string().c_str()); 83 | } 84 | 85 | llvm::Type* ParsedType::get_llvm_type() 86 | { 87 | ASSERT_OR_THROW_INTERNAL_ERROR(!is_invalid(), "during llvm type generation"); 88 | 89 | if(_subtype) 90 | { 91 | // for llvm void* is invalid 92 | if(!_subtype->is_pointer() && _subtype->_lexical_type == TYPE_VOID) 93 | return llvm::IntegerType::getInt8PtrTy(__context); 94 | return _subtype->get_llvm_type()->getPointerTo(); 95 | } 96 | else return _evi_type->_llvm_type; 97 | } 98 | 99 | 100 | bool ParsedType::eq(ParsedType* rhs, bool simple) 101 | { 102 | if(simple && (!get_depth() && _lexical_type == TYPE_BOOL && !rhs->get_depth() && rhs->_lexical_type != TYPE_VOID)) 103 | return true; 104 | 105 | if(_subtype) return get_depth() == rhs->get_depth() && _subtype->eq(rhs->_subtype); 106 | 107 | return _lexical_type == rhs->_lexical_type 108 | && (simple || _evi_type->eq(rhs->_evi_type)); 109 | } 110 | 111 | uint ParsedType::get_alignment() 112 | { 113 | if(_subtype) return POINTER_ALIGNMENT; 114 | else return _evi_type->_alignment; 115 | } 116 | 117 | uint ParsedType::get_depth() 118 | { 119 | if(_subtype) return _subtype->get_depth() + 1; 120 | else return 0; 121 | } 122 | 123 | bool ParsedType::is_pointer() 124 | { 125 | // ez 126 | return (bool)_subtype; 127 | } 128 | 129 | bool ParsedType::is_signed() 130 | { 131 | if(_subtype) return false; 132 | return _evi_type->_is_signed; 133 | } 134 | 135 | bool ParsedType::is_constant() 136 | { 137 | return _is_constant; 138 | } 139 | 140 | 141 | bool ParsedType::is_invalid() 142 | { 143 | #pragma clang diagnostic push 144 | #pragma clang diagnostic ignored "-Wundefined-bool-conversion" 145 | #pragma clang diagnostic ignored "-Wtautological-undefined-compare" 146 | return !this || _invalid; 147 | #pragma clang diagnostic pop 148 | } 149 | 150 | 151 | bool EviType::eq(EviType* rhs) 152 | { 153 | ASSERT_OR_THROW_INTERNAL_ERROR(rhs, "in type comparison"); 154 | // name is enough 155 | return _name == rhs->_name; 156 | } 157 | 158 | 159 | ccp lexical_type_strings[TYPE_NONE]; 160 | 161 | map __evi_types; 162 | llvm::LLVMContext __context; -------------------------------------------------------------------------------- /stdlib/Makefile: -------------------------------------------------------------------------------- 1 | STDLIB_CC = clang 2 | AR = ar 3 | ARFLAGS = -rc 4 | 5 | STDLIB_CXXFLAGS = -D COMPILER=\"$(STDLIB_CC)\" -D LLVM_VERSION=$(LLVM_VERSION) 6 | 7 | STDLIB_SRCDIR = $(STDLIB_SRC_DIR)/src 8 | STDLIB_EXT = .evi.c 9 | STDLIB_OBJDIR = $(BINDIR)/obj/stdlib 10 | STDLIB_LIB = $(BINDIR)/libevi.a 11 | 12 | STDLIB_SRC = $(wildcard $(STDLIB_SRCDIR)/**/*$(STDLIB_EXT)) 13 | STDLIB_OBJ = $(STDLIB_SRC:$(STDLIB_SRCDIR)/%$(STDLIB_EXT)=$(STDLIB_OBJDIR)/%.o) 14 | 15 | STDLIB_OBJCOUNT_NOPAD = $(shell v=`echo $(STDLIB_OBJ) | wc -w`; echo `seq 1 $$(expr $$v)`) 16 | STDLIB_OBJCOUNT = $(foreach v,$(STDLIB_OBJCOUNT_NOPAD),$(shell printf '%02d' $(v))) 17 | 18 | .PRECIOUS: $(STDLIB_OBJ) 19 | SHELL := /bin/bash 20 | 21 | stdlib: $(STDLIB_OBJ) | makedirs 22 | @$(AR) $(ARFLAGS) -c $(STDLIB_LIB) $(STDLIB_OBJ) 23 | 24 | $(STDLIB_OBJDIR)/%.o: $(STDLIB_SRCDIR)/%$(STDLIB_EXT) | makedirs 25 | @mkdir -p $(dir $@) 26 | @printf "[$(word 1,$(STDLIB_OBJCOUNT))/$(words $(STDLIB_OBJ))] compiling $(notdir $<) into $(notdir $@)..." 27 | @$(STDLIB_CC) $(STDLIB_CXXFLAGS) -I$(STDLIB_SRCDIR) -o $@ -c $< -fmodule-name=$($<:$(STDLIB_EXT)=.evi) 28 | @printf "\b\b done!\n" 29 | $(eval STDLIB_OBJCOUNT = $(filter-out $(word 1,$(STDLIB_OBJCOUNT)),$(STDLIB_OBJCOUNT))) 30 | @([ "$(word $(words $(STDLIB_OBJ)), $(STDLIB_OBJ))" == "$@" ] && echo "[stdlib] standard library compiled!") || true 31 | 32 | .PHONY: clean 33 | clean: 34 | @rm -rf $(STDLIB_OBJDIR) 35 | 36 | .PHONY: makedirs 37 | makedirs: 38 | @mkdir -p $(STDLIB_OBJDIR) -------------------------------------------------------------------------------- /stdlib/headers/std/__std_header_defs.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/__std_header_defs" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #ifnset __STDLIB_HEADER__ 29 | 30 | \ mark file as stdlib header 31 | #flag __STDLIB_HEADER__ 32 | 33 | \ mark compiler type 34 | #flag __OFFICIAL_EVI_COMPILER__ 35 | 36 | \ ==================================== Macros ====================================== 37 | 38 | \ compiler information (should be built-in) 39 | #info region 40 | 41 | #ifndef __COMPILER_NAME__ 42 | \ comment line below to mute warning 43 | #info warning "macro __COMPILER_NAME__ in stdlib header not defined by compiler!" 44 | #macro __COMPILER_NAME__ "unknown" 45 | #endif 46 | 47 | #ifndef __COMPILER_VERSION__ 48 | \ comment line below to mute warning 49 | #info warning "macro __COMPILER_VERSION__ in stdlib header not defined by compiler!" 50 | #macro __COMPILER_VERSION__ "unknown" 51 | #endif 52 | 53 | #ifndef __COMPILER_BUILD_TARGET__ 54 | \ comment line below to mute warning 55 | #info warning "macro __COMPILER_BUILD_TARGET__ in stdlib header not defined by compiler!" 56 | #macro __COMPILER_BUILD_TARGET__ "unkown" 57 | #endif 58 | 59 | #ifndef __COMPILER_BUILD_DATE__ 60 | \ comment line below to mute warning 61 | #info warning "macro __COMPILER_BUILD_DATE__ in stdlib header not defined by compiler!" 62 | #macro __COMPILER_BUILD_DATE__ "unknown" 63 | #endif 64 | 65 | #ifndef __COMPILER_BUILD_TIME__ 66 | \ comment line below to mute warning 67 | #info warning "macro __COMPILER_BUILD_TIME__ in stdlib header not defined by compiler!" 68 | #macro __COMPILER_BUILD_TIME__ "unknown" 69 | #endif 70 | 71 | #info endregion 72 | 73 | \ operating system 74 | #ifndef __OPERATING_SYSTEM__ 75 | \ comment line below to mute warning 76 | #info warning "macro __OPERATING_SYSTEM__ in stdlib header not defined by compiler!" 77 | #macro __OPERATING_SYSTEM__ "unkown" 78 | #endif 79 | 80 | \ standard macros 81 | #info region 82 | 83 | #macro NULL 0 84 | 85 | #macro EXIT_SUCCESS 0 86 | #macro EXIT_FAILURE 0 87 | 88 | #info endregion 89 | 90 | #endif \ __STDLIB_HEADER__ -------------------------------------------------------------------------------- /stdlib/headers/std/all.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/all" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #info apply_once 29 | #flag __STD_ALL__ 30 | 31 | #apply "std/__std_header_defs" 32 | 33 | #info warning "Applying header \"std/all\" will apply all headers in the standard library and this is not reccommended!" 34 | #info note "Try applying only the required headers for cleaner and more efficient code." 35 | 36 | #apply "std/io" 37 | #apply "std/math" 38 | #apply "std/mem" 39 | #apply "std/str" -------------------------------------------------------------------------------- /stdlib/headers/std/io.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/io" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #info apply_once 29 | #flag __STD_IO__ 30 | 31 | #apply "std/__std_header_defs" 32 | 33 | \ ===================================== Types ====================================== 34 | 35 | \ typedef FILE* type here? 36 | 37 | \ =================================== Constants ==================================== 38 | 39 | \ %stderr FILE* ???; 40 | \ %stdin FILE* ???; 41 | \ %stdout FILE* ???; 42 | 43 | \ ==================================== Macros ====================================== 44 | 45 | #macro EOF (-1) 46 | 47 | #macro SEEK_CUR 1 48 | 49 | #macro SEEK_END 2 50 | 51 | #macro SEEK_SET 0 52 | 53 | \ =================================== Functions ==================================== 54 | 55 | \ \? Closes the stream. All buffers are flushed. 56 | \ @fclose i32 (FILE*); 57 | 58 | \ \? Clears the end-of-file and error indicators for the given stream. 59 | \ @clearerr nll (FILE*); 60 | 61 | \ \? Tests the end-of-file indicator for the given stream. 62 | \ @feof i32 (FILE*); 63 | 64 | \ \? Tests the error indicator for the given stream. 65 | \ @ferror i32 (FILE*); 66 | 67 | \ \? Flushes the output buffer of a stream. 68 | \ @fflush i32 (FILE*); 69 | 70 | \ \? Gets the current file position of the stream and writes it to pos. 71 | \ @fgetpos i32 (FILE* fpos_t*); 72 | 73 | \ \? Opens the filename pointed to by filename using the given mode. 74 | \ @fopen FILE* (!chr* !chr*); 75 | 76 | \ \? Reads data from the given stream into the array pointed to by ptr. 77 | \ @fread sze (nll* sze sze FILE*); 78 | 79 | \ \? Associates a new filename with the given open stream and same time closing the old file in stream. 80 | \ @freopen FILE* (!chr* !chr* FILE*); 81 | 82 | \ \? Sets the file position of the stream to the given offset. The argument offset signifies the number of bytes to seek from the given whence position. 83 | \ @fseek i32 (FILE* long i32 i32); 84 | 85 | \ \? Sets the file position of the given stream to the given position. The argument pos is a position given by the function fgetpos. 86 | \ @fsetpos i32 (FILE* !fpos_t*); 87 | 88 | \ \? Returns the current file position of the given stream. 89 | \ @ftell long i32 (FILE*); 90 | 91 | \ \? Writes data from the array pointed to by ptr to the given stream. 92 | \ @fwrite sze (!nll* sze sze FILE*); 93 | 94 | \? Deletes the given filename so that it is no longer accessible. 95 | \? @param 0 the name of the file to be deleted 96 | \? @return zero on success and -1 on error (errno is set appropriately) 97 | @remove i32 (!chr*); 98 | 99 | \? Causes the filename referred to by old_filename to be changed to new_filename. 100 | \? @param 0 the name of the file to be renamed and/or moved 101 | \? @param 1 the new name for the file 102 | \? @return zero on success and -1 on error (errno is set appropriately) 103 | @rename i32 (!chr* !chr*); 104 | 105 | \ \? Sets the file position to the beginning of the file of the given stream. 106 | \ @rewind nll (FILE*); 107 | 108 | \ \? Defines how a stream should be buffered. 109 | \ @setbuf nll (FILE* chr*); 110 | 111 | \ \? Another function to define how a stream should be buffered. 112 | \ @setvbuf i32 (FILE* chr* i32 mode sze size); 113 | 114 | \ \? Creates a temporary file in binary update mode (wb+). 115 | \ @tmpfile FILE* (nll); 116 | 117 | \? Generates and returns a valid temporary filename which does not exist. 118 | \? Return value is a pointer to the C string containing the proposed name 119 | \? for a temporary file. If str was a null pointer, this points to an internal 120 | \? buffer that will be overwritten the next time this function is called. 121 | \? If str was not a null pointer, str is returned. If the function fails to 122 | \? create a suitable filename, it returns a null pointer 123 | \? @param 0 char pointer where the proposed tempname will be stored 124 | \? @return see function documentation 125 | @tmpnam chr* (chr*); 126 | 127 | \ \? Sends formatted output to a stream. 128 | \ @fprintf i32 (FILE* !chr* ...); 129 | 130 | \? Sends formatted output to stdout. 131 | \? @param 0 the format/string to print 132 | \? @param 1 optional replacement values for the format tags 133 | \? @return the amount of printed characteres on success, otherwise a negative number 134 | @printf i32 (!chr* ...); 135 | 136 | \? Sends formatted output to a string. 137 | \? @param 0 the pointer where the resulting string will be stored 138 | \? @param 1 the format/string to write to the buffer 139 | \? @param 2 optional replacement values for the format tags 140 | \? @return the amount of written characteres on success, otherwise a negative number 141 | @sprintf i32 (chr* !chr* ...); 142 | 143 | \ \? Sends formatted output to a stream using an argument list. 144 | \ @vfprintf i32 (FILE* !chr* va_list); 145 | 146 | \ \? Sends formatted output to stdout using an argument list. 147 | \ @vprintf i32 (!chr* va_list); 148 | 149 | \ \? Sends formatted output to a string using an argument list. 150 | \ @vsprintf i32 (chr* !chr* va_list); 151 | 152 | \ \? Reads formatted input from a stream. 153 | \ @fscanf i32 (FILE* !chr* ...); 154 | 155 | \? Reads formatted input from stdin. 156 | \? @param 0 the format for the input 157 | \? @param 1 the destinations for each inputted value 158 | \? @return the amount of scanned values on success, otherwise EOF 159 | @scanf i32 (!chr* ...); 160 | 161 | \? Reads formatted input from a string. 162 | \? @param 0 the string that will be used as input 163 | \? @param 1 the format for the input 164 | \? @param 2 the destinations for each inputted value 165 | \? @return the amount of scanned values on success, otherwise EOF 166 | @sscanf i32 (!chr* !chr* ...); 167 | 168 | \ \? Gets the next character from a stream and advances the position indicator for the stream. 169 | \ @fgetc i32 (FILE*); 170 | 171 | \ \? Reads a line from a stream and stores it into the string pointed to by str. It stops when either (n-1) characters are read the newline character is read or the end-of-file is reached whichever comes first. 172 | \ @*fgets chr (chr* i32 FILE*); 173 | 174 | \ \? Writes a to a stream and advances the position indicator for the stream. 175 | \ @fputc i32 (i32 chr FILE*); 176 | 177 | \ \? Writes a string to a stream up to but not including the null character. 178 | \ @fputs i32 (!chr* FILE*); 179 | 180 | \ \? Gets the next character from a stream and advances the position indicator for the stream. 181 | \ @getc i32 (FILE*); 182 | 183 | \? Gets a character from stdin. 184 | \? @return the character read as an unsigned char cast to an int or EOF on end of file or error 185 | @getchar i32 (); 186 | 187 | \? Reads a line from stdin and stores it into the string pointed to by str. It stops when either the newline character is read or when the end-of-file is reached whichever comes first. 188 | \? @param 0 char pointer where the read line will be stored 189 | \? @return returns the given char pointer on success, and NULL on error 190 | @gets chr* (chr*); 191 | 192 | \ \? Writes a character to a stream and advances the position indicator for the stream. 193 | \ @putc i32 (i32 chr FILE*); 194 | 195 | \? Writes a character to stdout. 196 | \? @param 0 the character to be written 197 | \? @return the character casted to an int on success, or EOF on error 198 | @putchar i32 (i32); 199 | 200 | \? Writes a string to stdout up to but not including the null character. A newline character is appended to the output. 201 | \? @param 0 the string to be written 202 | \? @return a positive number on success, or EOF on error 203 | @puts i32 (!chr*); 204 | 205 | \ \? Pushes the character chr onto a stream so that the next character is read. 206 | \ @ungetc i32 (i32 chr FILE*); 207 | 208 | \? Prints a descriptive error message to stderr. First the string is printed followed by a colon and then a space. 209 | \? @param 0 the message to print before the error itself 210 | @perror nll (!chr*); -------------------------------------------------------------------------------- /stdlib/headers/std/math.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/math" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #info apply_once 29 | #flag __STD_MATH__ 30 | 31 | #apply "std/__std_header_defs" 32 | 33 | \ =================================== Constants ==================================== 34 | 35 | #macro NAN (0.0 / 0.0) 36 | #macro RAND_MAX 2147483647 37 | 38 | \? e 39 | #macro M_E 2.718281828459045235360287471352662498 40 | 41 | \? log_2 e 42 | #macro M_LOG2E 1.442695040888963407359924681001892137 43 | 44 | \? log_10 e 45 | #macro M_LOG10E 0.434294481903251827651128918916605082 46 | 47 | \? log_e 2 48 | #macro M_LN2 0.693147180559945309417232121458176568 49 | 50 | \? log_e 10 51 | #macro M_LN10 2.302585092994045684017991454684364208 52 | 53 | \? pi 54 | #macro M_PI 3.141592653589793238462643383279502884 55 | 56 | \? pi/2 57 | #macro M_PI_2 1.570796326794896619231321691639751442 58 | 59 | \? pi/4 60 | #macro M_PI_4 0.785398163397448309615660845819875721 61 | 62 | \? 1/pi 63 | #macro M_1_PI 0.318309886183790671537767526745028724 64 | 65 | \? 2/pi 66 | #macro M_2_PI 0.636619772367581343075535053490057448 67 | 68 | \? 2/sqrt(pi) 69 | #macro M_2_SQRTPI 1.128379167095512573896158903121545172 70 | 71 | \? sqrt(2) 72 | #macro M_SQRT2 1.414213562373095048801688724209698079 73 | 74 | \? 1/sqrt(2) 75 | #macro M_SQRT1_2 0.707106781186547524400844362104849039 76 | 77 | \ =================================== Functions ==================================== 78 | 79 | \? Returns the absolute of x. 80 | \? @param 0 x 81 | @abs i32 (i32); 82 | 83 | \? Returns the arc cosine of x in radians. 84 | \? @param 0 x 85 | @acos dbl (dbl); 86 | 87 | \? Returns the arc sine of x in radians. 88 | \? @param 0 x 89 | @asin dbl (dbl); 90 | 91 | \? Returns the arc tangent of x in radians. 92 | \? @param 0 x 93 | @atan dbl (dbl); 94 | 95 | \? Returns the arc tangent in radians of y/x based on the 96 | \? signs of both values to determine the correct quadrant. 97 | \? @param 0 x 98 | \? @param 1 y 99 | @atan2 dbl (dbl dbl); 100 | 101 | \? Returns the cosine of a radian angle x. 102 | \? @param 0 x 103 | @cos dbl (dbl); 104 | 105 | \? Returns the hyperbolic cosine of x. 106 | \? @param 0 x 107 | @cosh dbl (dbl); 108 | 109 | \? Returns the sine of a radian angle x. 110 | \? @param 0 x 111 | @sin dbl (dbl); 112 | 113 | \? Returns the hyperbolic sine of x. 114 | \? @param 0 x 115 | @sinh dbl (dbl); 116 | 117 | \? Returns the hyperbolic tangent of x. 118 | \? @param 0 x 119 | @tanh dbl (dbl); 120 | 121 | \? Returns the value of e raised to the xth power. 122 | \? @param 0 x 123 | @exp dbl (dbl); 124 | 125 | \? The returned value is the mantissa and the integer pointed to by exponent 126 | \? is the exponent. The resultant value is x = mantissa * 2 ^ exponent. 127 | \? @param 0 x 128 | \? @param 1 exponent 129 | @frexp dbl (dbl i32*); 130 | 131 | \? Returns x multiplied by 2 raised to the power of exponent. 132 | \? @param 0 x 133 | \? @param 1 exponent 134 | @ldexp dbl (dbl i32); 135 | 136 | \? Returns the natural logarithm (base-e logarithm) of x. 137 | \? @param 0 x 138 | @log dbl (dbl); 139 | 140 | \? Returns the common logarithm (base-10 logarithm) of x. 141 | \? @param 0 x 142 | @log10 dbl (dbl); 143 | 144 | \? The returned value is the fraction component (part after the decimal), 145 | \? and sets integer to the integer component. 146 | \? @param 0 x 147 | \? @param 1 integer 148 | @modf dbl (dbl dbl*); 149 | 150 | \? Returns x raised to the power of y. 151 | \? @param 0 x 152 | \? @param 1 y 153 | @pow dbl (dbl dbl); 154 | 155 | \? Returns a pseudo-random number in the range of 0 to RAND_MAX. 156 | @rand i32 (); 157 | 158 | \? Seeds the random number generator used by the function rand. 159 | \? @param 0 the seed 160 | @srand nll (ui32); 161 | 162 | \? Returns the square root of x. 163 | \? @param 0 x 164 | @sqrt dbl (dbl); 165 | 166 | \? Returns the smallest integer value greater than or equal to x. 167 | \? @param 0 x 168 | @ceil dbl (dbl); 169 | 170 | \? Returns the absolute value of x. 171 | \? @param 0 x 172 | @fabs dbl (dbl); 173 | 174 | \? Returns the largest integer value less than or equal to x. 175 | \? @param 0 x 176 | @floor dbl (dbl); 177 | 178 | \? Returns the remainder of x divided by y. 179 | \? @param 0 x 180 | \? @param 1 y 181 | @fmod dbl (dbl dbl); -------------------------------------------------------------------------------- /stdlib/headers/std/mem.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/mem" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #info apply_once 29 | #flag __STD_MEM__ 30 | 31 | #apply "std/__std_header_defs" 32 | 33 | \ =================================== Functions ==================================== 34 | 35 | \? Allocates a block of n bytes that will be freed when the calling function exists. 36 | \? @param 0 the size in bytes 37 | \? @return the allocated block 38 | @alloca nll* (sze); 39 | 40 | \? Allocates an array of n elements each of which will be n bytes in size. 41 | \? @param 0 the amount of elements 42 | \? @param 1 the size in bytes of each element 43 | \? @return the allocated array 44 | @calloc nll* (i32 i32); 45 | 46 | \? Allocates an array of n bytes and returns them uninitialized. 47 | \? @param 0 the size in bytes of the array 48 | \? @return the allocated array 49 | @malloc nll* (sze); 50 | 51 | \? Re-allocates memory extending it to a given size. 52 | \? @param 0 the address to re-allocate 53 | \? @param 1 the new size in bytes 54 | \? @return a pointer to the address, or a null pointer if reallocation failed 55 | @realloc nll* (nll* i32); 56 | 57 | \? Releases a block of memory block specified by address. 58 | \? @param 0 the address to free 59 | @free nll (nll*); -------------------------------------------------------------------------------- /stdlib/headers/std/str.hevi: -------------------------------------------------------------------------------- 1 | \: 2 | Evi standard library header "std/str" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | :\ 27 | 28 | #info apply_once 29 | #flag __STD_STR__ 30 | 31 | #apply "std/__std_header_defs" 32 | 33 | \ ==================================== Macros ====================================== 34 | 35 | 36 | 37 | \ =================================== Functions ==================================== 38 | 39 | \? Searches for the first occurrence of the character c in the first n bytes of the string pointed to, by the argument str. 40 | @memchr nll* (!nll* i32 sze); 41 | 42 | \? Compares the first n bytes of str1 and str2. 43 | @memcmp i32 (!nll* !nll* sze); 44 | 45 | \? Copies n characters from src to dest. 46 | @memcpy nll* (nll* !nll* sze); 47 | 48 | \? Another function to copy n characters from str2 to str1. 49 | @memmove nll* (nll* !nll* sze); 50 | 51 | \? Copies the character c to the first n characters of the string pointed to, by the argument str. 52 | @memset nll* (nll* i32 sze); 53 | 54 | \? Appends the string pointed to, by src to the end of the string pointed to by dest. 55 | @strcat chr* (chr* !chr*); 56 | 57 | \? Appends the string pointed to, by src to the end of the string pointed to, by dest up to n characters long. 58 | @strncat chr* (chr* !chr* sze); 59 | 60 | \? Searches for the first occurrence of the character c (an unsigned char) in the string pointed to, by the argument str. 61 | @strchr chr* (!chr* i32); 62 | 63 | \? Compares the string pointed to, by str1 to the string pointed to by str2. 64 | @strcmp i32 (!chr* !chr*); 65 | 66 | \? Compares at most the first n bytes of str1 and str2. 67 | @strncmp i32 (!chr* !chr* sze); 68 | 69 | \? Compares string str1 to str2. The result is dependent on the LC_COLLATE setting of the location. 70 | @strcoll i32 (!chr* !chr*); 71 | 72 | \? Copies the string pointed to, by src to dest. 73 | @strcpy chr* (chr* !chr*); 74 | 75 | \? Copies up to n characters from the string pointed to, by src to dest. 76 | @strncpy chr* (chr* !chr* sze); 77 | 78 | \? Calculates the length of the initial segment of str1 which consists entirely of characters not in str2. 79 | @strcspn sze (!chr* !chr*); 80 | 81 | \? Searches an internal array for the error number errnum and returns a pointer to an error message string. 82 | @strerror chr* (i32); 83 | 84 | \? Computes the length of the string str up to but not including the terminating null character. 85 | @strlen sze (!chr*); 86 | 87 | \? Finds the first character in the string str1 that matches any character specified in str2. 88 | @strpbrk chr* (!chr* !chr*); 89 | 90 | \? Searches for the last occurrence of the character c (an unsigned char) in the string pointed to by the argument str. 91 | @strrchr chr* (!chr* i32); 92 | 93 | \? Calculates the length of the initial segment of str1 which consists entirely of characters in str2. 94 | @strspn sze (!chr* !chr*); 95 | 96 | \? Finds the first occurrence of the entire string needle (not including the terminating null character) which appears in the string haystack. 97 | @strstr chr* (!chr* !chr*); 98 | 99 | \? Breaks string str into a series of tokens separated by delim. 100 | @strtok chr* (chr* !chr*); 101 | 102 | \ Transforms the first n characters of the string src into current locale and places them in the string dest. 103 | @strxfrm sze (chr* !chr* sze); -------------------------------------------------------------------------------- /stdlib/src/std/__std_header_defs.hevi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Evi standard library header "std/__std_header_defs" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | // ============= C version of Evi's types ============= 29 | 30 | typedef /*signed*/ _Bool evi_i1_t; // i1 31 | 32 | typedef signed char /*?*/ evi_i4_t; // i4 33 | typedef unsigned char /*?*/ evi_ui4_t; // ui4 34 | 35 | typedef signed char evi_i8_t; // i8 36 | typedef unsigned char evi_ui8_t; // ui8 37 | 38 | typedef signed short int evi_i16_t; // i16 39 | typedef unsigned short int evi_ui16_t; // ui16 40 | 41 | typedef signed int evi_i32_t; // i32 42 | typedef unsigned int evi_ui32_t; // ui32 43 | 44 | typedef signed long int evi_i64_t; // i64 45 | typedef unsigned long int evi_ui64_t; // ui64 46 | 47 | typedef __int128_t evi_i128_t; // i128 48 | typedef __uint128_t evi_ui128_t; // ui128 49 | 50 | typedef float evi_flt_t; // flt 51 | typedef double evi_dbl_t; // dbl 52 | 53 | typedef unsigned long evi_sze_t; // sze 54 | typedef /*unsigned*/ _Bool evi_bln_t; // bln 55 | typedef char evi_chr_t; // chr 56 | typedef void evi_nll_t; // nll 57 | 58 | // ==================================================== -------------------------------------------------------------------------------- /stdlib/src/std/io.evi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Evi standard library header "std/io" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | #include "__std_header_defs.hevi.h" 29 | 30 | // defined in c's libm: 31 | 32 | // extern evi_i32_t fclose(FILE *stream); 33 | 34 | // extern evi_nll_t clearerr(FILE *stream); 35 | 36 | // extern evi_i32_t feof(FILE *stream); 37 | 38 | // extern evi_i32_t ferror(FILE *stream); 39 | 40 | // extern evi_i32_t fflush(FILE *stream); 41 | 42 | // extern evi_i32_t fgetpos(FILE *stream, fpos_t* pos); 43 | 44 | // extern FILE *fopen(const evi_chr_t* filename, const evi_chr_t* mode); 45 | 46 | // extern size_t fread(evi_nll_t* ptr, size_t size, size_t nmemb, FILE *stream); 47 | 48 | // extern FILE *freopen(const evi_chr_t* filename, const evi_chr_t* mode, FILE *stream); 49 | 50 | // extern evi_i32_t fseek(FILE *stream, long evi_i32_t offset, evi_i32_t whence); 51 | 52 | // extern evi_i32_t fsetpos(FILE *stream, const fpos_t* pos); 53 | 54 | // extern long evi_i32_t ftell(FILE *stream); 55 | 56 | // extern size_t fwrite(const evi_nll_t* ptr, size_t size, size_t nmemb, FILE *stream); 57 | 58 | extern evi_i32_t remove(const evi_chr_t* filename); 59 | 60 | extern evi_i32_t rename(const evi_chr_t* old_filename, const evi_chr_t* new_filename); 61 | 62 | // extern evi_nll_t rewind(FILE *stream); 63 | 64 | // extern evi_nll_t setbuf(FILE *stream, evi_chr_t* buffer); 65 | 66 | // extern evi_i32_t setvbuf(FILE *stream, evi_chr_t* buffer, evi_i32_t mode, size_t size); 67 | 68 | // extern FILE *tmpfile(); 69 | 70 | extern evi_chr_t* tmpnam(evi_chr_t* str); 71 | 72 | // extern evi_i32_t fprevi_i32_tf(FILE *stream, const evi_chr_t* format, ...); 73 | 74 | extern evi_i32_t printf(const evi_chr_t* format, ...); 75 | 76 | extern evi_i32_t sprintf(evi_chr_t* str, const evi_chr_t* format, ...); 77 | 78 | // extern evi_i32_t vfprevi_i32_tf(FILE *stream, const evi_chr_t* format, va_list arg); 79 | 80 | // extern evi_i32_t vprevi_i32_tf(const evi_chr_t* format, va_list arg); 81 | 82 | // extern evi_i32_t vsprevi_i32_tf(evi_chr_t* str, const evi_chr_t* format, va_list arg); 83 | 84 | // extern evi_i32_t fscanf(FILE *stream, const evi_chr_t* format, ...); 85 | 86 | extern evi_i32_t scanf(const evi_chr_t* format, ...); 87 | 88 | extern evi_i32_t sscanf(const evi_chr_t* str, const evi_chr_t* format, ...); 89 | 90 | // extern evi_i32_t fgetc(FILE *stream); 91 | 92 | // extern evi_chr_t* fgets(evi_chr_t* str, evi_i32_t n, FILE *stream); 93 | 94 | // extern evi_i32_t fputc(evi_i32_t evi_chr_t, FILE *stream); 95 | 96 | // extern evi_i32_t fputs(const evi_chr_t* str, FILE *stream); 97 | 98 | // extern evi_i32_t getc(FILE *stream); 99 | 100 | extern evi_i32_t getchar(); 101 | 102 | extern evi_chr_t* gets(evi_chr_t* str); 103 | 104 | // extern evi_i32_t putc(evi_i32_t evi_chr_t, FILE *stream); 105 | 106 | extern evi_i32_t putchar(evi_i32_t evi_chr_t); 107 | 108 | extern evi_i32_t puts(const evi_chr_t* str); 109 | 110 | // extern evi_i32_t ungetc(evi_i32_t evi_chr_t, FILE *stream); 111 | 112 | extern evi_nll_t perror(const evi_chr_t* str); 113 | -------------------------------------------------------------------------------- /stdlib/src/std/math.evi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Evi standard library header "std/math" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | #include "__std_header_defs.hevi.h" 29 | 30 | // defined in c's libm: 31 | 32 | extern evi_i32_t abs(evi_i32_t); 33 | 34 | extern evi_dbl_t acos(evi_dbl_t); 35 | 36 | extern evi_dbl_t asin(evi_dbl_t); 37 | 38 | extern evi_dbl_t atan(evi_dbl_t); 39 | 40 | extern evi_dbl_t atan2(evi_dbl_t, evi_dbl_t); 41 | 42 | extern evi_dbl_t cos(evi_dbl_t); 43 | 44 | extern evi_dbl_t cosh(evi_dbl_t); 45 | 46 | // extern div_t div(int numer, int denom); 47 | 48 | extern evi_dbl_t sin(evi_dbl_t); 49 | 50 | extern evi_dbl_t sinh(evi_dbl_t); 51 | 52 | extern evi_dbl_t tanh(evi_dbl_t); 53 | 54 | extern evi_dbl_t exp(evi_dbl_t); 55 | 56 | extern evi_dbl_t frexp(evi_dbl_t, evi_i32_t*); 57 | 58 | extern evi_dbl_t ldexp(evi_dbl_t, evi_i32_t); 59 | 60 | extern evi_dbl_t log(evi_dbl_t); 61 | 62 | extern evi_dbl_t log10(evi_dbl_t); 63 | 64 | extern evi_dbl_t modf(evi_dbl_t, evi_dbl_t*); 65 | 66 | extern evi_dbl_t pow(evi_dbl_t, evi_dbl_t); 67 | 68 | extern evi_i32_t rand(); 69 | 70 | extern evi_nll_t srand(evi_ui32_t); 71 | 72 | extern evi_dbl_t sqrt(evi_dbl_t); 73 | 74 | extern evi_dbl_t ceil(evi_dbl_t); 75 | 76 | extern evi_dbl_t fabs(evi_dbl_t); 77 | 78 | extern evi_dbl_t floor(evi_dbl_t); 79 | 80 | extern evi_dbl_t fmod(evi_dbl_t, evi_dbl_t); -------------------------------------------------------------------------------- /stdlib/src/std/mem.evi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Evi standard library header "std/mem" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | #include "__std_header_defs.hevi.h" 29 | 30 | // defined in c's libc: 31 | 32 | extern evi_nll_t* alloca(evi_sze_t s); 33 | 34 | extern evi_nll_t* malloc(evi_sze_t s); 35 | 36 | extern evi_nll_t* calloc(evi_sze_t n, evi_sze_t s); 37 | 38 | extern evi_nll_t* malloc(evi_sze_t s); 39 | 40 | extern evi_nll_t* realloc(evi_nll_t* a, evi_sze_t s); 41 | 42 | extern evi_nll_t free(evi_nll_t* a); -------------------------------------------------------------------------------- /stdlib/src/std/str.evi.c: -------------------------------------------------------------------------------- 1 | /* 2 | Implementation of Evi standard library header "std/str" 3 | Written by Sjoerd Vermeulen (2022) 4 | 5 | MIT License 6 | 7 | Copyright (c) 2022 Sjoerd Vermeulen 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | SOFTWARE. 26 | */ 27 | 28 | #include "__std_header_defs.hevi.h" 29 | 30 | // defined in c's libc: 31 | 32 | extern evi_nll_t* memchr(const evi_nll_t*, evi_i32_t, evi_sze_t); 33 | 34 | extern evi_i32_t memcmp(const evi_nll_t*, const evi_nll_t*, evi_sze_t); 35 | 36 | extern evi_nll_t* memcpy(evi_nll_t*, const evi_nll_t*, evi_sze_t); 37 | 38 | extern evi_nll_t* memmove(evi_nll_t*, const evi_nll_t*, evi_sze_t); 39 | 40 | extern evi_nll_t* memset(evi_nll_t*, evi_i32_t, evi_sze_t); 41 | 42 | extern evi_chr_t* strcat(evi_chr_t*, const evi_chr_t*); 43 | 44 | extern evi_chr_t* strncat(evi_chr_t*, const evi_chr_t*, evi_sze_t); 45 | 46 | extern evi_chr_t* strchr(const evi_chr_t*, evi_i32_t); 47 | 48 | extern evi_i32_t strcmp(const evi_chr_t*, const evi_chr_t*); 49 | 50 | extern evi_i32_t strncmp(const evi_chr_t*, const evi_chr_t*, evi_sze_t); 51 | 52 | extern evi_i32_t strcoll(const evi_chr_t*, const evi_chr_t*); 53 | 54 | extern evi_chr_t* strcpy(evi_chr_t*, const evi_chr_t*); 55 | 56 | extern evi_chr_t* strncpy(evi_chr_t*, const evi_chr_t*, evi_sze_t); 57 | 58 | extern evi_sze_t strcspn(const evi_chr_t*, const evi_chr_t*); 59 | 60 | extern evi_chr_t* strerror(evi_i32_t); 61 | 62 | extern evi_sze_t strlen(const evi_chr_t*); 63 | 64 | extern evi_chr_t* strpbrk(const evi_chr_t*, const evi_chr_t*); 65 | 66 | extern evi_chr_t* strrchr(const evi_chr_t*, evi_i32_t); 67 | 68 | extern evi_sze_t strspn(const evi_chr_t*, const evi_chr_t*); 69 | 70 | extern evi_chr_t* strstr(const evi_chr_t*, const evi_chr_t*); 71 | 72 | extern evi_chr_t* strtok(evi_chr_t*, const evi_chr_t*); 73 | 74 | extern evi_sze_t strxfrm(evi_chr_t*, const evi_chr_t*, evi_sze_t); -------------------------------------------------------------------------------- /test/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "evi.enableLanguageFeatures": false, 3 | "evi.logDebugInfo": true, 4 | // "codesnap.showWindowControls": false, 5 | // "codesnap.containerPadding": "0em", 6 | // "codesnap.boxShadow": "rgba(0, 0, 0, 0) 0px 0px 0px", 7 | // "codesnap.transparentBackground": true, 8 | // "codesnap.roundedCorners": false, 9 | // "codesnap.showWindowTitle": false, 10 | // "codesnap.backgroundColor": "transparent", 11 | 12 | } -------------------------------------------------------------------------------- /test/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "type": "shell", 5 | "label": "replace macros", 6 | "command": "/usr/bin/make", 7 | "args": [ 8 | "replace-macros", 9 | ], 10 | "options": { 11 | "cwd": "${fileDirname}" 12 | } 13 | } 14 | ], 15 | "version": "2.0.0" 16 | } -------------------------------------------------------------------------------- /test/arrays_and_pointers.evi: -------------------------------------------------------------------------------- 1 | #apply "std/io" 2 | #apply "std/mem" 3 | 4 | \ Main function 5 | @main i32 () 6 | { 7 | \ A normal pointer 8 | %myfltptr flt*; 9 | 10 | \ Strings and arrays 11 | %mystring chr* "This is a string!"; 12 | %myarray i32* {1, 2, 3, 4}; 13 | 14 | \ Dereferencing and indexing 15 | printf("mystring: %s\n", $mystring); \ prints 'This is a string!' 16 | printf("mystring's first character: %c\n", $mystring[0]); \ prints 'T' 17 | printf("myarray's third index: %d\n", $myarray[2]); \ prints '3' 18 | 19 | ~ 0; 20 | } -------------------------------------------------------------------------------- /test/calclang/main.evi: -------------------------------------------------------------------------------- 1 | #apply "std/io" 2 | #apply "std/mem" 3 | #apply "std/str" 4 | 5 | #macro MAX_LINE_SIZE 100 6 | 7 | @main i32 () !!(;1;;) 8 | { 9 | %line chr* malloc(?(chr) * MAX_LINE_SIZE#) -> chr*; 10 | 11 | printf(">>> "); 12 | !!(;strlen($line) < MAX_LINE_SIZE# && $line[strlen($line) - 1] /= '\n';;) 13 | =line[strlen($line) - 1] getchar(); 14 | 15 | puts($line); 16 | } -------------------------------------------------------------------------------- /test/fib.evi: -------------------------------------------------------------------------------- 1 | #apply "std/io" 2 | 3 | @fib nll () { 4 | \ initialize first and second terms 5 | %t1, t2 i32 0, 1; 6 | 7 | \ initialize the next term (3rd term) 8 | %tn i32 $t1 + $t2; 9 | 10 | \ get no. of terms from user 11 | printf("Enter the number of terms: "); 12 | scanf("%d", &$n); 13 | 14 | \ print the first two terms t1 and t2 15 | printf("Fibonacci Series: %d, %d, ", $t1, $t2); 16 | 17 | \ print 3rd to nth terms 18 | !!(%i i32 3; $i <= $n; =i ++$i;) { 19 | printf("%d%s", $tn, ++$i <= $n ? ", " : ""); 20 | =t1 $t2; 21 | =t2 $tn; 22 | =tn $t1 + $t2; 23 | } 24 | printf("\n"); 25 | } 26 | 27 | @main i32 () fib(); 28 | -------------------------------------------------------------------------------- /test/hello_world.evi: -------------------------------------------------------------------------------- 1 | #apply "std/io" 2 | 3 | @main i32 () puts("Hello World!"); -------------------------------------------------------------------------------- /test/memory.evi: -------------------------------------------------------------------------------- 1 | #apply "std/mem" 2 | 3 | @main i32 (i32 chr**) 4 | { 5 | %x i32* malloc(?i32) -> i32*; 6 | 7 | =x[0] 123; 8 | 9 | free($x); 10 | } -------------------------------------------------------------------------------- /test/test.evi: -------------------------------------------------------------------------------- 1 | #apply "std/io" 2 | 3 | @main i32 (i32 !chr**) 4 | { 5 | %var flt 0.0; 6 | puts("Test"); 7 | } 8 | -------------------------------------------------------------------------------- /tools/debian-package/control: -------------------------------------------------------------------------------- 1 | Package: {{package-name}} 2 | Version: {{version}} 3 | Section: utils 4 | Priority: optional 5 | Architecture: {{architecture}} 6 | Essential: no 7 | Maintainer: Sjoerd Vermeulen {{email}} 8 | Source: {{website}} 9 | Homepage: {{website}} 10 | Depends: llvm-12 11 | Description: The Evi compiler 12 | More information at https://github.com/SjVer/Evi-Lang. 13 | -------------------------------------------------------------------------------- /tools/debian-package/control_replacings.py: -------------------------------------------------------------------------------- 1 | from re import findall 2 | from os import path 3 | 4 | root_dir = str() 5 | target = str() 6 | 7 | def package_name(): 8 | with open(path.join(root_dir, "include", "common.hpp"), "r") as f: 9 | return findall("#define APP_NAME \"(.+)\"", f.read())[0] 10 | 11 | def version(): 12 | with open(path.join(root_dir, "include", "common.hpp"), "r") as f: 13 | return findall("#define APP_VERSION \"(.+)\"", f.read())[0] 14 | 15 | def architecture(): 16 | return target 17 | 18 | def email(): 19 | with open(path.join(root_dir, "include", "common.hpp"), "r") as f: 20 | return findall("#define EMAIL \"(.+)\"", f.read())[0] 21 | 22 | def website(): 23 | with open(path.join(root_dir, "include", "common.hpp"), "r") as f: 24 | return findall("#define LINK \"(.+)\"", f.read())[0] 25 | 26 | 27 | funcs = { 28 | "package-name": package_name, 29 | "version": version, 30 | "architecture": architecture, 31 | "email": email, 32 | "website": website, 33 | } -------------------------------------------------------------------------------- /tools/debian-package/copied-files.txt: -------------------------------------------------------------------------------- 1 | ROOT_DIR|bin|evi-man.gz -> DEB_DIR|usr|share|man|man1|evi.1.gz 2 | ROOT_DIR|tools|evi-bash-completion.sh -> DEB_DIR|usr|share|bash-completion|completions|evi 3 | ROOT_DIR|stdlib|headers -> DEB_DIR|usr|lib|evi|stdlib 4 | ROOT_DIR|bin|libevi.a -> DEB_DIR|usr|lib|libevi.a 5 | ROOT_DIR|bin|evi -> DEB_DIR|usr|bin|evi -------------------------------------------------------------------------------- /tools/debian-package/generate-deb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # generates the debian package (NOT THE BINARY) 3 | 4 | from sys import argv 5 | from os import path 6 | import os, shutil 7 | import control_replacings 8 | 9 | SCRIPT_DIR = path.dirname(path.realpath(__file__)) 10 | CURR_DIR = os.getcwd() + os.sep 11 | ROOT_DIR = path.realpath(path.join(SCRIPT_DIR, "../..")) 12 | DEB_DIR = path.join(ROOT_DIR, "bin", "deb") 13 | COPIED_FILES_FILE = path.join(SCRIPT_DIR, "copied-files.txt") 14 | COPIED_FILES_SEP = " -> " 15 | CONTROL_FORMAT_FILE = path.join(SCRIPT_DIR, "control") 16 | 17 | # ============================ 18 | 19 | def clean(): 20 | if path.isdir(DEB_DIR): 21 | print(f"[generate-deb] Cleaning directory \"{DEB_DIR}\"".replace(CURR_DIR, "")) 22 | shutil.rmtree(DEB_DIR) 23 | 24 | def prepare_files(): 25 | os.makedirs(DEB_DIR) 26 | 27 | pairs = [] 28 | # extract pairs 29 | with open(COPIED_FILES_FILE, "r") as f: 30 | for pair in [l.strip() for l in f.readlines()]: 31 | pair = pair.replace("ROOT_DIR", ROOT_DIR) 32 | pair = pair.replace("DEB_DIR", DEB_DIR) 33 | pair = pair.replace("|", os.sep) 34 | pairs.append(pair.split(COPIED_FILES_SEP)) 35 | 36 | # do the copying 37 | print(f"[generate-deb] Copying files to \"{DEB_DIR}\"".replace(CURR_DIR, "")) 38 | for i in range(len(pairs)): 39 | src = pairs[i][0] 40 | dest = pairs[i][1] 41 | 42 | if not path.isdir(path.dirname(dest)): 43 | # print(f"[generate-deb] creating directory: {path.dirname(dest)}") 44 | os.makedirs(path.dirname(dest)) 45 | 46 | if path.isfile(src): 47 | print(f"[generate-deb] {i + 1}/{len(pairs)}: {src} -> {dest}".replace(CURR_DIR, "")) 48 | shutil.copy(src, dest) 49 | 50 | elif path.isdir(src): 51 | print(f"[generate-deb] {i + 1}/{len(pairs)}: {src} -> {dest} (dir)".replace(CURR_DIR, "")) 52 | shutil.copytree(src, dest) 53 | 54 | else: 55 | print(f"[generate-deb] Error: Could not copy file or directory \"{src}\"".replace(CURR_DIR, "")) 56 | exit(1) 57 | 58 | print(f"[generate-deb] Finished copying files.") 59 | 60 | def prepare_control(): 61 | os.makedirs(path.join(DEB_DIR, "DEBIAN")) 62 | controlfile = path.join(DEB_DIR, "DEBIAN", "control") 63 | print(f"[generate-deb] Formatting control file \"{controlfile}\"".replace(CURR_DIR, "")) 64 | 65 | with open(CONTROL_FORMAT_FILE, "r") as f: text = f.read() 66 | 67 | for fmt, func in control_replacings.funcs.items(): 68 | print(f"[generate-deb] \"{fmt}\" -> \"{func()}\"") 69 | text = text.replace(f"{{{{{fmt}}}}}", func()) 70 | 71 | with open(controlfile, 'w') as f: f.write(text) 72 | print("[generate-deb] Finished formatting.") 73 | 74 | def finish(): 75 | basename = f"{control_replacings.package_name()}_{control_replacings.version()}_{control_replacings.target}.deb" 76 | filename = path.join(argv[2], basename) 77 | print(f"[generate-deb] Generating \"{basename}\"...") 78 | 79 | if os.system(f"dpkg -b {DEB_DIR} {filename}") != 0: 80 | print(f"[generate-deb] Failed to generate deb package!") 81 | exit(1) 82 | 83 | print(f"[generate-deb] Done generating deb package at \"{filename}\".") 84 | 85 | def main(): 86 | if len(argv) != 3: 87 | print("Error: Expected target and output directory as only arguments.") 88 | exit(1) 89 | 90 | control_replacings.target = argv[1] 91 | control_replacings.root_dir = ROOT_DIR 92 | 93 | clean() 94 | 95 | prepare_files() 96 | prepare_control() 97 | 98 | finish() 99 | 100 | # ============================ 101 | 102 | if __name__ == '__main__': 103 | main() -------------------------------------------------------------------------------- /tools/evi-bash-completion.sh: -------------------------------------------------------------------------------- 1 | # evi(1) completion -*- shell-script -*- 2 | 3 | _evi_autocomplete() 4 | { 5 | local cur prev words cword split 6 | _init_completion -s || return 7 | 8 | case $prev in 9 | -h|--help|--usage|-V|--version) 10 | return 11 | ;; 12 | -l|--link|-o|--output) 13 | _filedir 14 | return 15 | ;; 16 | -i|--include) 17 | _filedir '@' 18 | return 19 | ;; 20 | esac 21 | 22 | $split && return 23 | 24 | if [[ $cur == -* ]]; then 25 | COMPREPLY=( $(compgen -W '$(_parse_help "$1" --help)' -- "$cur") ) 26 | [[ $COMPREPLY == *= ]] && compopt -o nospace 27 | return 28 | fi 29 | 30 | _filedir '@(evi|hevi|evii)' 31 | } && 32 | complete -F _evi_autocomplete evi 33 | -------------------------------------------------------------------------------- /tools/evi-man: -------------------------------------------------------------------------------- 1 | .TH "EVI" "1" "<<>>" "1" "Evi" 2 | 3 | .SH NAME 4 | .B evi 5 | - the official Evi compiler 6 | 7 | 8 | 9 | .SH SYNOPSIS 10 | .B evi 11 | .RB [OPTION...] 12 | .RB files... 13 | 14 | 15 | 16 | .SH DESCRIPTION 17 | .B evi 18 | is the official compiler for the Evi programming language. It is written in C++ by Sjoerd Vermeulen. 19 | 20 | 21 | 22 | 23 | 24 | .SH OPTIONS 25 | 26 | .INDENT 0.0 27 | .TP 28 | .B \-c, --compile-only 29 | Compile and assemble but do not link. 30 | .UNINDENT 31 | 32 | .INDENT 0.0 33 | .TP 34 | .B \--emit-llvm 35 | Emit llvm IR instead of an executable. 36 | .UNINDENT 37 | 38 | .INDENT 0.0 39 | .TP 40 | .B \--generate-ast 41 | Generate AST image (for debugging purposes). 42 | .UNINDENT 43 | 44 | .INDENT 0.0 45 | .TP 46 | .B \-i, --include=DIRECTORY 47 | Add DIRECTORY to include search path. 48 | .UNINDENT 49 | 50 | .INDENT 0.0 51 | .TP 52 | .B \-l, --link=FILE 53 | Link with FILE. 54 | .UNINDENT 55 | 56 | .INDENT 0.0 57 | .TP 58 | .B \-o, --output=OUTFILE 59 | Output to OUTFILE instead of to standard output. 60 | .UNINDENT 61 | 62 | .INDENT 0.0 63 | .TP 64 | .B \-O LEVEL 65 | Set the optimization level. (Levels: O0, O1, O2, O3, On, Os, Oz) 66 | .UNINDENT 67 | 68 | .INDENT 0.0 69 | .TP 70 | .B \-p, --preprocess-only 71 | Preprocess only but do not compile or link. 72 | .UNINDENT 73 | 74 | .INDENT 0.0 75 | .TP 76 | .B \--print-ld-flags 77 | Display the flags passed to the linker. 78 | .UNINDENT 79 | 80 | .INDENT 0.0 81 | .TP 82 | .B \--print-staticlib-dir 83 | Display the directory of the evi static library. 84 | .UNINDENT 85 | 86 | .INDENT 0.0 87 | .TP 88 | .B \--print-stdlib-dir 89 | Display the standard library header directory. 90 | .UNINDENT 91 | 92 | .INDENT 0.0 93 | .TP 94 | .B \--usage 95 | Display a usage information message. 96 | .UNINDENT 97 | 98 | .INDENT 0.0 99 | .TP 100 | .B \-v, --verbose 101 | Produce verbose output. 102 | .UNINDENT 103 | 104 | .INDENT 0.0 105 | .TP 106 | .B \-h, --help 107 | Display a help message. 108 | .UNINDENT 109 | 110 | .INDENT 0.0 111 | .TP 112 | .B \-V, --version 113 | Display compiler version information. 114 | .UNINDENT 115 | 116 | 117 | 118 | .SH FILES 119 | .TP 120 | man page 121 | .I 122 | /usr/share/man/man1/evi.1.gz 123 | .TP 124 | bash autocomplete 125 | .I 126 | /usr/share/bash-completion/completions/evi 127 | .TP 128 | stdlib headers 129 | .I 130 | /usr/share/evi/* 131 | .TP 132 | stdlib static library 133 | .I 134 | /usr/lib/libevi.a 135 | .TP 136 | compiler 137 | .I 138 | /usr/bin/evi 139 | 140 | 141 | 142 | .SH EXIT STATUS 143 | .TP 144 | .B 145 | 0 146 | Success 147 | 148 | .TP 149 | .B 150 | 64 151 | Invalid command line arguments 152 | 153 | .TP 154 | .B 155 | 65 156 | Error occured during preprocessing. 157 | 158 | .TP 159 | .B 160 | 66 161 | Error occured during the parsing of the program. 162 | 163 | .TP 164 | .B 165 | 67 166 | Error occured during typchecking. 167 | 168 | .TP 169 | .B 170 | 68 171 | Error occured during code generation. 172 | 173 | .TP 174 | .B 175 | 69 176 | Error occured during file output. 177 | 178 | 179 | .SH BUGS 180 | Please feel free to report bugs to or create an issue on the github page. 181 | 182 | 183 | .SH SEE ALSO 184 | .UR "github page" 185 | https://github.com/SjVer/Evi-Lang 186 | .UE 187 | 188 | .SH COPYRIGHT 189 | .PP 190 | MIT License 191 | 192 | Copyright (c) 2022 Sjoerd Vermeulen 193 | 194 | Permission is hereby granted, free of charge, to any person obtaining a copy 195 | of this software and associated documentation files (the "Software"), to deal 196 | in the Software without restriction, including without limitation the rights 197 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 198 | copies of the Software, and to permit persons to whom the Software is 199 | furnished to do so, subject to the following conditions: 200 | 201 | The above copyright notice and this permission notice shall be included in all 202 | copies or substantial portions of the Software. 203 | 204 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 205 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 206 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 207 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 208 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 209 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 210 | SOFTWARE. 211 | 212 | 213 | .SH CREDITS 214 | .PP 215 | This man page is written by Sjoerd Vermeulen 216 | -------------------------------------------------------------------------------- /tools/sublime-package/Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scope 5 | source.evi 6 | settings 7 | 8 | shellVariables 9 | 10 | 11 | name 12 | TM_COMMENT_START 13 | value 14 | \ 15 | 16 | 17 | name 18 | TM_COMMENT_START_2 19 | value 20 | \: 21 | 22 | 23 | name 24 | TM_COMMENT_END_2 25 | value 26 | :\ 27 | 28 | 29 | name 30 | TM_COMMENT_DISABLE_INDENT_2 31 | value 32 | yes 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tools/sublime-package/Evi.sublime-build: -------------------------------------------------------------------------------- 1 | { 2 | "shell": true, 3 | "quiet": true, 4 | "word_wrap": false, 5 | "kill_previous": true, 6 | 7 | // "syntax": "Packages/ANSIescape/ANSI.sublime-syntax", 8 | // "target": "ansi_color_build", 9 | 10 | // "env": {"CLICOLOR_FORCE": "1"}, 11 | 12 | "cmd": ["~/Coding/Languages/Evi-Lang/bin/evi $file -o $file_path/$file_base_name && $file_path/$file_base_name; rm $file_path/$file_base_name"], 13 | 14 | "file_regex": "^[ ]File \"(...?)\", line ([0-9]*)", 15 | "selector": "source.evi", 16 | "file_patterns": ["*.evi"] 17 | } -------------------------------------------------------------------------------- /tools/sublime-package/Evi.sublime-completions: -------------------------------------------------------------------------------- 1 | { 2 | "scope": "source.evi", 3 | 4 | "completions": [ 5 | // { "trigger": "some_trigger", "contents": "some_trigger" }, 6 | { "trigger": "apply", "contents": "#apply \"$1\"" }, 7 | { "trigger": "flag", "contents": "#flag ${1:DEBUG}" }, 8 | { "trigger": "macro", "contents": "#macro ${1:MACRO}" }, 9 | { "trigger": "undef", "contents": "#undef ${1:MACRO}" }, 10 | { "trigger": "unset", "contents": "#unset ${1:DEBUG}" }, 11 | { "trigger": "ifset", "contents": "#ifset ${1:DEBUG}\n\t$2\n#endif" }, 12 | { "trigger": "ifnset", "contents": "#ifnset ${1:DEBUG}\n\t$2\n#endif" }, 13 | { "trigger": "else", "contents": "#else\n\t$2\n#endif" }, 14 | { "trigger": "endif", "contents": "#endif" }, 15 | 16 | { "trigger": "function", "contents": "@${1:func} ${2:nll} (${3}) $4" }, 17 | { "trigger": "variable", "contents": "%${1:var} ${2:i32}${4: };" }, 18 | ] 19 | } -------------------------------------------------------------------------------- /tools/vscode-extension/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "preLaunchTask": "prepare", 16 | 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /tools/vscode-extension/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "prepare", 8 | "type": "shell", 9 | "command": "clear; npm run grammar; clear; tsc --build", 10 | "presentation": { 11 | "echo": true, 12 | "reveal": "never", 13 | "focus": false, 14 | "panel": "shared", 15 | "showReuseMessage": false, 16 | "clear": true, 17 | "close": true 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /tools/vscode-extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | 6 | build-grammar.js 7 | syntaxes/evi.old.tmLanguage.json 8 | 9 | **/*.ts 10 | **/*.map 11 | **/tsconfig.json 12 | **/tsconfig.base.json 13 | contributing.md 14 | .travis.yml 15 | client/node_modules/** 16 | !client/node_modules/vscode-jsonrpc/** 17 | !client/node_modules/vscode-languageclient/** 18 | !client/node_modules/vscode-languageserver-protocol/** 19 | !client/node_modules/vscode-languageserver-types/** 20 | !client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** 21 | !client/node_modules/{semver,lru-cache,yallist}/** 22 | -------------------------------------------------------------------------------- /tools/vscode-extension/README.md: -------------------------------------------------------------------------------- 1 | # Evi-Lang README 2 | 3 | This is the extension for the Evi language. For more info go to [the github repository](https://github.com/SjVer/Evi-Lang). 4 | 5 | ## Features 6 | 7 | - Syntax highlighting 8 | - Autocomplete 9 | - Suggestions 10 | - Goto-definition 11 | - Diagnostics 12 | - Signature help 13 | - Hover tools 14 | 15 | ## Screenshots 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tools/vscode-extension/assets/readme/features.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjVer/Evi-Lang/675a74fe28abb2d7065c5172279b0905fef4ff0d/tools/vscode-extension/assets/readme/features.gif -------------------------------------------------------------------------------- /tools/vscode-extension/assets/readme/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SjVer/Evi-Lang/675a74fe28abb2d7065c5172279b0905fef4ff0d/tools/vscode-extension/assets/readme/screenshot1.png -------------------------------------------------------------------------------- /tools/vscode-extension/build-grammar.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var fs = require("fs"); 4 | var path = require("path"); 5 | var yaml = require("js-yaml"); 6 | var plist = require("plist"); 7 | // var Language; 8 | // (function (Language) { 9 | // Language["TypeScript"] = "TypeScript"; 10 | // Language["TypeScriptReact"] = "TypeScriptReact"; 11 | // })(Language || (Language = {})); 12 | // var Extension; 13 | // (function (Extension) { 14 | // Extension["TmLanguage"] = "tmLanguage"; 15 | // Extension["TmTheme"] = "tmTheme"; 16 | // Extension["YamlTmLanguage"] = "YAML-tmLanguage"; 17 | // Extension["YamlTmTheme"] = "YAML-tmTheme"; 18 | // })(Extension || (Extension = {})); 19 | // function file(language, extension) { 20 | // return path.join(__dirname, "..", "".concat(language, ".").concat(extension)); 21 | // } 22 | function writePlistFile(grammar, fileName) { 23 | var text = plist.build(grammar); 24 | fs.writeFileSync(fileName, text); 25 | } 26 | function readYaml(fileName) { 27 | var text = fs.readFileSync(fileName, "utf8"); 28 | return yaml.load(text); 29 | } 30 | // function changeTsToTsx(str) { 31 | // return str.replace(/\.ts/g, ".tsx"); 32 | // } 33 | function transformGrammarRule(rule, propertyNames, transformProperty) { 34 | for ( 35 | var _i = 0, propertyNames_1 = propertyNames; 36 | _i < propertyNames_1.length; 37 | _i++ 38 | ) { 39 | var propertyName_1 = propertyNames_1[_i]; 40 | var value = rule[propertyName_1]; 41 | if (typeof value === "string") { 42 | rule[propertyName_1] = transformProperty(value); 43 | } 44 | } 45 | for (var propertyName in rule) { 46 | var value = rule[propertyName]; 47 | if (typeof value === "object") { 48 | transformGrammarRule(value, propertyNames, transformProperty); 49 | } 50 | } 51 | } 52 | function transformGrammarRepository(grammar, propertyNames, transformProperty) { 53 | var repository = grammar.repository; 54 | for (var key in repository) { 55 | transformGrammarRule(repository[key], propertyNames, transformProperty); 56 | } 57 | } 58 | // function getTsxGrammar() { 59 | // var variables; 60 | // var tsxUpdatesBeforeTransformation = readYaml( 61 | // file(Language.TypeScriptReact, Extension.YamlTmLanguage) 62 | // ); 63 | // var grammar = getTsGrammar(function (tsGrammarVariables) { 64 | // variables = tsGrammarVariables; 65 | // for (var variableName in tsxUpdatesBeforeTransformation.variables) { 66 | // variables[variableName] = 67 | // tsxUpdatesBeforeTransformation.variables[variableName]; 68 | // } 69 | // return variables; 70 | // }); 71 | // var tsxUpdates = updateGrammarVariables( 72 | // tsxUpdatesBeforeTransformation, 73 | // variables 74 | // ); 75 | // // Update name, file types, scope name and uuid 76 | // grammar.name = tsxUpdates.name; 77 | // grammar.scopeName = tsxUpdates.scopeName; 78 | // grammar.fileTypes = tsxUpdates.fileTypes; 79 | // grammar.uuid = tsxUpdates.uuid; 80 | // // Update scope names to .tsx 81 | // transformGrammarRepository(grammar, ["name", "contentName"], changeTsToTsx); 82 | // // Add repository items 83 | // var repository = grammar.repository; 84 | // var updatesRepository = tsxUpdates.repository; 85 | // for (var key in updatesRepository) { 86 | // switch (key) { 87 | // case "expressionWithoutIdentifiers": 88 | // // Update expression 89 | // repository[key].patterns.unshift(updatesRepository[key].patterns[0]); 90 | // break; 91 | // default: 92 | // // Add jsx 93 | // repository[key] = updatesRepository[key]; 94 | // } 95 | // } 96 | // return grammar; 97 | // } 98 | function getGrammar(getVariables) { 99 | // function getTsGrammar(getVariables) { 100 | var grammarBeforeTransformation = readYaml( 101 | // file(Language.TypeScript, Extension.YamlTmLanguage) 102 | "./syntaxes/evi.tmLanguage.yaml" 103 | ); 104 | return updateGrammarVariables( 105 | grammarBeforeTransformation, 106 | getVariables(grammarBeforeTransformation.variables) 107 | ); 108 | } 109 | function replacePatternVariables(pattern, variableReplacers) { 110 | var result = pattern; 111 | for ( 112 | var _i = 0, variableReplacers_1 = variableReplacers; 113 | _i < variableReplacers_1.length; 114 | _i++ 115 | ) { 116 | var _a = variableReplacers_1[_i], 117 | variableName = _a[0], 118 | value = _a[1]; 119 | result = result.replace(variableName, value); 120 | } 121 | return result; 122 | } 123 | function updateGrammarVariables(grammar, variables) { 124 | delete grammar.variables; 125 | var variableReplacers = []; 126 | for (var variableName in variables) { 127 | // Replace the pattern with earlier variables 128 | var pattern = replacePatternVariables( 129 | variables[variableName], 130 | variableReplacers 131 | ); 132 | variableReplacers.push([ 133 | new RegExp("{{".concat(variableName, "}}"), "gim"), 134 | pattern, 135 | ]); 136 | } 137 | transformGrammarRepository( 138 | grammar, 139 | ["begin", "end", "match"], 140 | function (pattern) { 141 | return replacePatternVariables(pattern, variableReplacers); 142 | } 143 | ); 144 | return grammar; 145 | } 146 | function buildGrammar() { 147 | var grammar = getGrammar(function (grammarVariables) { 148 | return grammarVariables; 149 | }); 150 | // Write TypeScript.tmLanguage 151 | // writePlistFile(grammar, file(Language.TypeScript, Extension.TmLanguage)); 152 | writePlistFile(grammar, "./syntaxes/evi.generated.tmLanguage"); 153 | // Write TypeScriptReact.tmLangauge 154 | // var tsxGrammar = getTsxGrammar(); 155 | // writePlistFile( 156 | // tsxGrammar, 157 | // file(Language.TypeScriptReact, Extension.TmLanguage) 158 | // ); 159 | } 160 | // function changeTsToTsxTheme(theme) { 161 | // var tsxUpdates = readYaml( 162 | // file(Language.TypeScriptReact, Extension.YamlTmTheme) 163 | // ); 164 | // // Update name, uuid 165 | // theme.name = tsxUpdates.name; 166 | // theme.uuid = tsxUpdates.uuid; 167 | // // Update scope names to .tsx 168 | // var settings = theme.settings; 169 | // for (var i = 0; i < settings.length; i++) { 170 | // settings[i].scope = changeTsToTsx(settings[i].scope); 171 | // } 172 | // // Add additional setting items 173 | // theme.settings = theme.settings.concat(tsxUpdates.settings); 174 | // return theme; 175 | // } 176 | // function buildTheme() { 177 | // var tsTheme = readYaml(file(Language.TypeScript, Extension.YamlTmTheme)); 178 | // // Write TypeScript.tmTheme 179 | // writePlistFile(tsTheme, file(Language.TypeScript, Extension.TmTheme)); 180 | // // Write TypeScriptReact.thTheme 181 | // var tsxTheme = changeTsToTsxTheme(tsTheme); 182 | // writePlistFile(tsxTheme, file(Language.TypeScriptReact, Extension.TmTheme)); 183 | // } 184 | buildGrammar(); 185 | // buildTheme(); 186 | -------------------------------------------------------------------------------- /tools/vscode-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "evi-lang", 3 | "displayName": "Evi-Lang", 4 | "description": "Evi programming language extension", 5 | "version": "0.0.1", 6 | "publisher": "SjVer", 7 | "author": { 8 | "email": "sjoerd@marsenaar.com", 9 | "name": "Sjoerd Vermeulen", 10 | "url": "https://github.com/SjVer" 11 | }, 12 | "categories": [ 13 | "Programming Languages", 14 | "Snippets", 15 | "Linters", 16 | "Testing" 17 | ], 18 | "engines": { 19 | "vscode": "^1.63.0" 20 | }, 21 | "contributes": { 22 | "languages": [ 23 | { 24 | "id": "evi", 25 | "aliases": [ 26 | "Evi", 27 | "evi" 28 | ], 29 | "extensions": [ 30 | ".evi", 31 | ".evii", 32 | ".hevi" 33 | ], 34 | "configuration": "./syntaxes/language-configuration.json" 35 | } 36 | ], 37 | "grammars": [ 38 | { 39 | "language": "evi", 40 | "scopeName": "source.evi", 41 | "path": "./syntaxes/evi.generated.tmLanguage" 42 | } 43 | ], 44 | "snippets": [ 45 | { 46 | "language": "evi", 47 | "path": "./syntaxes/evi.code-snippets" 48 | } 49 | ], 50 | "configuration": [ 51 | { 52 | "title": "Evi", 53 | "properties": { 54 | "evi.eviExecutablePath": { 55 | "type": "string", 56 | "default": "/usr/bin/evi", 57 | "description": "The path to the Evi executable (/usr/bin/evi by default)" 58 | }, 59 | "evi.logDebugInfo": { 60 | "type": "boolean", 61 | "default": false, 62 | "description": "Log debugging information to the console (requires reload)" 63 | }, 64 | "evi.enableLanguageFeatures": { 65 | "type": "boolean", 66 | "default": true, 67 | "description": "Enable the language features of the Evi-Lang extension (requires reload)" 68 | }, 69 | "evi.includeSearchPaths": { 70 | "type": "array", 71 | "default": [], 72 | "description": "A list of the paths used to search for included files." 73 | } 74 | } 75 | } 76 | ], 77 | "taskDefinitions": [ 78 | { 79 | "type": "evi", 80 | "properties": { 81 | "flags": { 82 | "type": "array", 83 | "examples": [ 84 | "--include=include/", 85 | "--compile-only" 86 | ], 87 | "description": "The arguments passed to the compiler." 88 | } 89 | } 90 | } 91 | ] 92 | }, 93 | "activationEvents": [ 94 | "onLanguage:evi" 95 | ], 96 | "main": "./out/eviMain.js", 97 | "scripts": { 98 | "vscode:prepublish": "clear; npm run compile", 99 | "compile": "clear; tsc --build", 100 | "watch": "clear; tsc --build --watch ", 101 | "clean": "clear; tsc --build --clean", 102 | "grammar": "node build-grammar.js" 103 | }, 104 | "devDependencies": { 105 | "@types/node": "16.x", 106 | "@types/vscode": "^1.51.0", 107 | "@types/js-yaml": "latest", 108 | "@types/which": "^2.0.0", 109 | "js-yaml": "latest", 110 | "plist": "latest" 111 | }, 112 | "dependencies": { 113 | "which": "^2.0.2" 114 | }, 115 | "repository": { 116 | "type": "git", 117 | "url": "https://github.com/SjVer/Evi-Lang.git" 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tools/vscode-extension/src/eviMain.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { execSync } from 'child_process'; 3 | 4 | import EviCompletionItemProvider from './features/completionItemProvider'; 5 | import EviHoverProvider from './features/hoverProvider'; 6 | import EviDefenitionProvider from './features/definitionProvider'; 7 | import EviSignatureHelpProvider from './features/signatureHelpProvider'; 8 | import EviValidationProvider from './features/validationProvider'; 9 | // import { EviTaskProvider } from './features/taskProvider'; 10 | 11 | export function activate(context: vscode.ExtensionContext): any { 12 | if(!vscode.workspace.getConfiguration('evi').get('enableLanguageFeatures')) return; 13 | 14 | // // get workspace root 15 | // const workspaceRoot = (vscode.workspace.workspaceFolders && (vscode.workspace.workspaceFolders.length > 0)) 16 | // ? vscode.workspace.workspaceFolders[0].uri.fsPath : undefined; 17 | // if (!workspaceRoot) return; 18 | 19 | let validator = new EviValidationProvider(); 20 | validator.activate(context.subscriptions); 21 | 22 | // set status bar 23 | let statusbarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); 24 | try { 25 | const output = execSync(vscode.workspace.getConfiguration('evi').get('eviExecutablePath') + ' --version').toString(); 26 | statusbarItem.text = output.replace('evi ', 'Evi: '); 27 | statusbarItem.show(); 28 | } 29 | catch (error) { vscode.window.showErrorMessage(`Failed to get Evi version:\n\"${error}\"`); } 30 | 31 | // refresh diagnostics if neccesary 32 | // if (vscode.window.activeTextEditor) diagnostics.eviDiagnosticsChangeActiveEditorCallback(vscode.window.activeTextEditor); 33 | 34 | // add providers 35 | context.subscriptions.push(vscode.languages.registerCompletionItemProvider('evi', new EviCompletionItemProvider())); 36 | context.subscriptions.push(vscode.languages.registerHoverProvider('evi', new EviHoverProvider())); 37 | context.subscriptions.push(vscode.languages.registerDefinitionProvider('evi', new EviDefenitionProvider())); 38 | context.subscriptions.push(vscode.languages.registerSignatureHelpProvider('evi', new EviSignatureHelpProvider(), '(', ',')); 39 | // context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(diagnostics.eviDiagnosticsChangeActiveEditorCallback)); 40 | // context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(diagnostics.eviDiagnosticsChangeTextCallback)); 41 | // context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(diagnostics.eviDiagnosticsCloseEditorCallback)); 42 | // context.subscriptions.push(vscode.tasks.registerTaskProvider(EviTaskProvider.eviTaskType, new EviTaskProvider(workspaceRoot))) 43 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/completionItemProvider.ts: -------------------------------------------------------------------------------- 1 | import { CompletionItemProvider, CompletionItem, CompletionItemKind, CancellationToken, TextDocument, 2 | Position, Range, workspace, CompletionContext, window } from 'vscode'; 3 | import eviSymbols = require('./eviSymbols'); 4 | import { callEviLint, eviLintType, eviLintFunctions, eviLintVariables } from './utils/eviLintUtil'; 5 | 6 | const identifierRegex: RegExp = /^[a-zA-Z_][a-zA-Z0-9_]*$/g; 7 | 8 | export default class EviCompletionItemProvider implements CompletionItemProvider { 9 | 10 | public async provideCompletionItems(document: TextDocument, position: Position, _token: CancellationToken, context: CompletionContext): Promise { 11 | let result: CompletionItem[] = []; 12 | let added: any = {}; 13 | 14 | let shouldProvideCompletionItems = workspace.getConfiguration('editor').get('acceptSuggestionOnCommitCharacter', true); 15 | if (!shouldProvideCompletionItems) return Promise.resolve(result); 16 | 17 | let range = document.getWordRangeAtPosition(position); 18 | let prefix = range ? document.getText(range) : ''; 19 | if (!range) range = new Range(position, position); 20 | 21 | let createNewProposal = function (kind: CompletionItemKind, name: string, entry: eviSymbols.IEntry | null): CompletionItem { 22 | let proposal: CompletionItem = new CompletionItem(name); 23 | proposal.kind = kind; 24 | if (entry) { 25 | if (entry.description) proposal.documentation = entry.description; 26 | if (entry.signature) proposal.detail = entry.signature; 27 | } 28 | return proposal; 29 | }; 30 | let matches = (name: string) => { return prefix.length === 0 || name.length >= prefix.length && name.substring(0, prefix.length) === prefix; }; 31 | 32 | // search for keywords 33 | for (let keyword in eviSymbols.keywords) { 34 | if (matches(keyword)) { 35 | added[keyword] = true; 36 | result.push(createNewProposal(CompletionItemKind.Keyword, keyword, eviSymbols.keywords[keyword])); 37 | } 38 | } 39 | 40 | // search for variables 41 | if (prefix[0] === '$' || prefix.length === 0) { 42 | const vars: eviLintVariables = await callEviLint(document, eviLintType.getVariables, position); 43 | 44 | for(var variable of vars.variables) { 45 | let word = '$' + variable.identifier; 46 | if (matches(word) && !added[word]) { 47 | added[word] = true; 48 | const signature = `$${variable.identifier} -> ${variable.type}`; 49 | result.push(createNewProposal(CompletionItemKind.Variable, word, { signature: signature })); 50 | } 51 | } 52 | } 53 | 54 | // search for types 55 | for (let type in eviSymbols.types) { 56 | if (matches(type)) { 57 | added[type] = true; 58 | result.push(createNewProposal(CompletionItemKind.Class, type, eviSymbols.types[type])); 59 | } 60 | } 61 | 62 | // search for directives 63 | if (prefix[0] === '#' || prefix.length === 0) 64 | { 65 | for (let directive in eviSymbols.directives) { 66 | if (matches('#' + directive)) { 67 | added['#' + directive] = true; 68 | result.push(createNewProposal(CompletionItemKind.Keyword, '#' + directive, eviSymbols.directives[directive])); 69 | } 70 | } 71 | } 72 | 73 | // search in functions 74 | if (identifierRegex.test(prefix) || prefix.length === 0) { 75 | const funcs: eviLintFunctions = await callEviLint(document, eviLintType.getFunctions, position); 76 | 77 | for(var func of funcs.functions) { 78 | let word = func.identifier; 79 | if (matches(word) && !added[word]) { 80 | added[word] = true; 81 | 82 | let signature = `@${func.identifier} ${func.return_type} (`; 83 | func.parameters.forEach(param => signature += param + ' '); 84 | if(func.variadic) signature += '... '; 85 | signature = signature.trim() + ')'; 86 | 87 | result.push(createNewProposal(CompletionItemKind.Function, word, { signature: signature })); 88 | } 89 | } 90 | } 91 | 92 | return Promise.resolve(result); 93 | } 94 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/definitionProvider.ts: -------------------------------------------------------------------------------- 1 | import { CancellationToken, Definition, DefinitionProvider, LocationLink, Position, ProviderResult, TextDocument, Range, window, Uri } from 'vscode'; 2 | import { callEviLint, eviLintDeclaration, eviLintType } from './utils/eviLintUtil'; 3 | 4 | export default class EviDefenitionProvider implements DefinitionProvider { 5 | 6 | public provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult { 7 | let result: eviLintDeclaration; 8 | callEviLint(document, eviLintType.getDeclaration, position) 9 | .then(_result => result = _result) 10 | .catch(_reason => { return Promise.reject("Declaration not found."); }); 11 | 12 | const start: Position = new Position(result.position.line, result.position.column); 13 | const end: Position = start.translate(0, result.position.length); 14 | return { uri: Uri.file(result.position.file), range: new Range(start, end) }; 15 | } 16 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/diagnosticsUpdater.ts.unused: -------------------------------------------------------------------------------- 1 | import { TextDocument, Position, Uri, Diagnostic, Range, DiagnosticSeverity, TextEditor, languages, TextDocumentChangeEvent, Location } from 'vscode'; 2 | import { callEviLint, eviLintType, eviLintDiagnostics } from './utils/eviLintUtil'; 3 | 4 | const collection = languages.createDiagnosticCollection('evi'); 5 | 6 | function eviDiagnosticsCallback(document: TextDocument, retry: boolean = false): void { 7 | if(!document) { collection.clear(); return; } 8 | 9 | const errors: eviLintDiagnostics = callEviLint(document, eviLintType.getDiagnostics, new Position(0, 0)); 10 | if(!errors) { 11 | if(!retry) eviDiagnosticsCallback(document, true); 12 | else { console.log("evi lint failed to get errors."); return; } 13 | } 14 | 15 | let diagnostics: { [file: string]: Diagnostic[] } = {}; 16 | 17 | // gather all diagnostics sorted by file 18 | errors.errors.forEach(error => { 19 | if(!diagnostics[error.position.file]) diagnostics[error.position.file] = []; 20 | 21 | const start: Position = new Position(error.position.line, error.position.column); 22 | const end: Position = start.translate(0, error.position.length); 23 | 24 | let diagnostic: Diagnostic = new Diagnostic(new Range(start, end), error.message); 25 | diagnostic.source = 'evi'; 26 | // diagnostic.code = some code for CodeActions, 27 | diagnostic.relatedInformation = []; 28 | 29 | // add related information 30 | error.related.forEach(info => { 31 | const start: Position = new Position(info.position.line, info.position.column); 32 | const end: Position = start.translate(0, info.position.length); 33 | 34 | diagnostic.relatedInformation.push({ 35 | location: { uri: Uri.file(info.position.file), range: new Range(start, end) }, 36 | message: info.message, 37 | }); 38 | }); 39 | 40 | // set severity 41 | if(error.type == "error") diagnostic.severity = DiagnosticSeverity.Error; 42 | else if(error.type == "warning") diagnostic.severity = DiagnosticSeverity.Warning; 43 | 44 | diagnostics[error.position.file].push(diagnostic); 45 | }); 46 | 47 | // set al diagnostics per file 48 | for (let file in diagnostics) collection.set(Uri.file(file), diagnostics[file]); 49 | } 50 | 51 | export const eviDiagnosticsChangeActiveEditorCallback = (editor: TextEditor) => { 52 | if(editor.document.languageId != 'evi') return; 53 | collection.delete(editor.document.uri); 54 | eviDiagnosticsCallback(editor.document); 55 | } 56 | 57 | export const eviDiagnosticsChangeTextCallback = (event: TextDocumentChangeEvent) => { 58 | if(event.document.languageId != 'evi') return; 59 | collection.delete(event.document.uri); 60 | eviDiagnosticsCallback(event.document); 61 | } 62 | 63 | export const eviDiagnosticsCloseEditorCallback = (document: TextDocument) => { 64 | if(document.languageId != 'evi') return; 65 | collection.delete(document.uri); 66 | }; -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/eviSymbols.ts: -------------------------------------------------------------------------------- 1 | export interface IEntry { description?: string; signature?: string } 2 | export interface IEntries { [name: string]: IEntry } 3 | 4 | export const keywords: IEntries = { 5 | '@': { 6 | description: 'declare/define function', 7 | signature: '@func type (params)' 8 | }, 9 | '%': { 10 | description: 'declare/define variable', 11 | signature: '%var type value' 12 | }, 13 | '~': { 14 | description: 'return', 15 | signature: '~ [value]' 16 | }, 17 | '=': { 18 | description: 'assign', 19 | signature: '=var value' 20 | }, 21 | '??': { 22 | description: 'if', 23 | signature: '??(cond) then [:: else]' 24 | }, 25 | '::': { 26 | description: 'else', 27 | signature: '??(cond) then [:: else]' 28 | }, 29 | '!!': { 30 | description: 'loop', 31 | signature: '!!([init]; [cond]; [inc];) body' 32 | }, 33 | }; 34 | 35 | export const directives: IEntries = { 36 | 'apply': { 37 | description: "import directive", 38 | signature: "#apply \"FILENAME\"" 39 | }, 40 | 'info': { 41 | description: "pragma directive", 42 | signature: "#info OPTION \\ [ARGUMENTS...] (compiler specific)" 43 | }, 44 | 'file': { 45 | description: "filename directive", 46 | signature: "#file \"FILENAME\"" 47 | }, 48 | 'line': { 49 | description: "line number directive", 50 | signature: "#line LINENO" 51 | }, 52 | 53 | 'macro': { 54 | description: "define macro directive", 55 | signature: "#macro MACRO body" 56 | }, 57 | 'undef': { 58 | description: "undefine macro directive", 59 | signature: "#undef MACRO" 60 | }, 61 | 62 | 'flag': { 63 | description: "set flag directive", 64 | signature: "#flag FLAG" 65 | }, 66 | 'unset': { 67 | description: "unset flag directive", 68 | signature: "#unset FLAG" 69 | }, 70 | 71 | 72 | 'ifdef': { 73 | description: "if-macro-is-defined directive", 74 | signature: "#ifdef MACRO" 75 | }, 76 | 'ifndef': { 77 | description: "if-macro-is-not-defined directive", 78 | signature: "#ifndef MACRO" 79 | }, 80 | 'ifset': { 81 | description: "if-flag-is-set directive", 82 | signature: "#ifset FLAG" 83 | }, 84 | 'ifnset': { 85 | description: "if-flag-is-not-set directive", 86 | signature: "#ifnset FLAG" 87 | }, 88 | 'else': { 89 | description: "else directive", 90 | signature: "#else" 91 | }, 92 | 'endif': { 93 | description: "end if-statement directive", 94 | signature: "#endif" 95 | }, 96 | }; 97 | 98 | export const types: IEntries = { 99 | 'i1': { 100 | description: "1-bit signed integer", 101 | signature: "1-bit signed int" 102 | }, 103 | 104 | 'i4': { 105 | description: "4-bit signed integer", 106 | signature: "4-bit signed int" 107 | }, 108 | 'ui4': { 109 | description: "4-bit unsigned integer", 110 | signature: "4-bit unsigned int" 111 | }, 112 | 113 | 'i8': { 114 | description: "8-bit signed integer", 115 | signature: "8-bit signed int" 116 | }, 117 | 'ui8': { 118 | description: "8-bit unsigned integer", 119 | signature: "8-bit unsigned int" 120 | }, 121 | 122 | 'i16': { 123 | description: "16-bit signed integer", 124 | signature: "16-bit signed int" 125 | }, 126 | 'ui16': { 127 | description: "16-bit unsigned integer", 128 | signature: "16-bit unsigned int" 129 | }, 130 | 131 | 'i32': { 132 | description: "32-bit signed integer", 133 | signature: "32-bit signed int" 134 | }, 135 | 'ui32': { 136 | description: "32-bit unsigned integer", 137 | signature: "32-bit unsigned int" 138 | }, 139 | 140 | 'i64': { 141 | description: "64-bit signed integer", 142 | signature: "64-bit signed int" 143 | }, 144 | 'ui64': { 145 | description: "64-bit unsigned integer", 146 | signature: "64-bit unsigned int" 147 | }, 148 | 149 | 'i128': { 150 | description: "128-bit signed integer", 151 | signature: "128-bit signed int" 152 | }, 153 | 'ui128': { 154 | description: "128-bit unsigned integer", 155 | signature: "128-bit unsigned int" 156 | }, 157 | 158 | 'flt': { 159 | description: "32-bit single precision floating point number", 160 | signature: "single precision float" 161 | }, 162 | 'dbl': { 163 | description: "64-bit double precision floating point number", 164 | signature: "double precision float" 165 | }, 166 | 167 | 'sze': { 168 | description: "unsigned integral", 169 | signature: "size" 170 | }, 171 | 'bln': { 172 | description: "unsigned 1-bit boolean", 173 | signature: "boolean" 174 | }, 175 | 'chr': { 176 | description: "unsigned 8-bit character", 177 | signature: "8-bit character" 178 | }, 179 | 'nll': { 180 | description: "null/void type", 181 | signature: "null/void" 182 | }, 183 | 184 | 185 | '...': { 186 | description: "variadic arguments operator", 187 | signature: "variadic" 188 | }, 189 | } 190 | -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/hoverProvider.ts: -------------------------------------------------------------------------------- 1 | import { HoverProvider, Hover, MarkedString, TextDocument, CancellationToken, Position, workspace, window, SemanticTokens } from 'vscode'; 2 | import { textToMarkedString } from './utils/markedTextUtil'; 3 | import { callEviLint, eviLintType, getDocumentationAsString } from './utils/eviLintUtil'; 4 | import eviSymbols = require('./eviSymbols'); 5 | 6 | const constantRegexes: { [regex: string]: string } = { 7 | /*integers*/ "^(-?(?:0(x|X)[0-f]+)|(0(c|C)[0-7]+)|(0(b|B)[0-1]+)|([0-9]+))$": "i32", 8 | /*floats */ "^(-?[0-9]+(\.[0-9]+)?)$": "dbl", 9 | /*strings */ "^(\"(\\.|.)*\")$": "chr*", 10 | } 11 | 12 | const functionRegex: RegExp = /[a-zA-Z_]+[a-zA-Z0-9_]*/; 13 | 14 | export default class EviHoverProvider implements HoverProvider { 15 | 16 | public async provideHover(document: TextDocument, position: Position, _token: CancellationToken): Promise { 17 | let wordRange = document.getWordRangeAtPosition(position); 18 | let word = wordRange ? document.getText(wordRange) : ''; 19 | 20 | if (eviSymbols.keywords[word]) { 21 | // its a keyword 22 | let entry = eviSymbols.keywords[word]; 23 | let signature = { language: 'evi', value: entry.signature || word }; 24 | let documentation = textToMarkedString(entry.description || ''); 25 | return new Hover([signature, documentation], wordRange); 26 | } 27 | else if (eviSymbols.types[word]) 28 | { 29 | // its a type 30 | let documentation = textToMarkedString(eviSymbols.types[word].description); 31 | return new Hover([{ 32 | language: 'evi', 33 | value: word 34 | }, documentation], wordRange); 35 | } 36 | else if(word.startsWith('#')) 37 | { 38 | // preprocessor directive 39 | for (let directive in eviSymbols.directives) { 40 | if (word.substring(1) === directive) 41 | { 42 | let entry = eviSymbols.directives[directive]; 43 | let signature = { language: 'evi', value: entry.signature || word }; 44 | let documentation = textToMarkedString(entry.description || ''); 45 | return new Hover([signature, documentation], wordRange); 46 | } 47 | } 48 | } 49 | else if(word.startsWith('$')) 50 | { 51 | // variable 52 | // find type 53 | let signature: string = ""; 54 | await callEviLint(document, eviLintType.getVariables, position).then(result => { 55 | result.variables.forEach(variable => { 56 | if(signature.length || word != '$' + variable.identifier) return; 57 | signature = `%${variable.identifier} ${variable.type}`; 58 | }); 59 | }); 60 | 61 | // found! 62 | if(signature.length) { 63 | const documentation = await getDocumentationAsString(document, position); 64 | return new Hover([{ language: 'evi', value: signature }, documentation], wordRange); 65 | } 66 | } 67 | else if(functionRegex.test(word)) 68 | { 69 | // function? 70 | // find params and whatnot 71 | let signature: string = ""; 72 | await callEviLint(document, eviLintType.getFunctions, position).then(result => { 73 | result.functions.forEach(func => { 74 | if(signature.length || word != func.identifier) return; 75 | 76 | signature = `@${func.identifier} ${func.return_type} (`; 77 | for (let param in func.parameters) signature += func.parameters[param] + ' '; 78 | if (func.variadic) signature += '... '; 79 | signature = signature.trim() + ')'; 80 | }); 81 | }); 82 | 83 | // found! 84 | if(signature.length) { 85 | const documentation = await getDocumentationAsString(document, position); 86 | return new Hover([{ language: 'evi', value: signature }, documentation], wordRange); 87 | } 88 | } 89 | for (let regex in constantRegexes) { 90 | if(new RegExp(regex).test(word)) 91 | return new Hover({ 92 | language: 'evi', 93 | value: word + ' -> ' + constantRegexes[regex] 94 | }, wordRange); 95 | } 96 | 97 | // return wordRange ? new Hover([{ language: 'evi', value: word }, "Symbol not found."], wordRange) : undefined; 98 | return undefined; 99 | } 100 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/signatureHelpProvider.ts: -------------------------------------------------------------------------------- 1 | import { SignatureHelpProvider, SignatureHelp, SignatureInformation, CancellationToken, TextDocument, Position, workspace, window } from 'vscode'; 2 | import { callEviLint, eviLintType, getDocumentation } from './utils/eviLintUtil'; 3 | import { BackwardIterator } from './utils/backwardIterator'; 4 | 5 | export default class EviSignatureHelpProvider implements SignatureHelpProvider { 6 | 7 | 8 | public async provideSignatureHelp(document: TextDocument, position: Position, _token: CancellationToken): Promise { 9 | if (!workspace.getConfiguration('evi').get('suggestions', true)) return null; 10 | 11 | let iterator = new BackwardIterator(document, position.character - 1, position.line); 12 | 13 | let paramCount = this.readArguments(iterator); 14 | if (paramCount < 0) return null; 15 | 16 | let ident = this.readIdent(iterator); 17 | if (!ident) return null; 18 | 19 | // find params and whatnot 20 | let signature: string = ""; 21 | let params: string[] = []; 22 | await callEviLint(document, eviLintType.getFunctions, position).then(result => { 23 | result.functions.forEach(func => { 24 | if(signature.length || ident != func.identifier) return; 25 | 26 | signature = `@${func.identifier} ${func.return_type} (`; 27 | for (let param in func.parameters) { 28 | params.push(func.parameters[param]); 29 | signature += func.parameters[param] + ' '; 30 | } 31 | if (func.variadic) { 32 | signature += "..."; 33 | params.push("... "); 34 | } 35 | if (signature.endsWith(' ')) signature = signature.substring(0, signature.length - 1); 36 | signature += ')'; 37 | }); 38 | }); 39 | if(!signature.length) return Promise.reject("Function not found."); 40 | 41 | const doc = await getDocumentation(document, position); 42 | let signatureInfo = new SignatureInformation(signature, doc.main); 43 | params.forEach(param => signatureInfo.parameters.push({ label: param, documentation: doc.params[signatureInfo.parameters.length] })); 44 | 45 | console.log(signatureInfo.parameters as any); 46 | console.log(Math.min(paramCount, signatureInfo.parameters.length - 1)); 47 | 48 | let ret = new SignatureHelp(); 49 | ret.signatures.push(signatureInfo); 50 | ret.activeSignature = 0; 51 | ret.activeParameter = Math.min(paramCount, signatureInfo.parameters.length - 1); 52 | return Promise.resolve(ret); 53 | } 54 | 55 | private readArguments(iterator: BackwardIterator): number { 56 | let parentNesting = 0; 57 | let bracketNesting = 0; 58 | let curlyNesting = 0; 59 | let paramCount = 0; 60 | while (iterator.hasNext()) { 61 | let ch = iterator.next(); 62 | switch (ch) { 63 | // case CharCodes.LParent: 64 | case '(': 65 | parentNesting--; 66 | if (parentNesting < 0) { 67 | return paramCount; 68 | } 69 | break; 70 | // case CharCodes.RParent: parentNesting++; break; 71 | case ')': parentNesting++; break; 72 | // case CharCodes.LCurly: curlyNesting--; break; 73 | case '{': curlyNesting--; break; 74 | // case CharCodes.RCurly: curlyNesting++; break; 75 | case '}': curlyNesting++; break; 76 | // case CharCodes.LBracket: bracketNesting--; break; 77 | case '[': bracketNesting--; break; 78 | // case CharCodes.RBracket: bracketNesting++; break; 79 | case ']': bracketNesting++; break; 80 | // case CharCodes.DQuote: 81 | case '"': 82 | // case CharCodes.Quote: 83 | case '\'': 84 | while (iterator.hasNext() && ch !== iterator.next()) { 85 | // find the closing quote or double quote 86 | } 87 | break; 88 | // case CharCodes.Comma: 89 | case ',': 90 | if (!parentNesting && !bracketNesting && !curlyNesting) paramCount++; 91 | break; 92 | } 93 | } 94 | return -1; 95 | } 96 | 97 | private isIdentPart(ch: string): boolean { 98 | // if (ch === CharCodes.USC || // _ 99 | // ch >= CharCodes.a && ch <= CharCodes.z || // a-z 100 | // ch >= CharCodes.A && ch <= CharCodes.Z || // A-Z 101 | // ch >= CharCodes._0 && ch <= CharCodes._9 || // 0/9 102 | // ch >= 0x80 && ch <= 0xFFFF) { // nonascii 103 | return /[a-zA-Z0-9_]/.test(ch); 104 | } 105 | 106 | private readIdent(iterator: BackwardIterator): string { 107 | let identStarted = false; 108 | let ident = ''; 109 | while (iterator.hasNext()) { 110 | let ch = iterator.next(); 111 | if (!identStarted && /\s/.test(ch)) { // (ch === CharCodes.WSB || ch === CharCodes.TAB || ch === CharCodes.NL)) { 112 | continue; 113 | } 114 | if (this.isIdentPart(ch)) { 115 | identStarted = true; 116 | // ident = String.fromCharCode(ch) + ident; 117 | ident = ch + ident; 118 | } else if (identStarted) { 119 | return ident; 120 | } 121 | } 122 | return ident; 123 | } 124 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/taskProvider.ts: -------------------------------------------------------------------------------- 1 | import { TaskDefinition, TaskProvider, Task, TaskScope, Pseudoterminal, CustomExecution, EventEmitter, Event, FileSystemWatcher, TerminalDimensions, workspace } from 'vscode'; 2 | import { exec } from 'child_process'; 3 | 4 | interface EviTaskDefinition extends TaskDefinition { flags?: string[] } 5 | 6 | export class EviTaskProvider implements TaskProvider { 7 | static eviTaskType: string = "evi"; 8 | private tasks: Task[] | undefined; 9 | private sharedState: string | undefined; 10 | 11 | constructor(private workspaceRoot: string) { } 12 | public async provideTasks(): Promise { return this.getTasks(); } 13 | 14 | public resolveTask(task: Task): Task | undefined { 15 | const definition: EviTaskDefinition = task.definition; 16 | return this.getTask(definition.flags ? definition.flags : [], definition); 17 | } 18 | 19 | private getTasks(): Task[] { 20 | if (this.tasks !== undefined) return this.tasks; 21 | else return [ this.getTask([]) ]; 22 | } 23 | 24 | private getTask(flags: string[], definition?: EviTaskDefinition): Task { 25 | if (definition === undefined) definition = { type: EviTaskProvider.eviTaskType, flags }; 26 | 27 | return new Task(definition, TaskScope.Workspace, `${flags.join(' ')}`, 28 | EviTaskProvider.eviTaskType, new CustomExecution(async (): Promise => { 29 | // When the task is executed, this callback will run. Here, we setup for running the task. 30 | return new EviTaskTerminal(this.workspaceRoot, flags, () => this.sharedState, (state: string) => this.sharedState = state); 31 | })); 32 | } 33 | } 34 | 35 | class EviTaskTerminal implements Pseudoterminal { 36 | private writeEmitter = new EventEmitter(); 37 | onDidWrite: Event = this.writeEmitter.event; 38 | private closeEmitter = new EventEmitter(); 39 | onDidClose?: Event = this.closeEmitter.event; 40 | 41 | private eviExecutable: string = workspace.getConfiguration('evi').get('eviExecutablePath'); 42 | 43 | private fileWatcher: FileSystemWatcher | undefined; 44 | 45 | constructor(private workspaceRoot: string, private flags: string[], private getSharedState: () => string | undefined, private setSharedState: (state: string) => void) { 46 | } 47 | 48 | open(_initialDimensions: TerminalDimensions | undefined): void { 49 | // At this point we can start using the terminal. 50 | 51 | // if (this.flags.indexOf('watch') > -1) { 52 | // const pattern = path.join(this.workspaceRoot, 'customBuildFile'); 53 | // this.fileWatcher = workspace.createFileSystemWatcher(pattern); 54 | // this.fileWatcher.onDidChange(() => this.doBuild()); 55 | // this.fileWatcher.onDidCreate(() => this.doBuild()); 56 | // this.fileWatcher.onDidDelete(() => this.doBuild()); 57 | // } 58 | 59 | this.doBuild(); 60 | } 61 | 62 | close(): void { 63 | // The terminal has been closed. Shutdown the build. 64 | if (this.fileWatcher) { 65 | this.fileWatcher.dispose(); 66 | } 67 | } 68 | 69 | private async doBuild(): Promise { 70 | let result: { error, stdout: string, sterr: string}; 71 | 72 | const cmd = `${this.eviExecutable} ~/Coding/Languages/Evi-Lang/test/test.evi ${this.flags.join(' ')}`; 73 | exec(cmd, (error, stdout, stderr) => { 74 | result.error = error; 75 | result.stdout = stdout; 76 | result.sterr = stderr; 77 | }); 78 | 79 | this.writeEmitter.fire(result.stdout); 80 | 81 | this.closeEmitter.fire(0); 82 | } 83 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/utils/async.ts: -------------------------------------------------------------------------------- 1 | export interface ITask { 2 | (): T; 3 | } 4 | 5 | /** 6 | * A helper to prevent accumulation of sequential async tasks. 7 | * 8 | * Imagine a mail man with the sole task of delivering letters. As soon as 9 | * a letter submitted for delivery, he drives to the destination, delivers it 10 | * and returns to his base. Imagine that during the trip, N more letters were submitted. 11 | * When the mail man returns, he picks those N letters and delivers them all in a 12 | * single trip. Even though N+1 submissions occurred, only 2 deliveries were made. 13 | * 14 | * The throttler implements this via the queue() method, by providing it a task 15 | * factory. Following the example: 16 | * 17 | * var throttler = new Throttler(); 18 | * var letters = []; 19 | * 20 | * function letterReceived(l) { 21 | * letters.push(l); 22 | * throttler.queue(() => { return makeTheTrip(); }); 23 | * } 24 | */ 25 | export class Throttler { 26 | 27 | private activePromise: Promise | null; 28 | private queuedPromise: Promise | null; 29 | private queuedPromiseFactory: ITask> | null; 30 | 31 | constructor() { 32 | this.activePromise = null; 33 | this.queuedPromise = null; 34 | this.queuedPromiseFactory = null; 35 | } 36 | 37 | public queue(promiseFactory: ITask>): Promise { 38 | if (this.activePromise) { 39 | this.queuedPromiseFactory = promiseFactory; 40 | 41 | if (!this.queuedPromise) { 42 | let onComplete = () => { 43 | this.queuedPromise = null; 44 | 45 | let result = this.queue(this.queuedPromiseFactory!); 46 | this.queuedPromiseFactory = null; 47 | 48 | return result; 49 | }; 50 | 51 | this.queuedPromise = new Promise((resolve) => { 52 | this.activePromise!.then(onComplete, onComplete).then(resolve); 53 | }); 54 | } 55 | 56 | return new Promise((resolve, reject) => { 57 | this.queuedPromise!.then(resolve, reject); 58 | }); 59 | } 60 | 61 | this.activePromise = promiseFactory(); 62 | 63 | return new Promise((resolve, reject) => { 64 | this.activePromise!.then((result: T) => { 65 | this.activePromise = null; 66 | resolve(result); 67 | }, (err: any) => { 68 | this.activePromise = null; 69 | reject(err); 70 | }); 71 | }); 72 | } 73 | } 74 | 75 | /** 76 | * A helper to delay execution of a task that is being requested often. 77 | * 78 | * Following the throttler, now imagine the mail man wants to optimize the number of 79 | * trips proactively. The trip itself can be long, so the he decides not to make the trip 80 | * as soon as a letter is submitted. Instead he waits a while, in case more 81 | * letters are submitted. After said waiting period, if no letters were submitted, he 82 | * decides to make the trip. Imagine that N more letters were submitted after the first 83 | * one, all within a short period of time between each other. Even though N+1 84 | * submissions occurred, only 1 delivery was made. 85 | * 86 | * The delayer offers this behavior via the trigger() method, into which both the task 87 | * to be executed and the waiting period (delay) must be passed in as arguments. Following 88 | * the example: 89 | * 90 | * var delayer = new Delayer(WAITING_PERIOD); 91 | * var letters = []; 92 | * 93 | * function letterReceived(l) { 94 | * letters.push(l); 95 | * delayer.trigger(() => { return makeTheTrip(); }); 96 | * } 97 | */ 98 | export class Delayer { 99 | 100 | public defaultDelay: number; 101 | private timeout: NodeJS.Timer | null; 102 | private completionPromise: Promise | null; 103 | private onResolve: ((value: T | PromiseLike | undefined) => void) | null; 104 | private task: ITask | null; 105 | 106 | constructor(defaultDelay: number) { 107 | this.defaultDelay = defaultDelay; 108 | this.timeout = null; 109 | this.completionPromise = null; 110 | this.onResolve = null; 111 | this.task = null; 112 | } 113 | 114 | public trigger(task: ITask, delay: number = this.defaultDelay): Promise { 115 | this.task = task; 116 | this.cancelTimeout(); 117 | 118 | if (!this.completionPromise) { 119 | this.completionPromise = new Promise((resolve) => { 120 | this.onResolve = resolve; 121 | }).then(() => { 122 | this.completionPromise = null; 123 | this.onResolve = null; 124 | 125 | let result = this.task!(); 126 | this.task = null; 127 | 128 | return result; 129 | }); 130 | } 131 | 132 | this.timeout = setTimeout(() => { 133 | this.timeout = null; 134 | this.onResolve!(undefined); 135 | }, delay); 136 | 137 | return this.completionPromise; 138 | } 139 | 140 | public isTriggered(): boolean { 141 | return this.timeout !== null; 142 | } 143 | 144 | public cancel(): void { 145 | this.cancelTimeout(); 146 | 147 | if (this.completionPromise) { 148 | this.completionPromise = null; 149 | } 150 | } 151 | 152 | private cancelTimeout(): void { 153 | if (this.timeout !== null) { 154 | clearTimeout(this.timeout); 155 | this.timeout = null; 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * A helper to delay execution of a task that is being requested often, while 162 | * preventing accumulation of consecutive executions, while the task runs. 163 | * 164 | * Simply combine the two mail man strategies from the Throttler and Delayer 165 | * helpers, for an analogy. 166 | */ 167 | export class ThrottledDelayer extends Delayer> { 168 | 169 | private throttler: Throttler; 170 | 171 | constructor(defaultDelay: number) { 172 | super(defaultDelay); 173 | 174 | this.throttler = new Throttler(); 175 | } 176 | 177 | public override trigger(promiseFactory: ITask>, delay?: number): Promise> { 178 | return super.trigger(() => this.throttler.queue(promiseFactory), delay); 179 | } 180 | } 181 | 182 | // =================================================== 183 | 184 | // refer to https://xmanyou.com/javascript-wait-until-condition-meet-or-timeout/ 185 | export function waitUntil(condition: () => boolean, timeout: number = 0, interval: number = 50) { 186 | let waitHandler; 187 | let timeoutHandler; 188 | return new Promise(function (resolve, reject) { 189 | var waitFn = function () { 190 | if (condition()) { 191 | if(timeoutHandler){ 192 | clearTimeout(timeoutHandler); 193 | } 194 | resolve(); 195 | } 196 | else { 197 | waitHandler = setTimeout(waitFn, interval); 198 | } 199 | }; 200 | // 201 | waitHandler = setTimeout(waitFn, interval); 202 | 203 | // timeout, if timeout <=0, never timeout 204 | if(timeout>0){ 205 | timeoutHandler = setTimeout(()=>{ 206 | if(waitHandler){ 207 | clearTimeout(waitHandler); 208 | } 209 | 210 | reject({ 211 | code:"TIMEOUT", 212 | message: "timeout" 213 | }); 214 | }, timeout); 215 | } 216 | }); 217 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/utils/backwardIterator.ts: -------------------------------------------------------------------------------- 1 | import { TextDocument } from "vscode"; 2 | 3 | // export enum CharCodes { 4 | // NL = '\n'.charCodeAt(0), 5 | // TAB = '\t'.charCodeAt(0), 6 | // WSB = ' '.charCodeAt(0), 7 | // LBracket = '['.charCodeAt(0), 8 | // RBracket = ']'.charCodeAt(0), 9 | // LCurly = '{'.charCodeAt(0), 10 | // RCurly = '}'.charCodeAt(0), 11 | // LParent = '('.charCodeAt(0), 12 | // RParent = ')'.charCodeAt(0), 13 | // Comma = ','.charCodeAt(0), 14 | // Quote = '\''.charCodeAt(0), 15 | // DQuote = '"'.charCodeAt(0), 16 | // USC = '_'.charCodeAt(0), 17 | // a = 'a'.charCodeAt(0), 18 | // z = 'z'.charCodeAt(0), 19 | // A = 'A'.charCodeAt(0), 20 | // Z = 'Z'.charCodeAt(0), 21 | // _0 = '0'.charCodeAt(0), 22 | // _9 = '9'.charCodeAt(0), 23 | // BOF = 0 24 | // } 25 | 26 | export class BackwardIterator { 27 | private lineNumber: number; 28 | private offset: number; 29 | private line: string; 30 | private model: TextDocument; 31 | 32 | constructor(model: TextDocument, offset: number, lineNumber: number) { 33 | this.lineNumber = lineNumber; 34 | this.offset = offset; 35 | this.line = model.lineAt(this.lineNumber).text; 36 | this.model = model; 37 | } 38 | 39 | public hasNext(): boolean { 40 | return this.lineNumber >= 0; 41 | } 42 | 43 | public next(): string { 44 | if (this.offset < 0) { 45 | if (this.lineNumber > 0) { 46 | this.lineNumber--; 47 | this.line = this.model.lineAt(this.lineNumber).text; 48 | this.offset = this.line.length - 1; 49 | // return CharCodes.NL; 50 | return '\n'; 51 | } 52 | this.lineNumber = -1; 53 | // return CharCodes.BOF; 54 | return '\0'; 55 | } 56 | // let ch = this.line.charCodeAt(this.offset); 57 | let ch = this.line.charAt(this.offset); 58 | this.offset--; 59 | return ch; 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/utils/eviLintUtil.ts: -------------------------------------------------------------------------------- 1 | import { TextDocument, Position, window, workspace, MarkdownString, Uri } from 'vscode'; 2 | import { BackwardIterator } from './backwardIterator'; 3 | import { execSync } from 'child_process'; 4 | import { copyFileSync, rmSync, writeFileSync } from 'fs'; 5 | import { basename, dirname, join } from 'path'; 6 | import { tmpdir } from 'os'; 7 | import { waitUntil } from './async'; 8 | 9 | export enum eviLintType { 10 | getDeclaration = 'get-declaration', 11 | getDiagnostics = 'get-diagnostics', 12 | getFunctions = 'get-functions', 13 | getVariables = 'get-variables', 14 | } 15 | 16 | export interface eviLintPosition { file: string, line: number, column: number, length: number }; 17 | export interface eviLintDeclaration { position: eviLintPosition }; 18 | export interface eviLintDiagnostics { diagnostics: { position: eviLintPosition, message: string, type: string, related: { position: eviLintPosition, message: string }[] }[] }; 19 | export interface eviLintFunctions { functions: { identifier: string, return_type: string, parameters: string[], variadic: boolean }[] }; 20 | export interface eviLintVariables { variables: { identifier: string, type: string }[] }; 21 | 22 | let do_log: boolean = workspace.getConfiguration('evi').get('logDebugInfo'); 23 | function log(message?: any, ...optionalParams: any[]): void { 24 | if (do_log) console.log(message, ...optionalParams); 25 | } 26 | 27 | let blocked = false; 28 | 29 | export async function callEviLint(document: TextDocument, type: eviLintType, position?: Position): Promise { 30 | if(blocked) waitUntil(() => !blocked, 3000); 31 | blocked = true; 32 | 33 | log(`\n\n\n====== LINT: ${type.toUpperCase()} ======`); 34 | 35 | // copy file so that unsaved changes are included 36 | let tmpfile = join(tmpdir(), basename(document.fileName) + '.evilint_tmp'); 37 | copyFileSync(document.fileName, tmpfile); 38 | writeFileSync(tmpfile, document.getText()); 39 | 40 | const dir = dirname(document.fileName); 41 | const eviExec = workspace.getConfiguration('evi').get('eviExecutablePath'); 42 | const workspacefolder = workspace.getWorkspaceFolder(document.uri).uri.fsPath; 43 | 44 | let pos = position ? `${position.line + 1}:${position.character}` : "0:0"; 45 | let cmd = `${eviExec} ${tmpfile} ` + 46 | `--include="${dir}" ` + 47 | `--lint-type="${type}" ` + 48 | `--lint-pos="${pos}" ` + 49 | `--lint-tab-width="${workspace.getConfiguration('editor').get('tabSize')}" `; 50 | workspace.getConfiguration('evi').get('includeSearchPaths').forEach(path => 51 | cmd += ` --include=${path}`.replace('${workspaceFolder}', workspacefolder)); 52 | 53 | log("lint cmd: " + cmd); 54 | 55 | let output: string; 56 | try { output = execSync(cmd).toString(); } 57 | catch (error) { console.warn(`Failed to execute Evi binary:\n\"${error}\"`); } 58 | if (tmpfile.endsWith('.evilint_tmp')) rmSync(tmpfile); 59 | 60 | blocked = false; 61 | 62 | // remove ansii escape codes 63 | output = output.replace(RegExp(String.fromCharCode(0x1b) + "\\[([0-9]+;)?[0-9]+m", "g"), ''); 64 | log("lint output: " + output); 65 | 66 | let data: any; 67 | try { data = JSON.parse(output); } 68 | catch (error) { console.warn(`Failed to parse data returned by Evi binary:\n"${error}"\nData: "${output}"`); return Promise.reject() } 69 | 70 | try { switch (type) 71 | { 72 | case eviLintType.getDeclaration: { 73 | if (data['invalid']) return undefined; 74 | 75 | let result: eviLintDeclaration = { 76 | position: { 77 | file: data['file'] == tmpfile ? document.fileName : data['file'], 78 | line: data['line'] - 1, 79 | column: data['column'], 80 | length: data['length'], 81 | }, 82 | }; 83 | 84 | log(result as any); 85 | return Promise.resolve(result); 86 | } 87 | case eviLintType.getDiagnostics: { 88 | let result: eviLintDiagnostics = { diagnostics: [] }; 89 | data.forEach((error: any) => { 90 | if(error['invalid']) return; 91 | 92 | // gather related information 93 | let related: { position: eviLintPosition, message: string }[] = []; 94 | 95 | error['related'].forEach((info) => { 96 | related.push({ 97 | position: { 98 | file: info['file'] == tmpfile ? document.fileName : info['file'], 99 | line: info['line'] - 1, 100 | column: info['column'], 101 | length: info['length'] > 0 ? info['length'] : document.lineAt(info['line'] - 1).range.end.character, 102 | }, 103 | message: info['message'], 104 | }); 105 | }); 106 | 107 | // create error itself 108 | result.diagnostics.push({ 109 | position: { 110 | file: error['file'] == tmpfile ? document.fileName : error['file'], 111 | line: error['line'] - 1, 112 | column: error['column'], 113 | length: error['length'], 114 | }, 115 | message: error['message'], 116 | type: error['type'], 117 | related: related, 118 | }); 119 | }); 120 | log(result as any); 121 | return Promise.resolve(result); 122 | } 123 | case eviLintType.getFunctions: { 124 | let result: eviLintFunctions = { functions: [] }; 125 | for (let func in data) { 126 | result.functions.push({ 127 | identifier: func, 128 | return_type: data[func]['return type'], 129 | parameters: data[func]['parameters'], 130 | variadic: data[func]['variadic'] 131 | }); 132 | } 133 | log(result as any); 134 | return Promise.resolve(result); 135 | } 136 | case eviLintType.getVariables: { 137 | let result: eviLintVariables = { variables: [] }; 138 | for (let varr in data) { 139 | result.variables.push({ 140 | identifier: varr, 141 | type: data[varr] 142 | }); 143 | } 144 | return Promise.resolve(result); 145 | } 146 | } } catch (e) { log(e); } 147 | } 148 | 149 | export interface FuncDocumentation { main: string, params: string[], ret?: string }; 150 | 151 | export async function getDocumentation(document: TextDocument, position: Position): Promise { 152 | // get defenition location of function 153 | const declaration: eviLintDeclaration = await callEviLint(document, eviLintType.getDeclaration, position); 154 | if (!declaration) return Promise.reject("Function declaration not found."); 155 | 156 | let delcdoc: TextDocument = await workspace.openTextDocument(Uri.file(declaration.position.file)); 157 | if (!delcdoc) return Promise.reject("Could not open file " + declaration.position.file); 158 | 159 | let it = new BackwardIterator(delcdoc, declaration.position.column, declaration.position.line); 160 | 161 | // get newline before declaration 162 | while (it.hasNext()) { if (it.next() == "\n") break; }; 163 | 164 | // get full documentation 165 | let doc: string = ""; 166 | while (it.hasNext()) { 167 | const c = it.next(); 168 | if (c == "\n" && !doc.startsWith("\\?")) { 169 | // end of documentation, remove last (non-doc) line 170 | doc = doc.slice(doc.indexOf("\\?")); 171 | 172 | // test if there's actually a documentation 173 | if(!doc.startsWith('\\?')) doc = ""; 174 | 175 | break; 176 | } 177 | doc = c + doc; 178 | } 179 | // replace comment tokens with just newlines 180 | doc = doc.replace(/\n?\\\?\s*/g, "\n") + "\n"; 181 | while (doc.startsWith("\n")) doc = doc.slice(1); 182 | 183 | 184 | // get parameters 185 | let params: string[] = []; 186 | const paramRegex = /\n\s*\@param\s+([0-9]+)\s+(.*)\n/; 187 | while (paramRegex.test(doc)) { 188 | const match = doc.match(paramRegex); 189 | doc = doc.replace(match[0], "\n"); 190 | params.push(match[2]); 191 | } 192 | 193 | // get return type 194 | let ret: string = undefined; 195 | const retRegex = /\n\s*\@return\s+(.*)\n/; 196 | if (retRegex.test(doc)) { 197 | const match = doc.match(retRegex); 198 | doc = doc.replace(match[0], "\n"); 199 | // doc += `\n*@return* - ${match[1]}`; 200 | ret = match[1]; 201 | } 202 | 203 | return Promise.resolve({ main: doc, params: params, ret: ret }); 204 | } 205 | 206 | export async function getDocumentationAsString(document: TextDocument, position: Position): Promise { 207 | const doc: FuncDocumentation = await getDocumentation(document, position); 208 | 209 | let text = doc.main 210 | for (let i = 0; i < doc.params.length; i++) 211 | text += `\n*@param* \`${i}\` - ${doc.params[i]}`; 212 | if (doc.ret) 213 | text += `\n*@return* - ${doc.ret}`; 214 | 215 | return new MarkdownString(text.trim().replaceAll("\n", " \\\n")); 216 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/utils/markedTextUtil.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownString, MarkedString } from 'vscode'; 2 | 3 | export function textToMarkedString(text: string): MarkdownString { 4 | return new MarkdownString(text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&')); 5 | // return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash 6 | } -------------------------------------------------------------------------------- /tools/vscode-extension/src/features/validationProvider.ts: -------------------------------------------------------------------------------- 1 | import { isAbsolute } from 'path'; 2 | import * as vscode from 'vscode'; 3 | import * as which from 'which'; 4 | import { ThrottledDelayer } from './utils/async'; 5 | import { callEviLint, eviLintType, eviLintDiagnostics } from './utils/eviLintUtil'; 6 | 7 | export default class EviValidationProvider { 8 | 9 | private validationEnabled: boolean; 10 | private pauseValidation: boolean; 11 | private config: IPhpConfig | undefined; 12 | private loadConfigP: Promise; 13 | 14 | private documentListener: vscode.Disposable | null = null; 15 | private diagnosticCollection?: vscode.DiagnosticCollection; 16 | private delayers?: { [key: string]: ThrottledDelayer }; 17 | 18 | constructor() { 19 | this.validationEnabled = true; 20 | this.pauseValidation = false; 21 | this.loadConfigP = this.loadConfiguration(); 22 | } 23 | 24 | public activate(subscriptions: vscode.Disposable[]) { 25 | this.pauseValidation = false; 26 | this.diagnosticCollection = vscode.languages.createDiagnosticCollection(); 27 | subscriptions.push(this); 28 | subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => this.loadConfigP = this.loadConfiguration())); 29 | 30 | vscode.workspace.onDidOpenTextDocument(this.triggerValidate, this, subscriptions); 31 | vscode.workspace.onDidCloseTextDocument((textDocument) => { 32 | this.diagnosticCollection!.delete(textDocument.uri); 33 | if (this.delayers) { 34 | delete this.delayers[textDocument.uri.toString()]; 35 | } 36 | }, null, subscriptions); 37 | } 38 | 39 | public dispose(): void { 40 | if (this.diagnosticCollection) { 41 | this.diagnosticCollection.clear(); 42 | this.diagnosticCollection.dispose(); 43 | } 44 | if (this.documentListener) { 45 | this.documentListener.dispose(); 46 | this.documentListener = null; 47 | } 48 | } 49 | 50 | private async loadConfiguration(): Promise { 51 | // const section = vscode.workspace.getConfiguration(); 52 | const oldExecutable = this.config?.executable; 53 | // this.validationEnabled = section.get(Setting.Enable, true); 54 | this.validationEnabled = true; 55 | 56 | this.config = await getConfig(); 57 | 58 | this.delayers = Object.create(null); 59 | if (this.pauseValidation) { 60 | this.pauseValidation = oldExecutable === this.config.executable; 61 | } 62 | if (this.documentListener) { 63 | this.documentListener.dispose(); 64 | this.documentListener = null; 65 | } 66 | this.diagnosticCollection!.clear(); 67 | if (this.validationEnabled) { 68 | this.documentListener = vscode.workspace.onDidChangeTextDocument((e) => { this.triggerValidate(e.document); }); 69 | // Configuration has changed. Reevaluate all documents. 70 | vscode.workspace.textDocuments.forEach(this.triggerValidate, this); 71 | } 72 | } 73 | 74 | private async triggerValidate(textDocument: vscode.TextDocument): Promise { 75 | await this.loadConfigP; 76 | if (textDocument.languageId !== 'evi' || this.pauseValidation || !this.validationEnabled) return; 77 | 78 | let key = textDocument.uri.toString(); 79 | let delayer = this.delayers![key]; 80 | if (!delayer) { 81 | // delayer = new ThrottledDelayer(this.config?.trigger === RunTrigger.onType ? 250 : 0); 82 | delayer = new ThrottledDelayer(250); 83 | this.delayers![key] = delayer; 84 | } 85 | 86 | delayer.trigger(() => this.doValidate(textDocument)); 87 | } 88 | 89 | private doValidate(textDocument: vscode.TextDocument): Promise { 90 | return new Promise(resolve => { 91 | const executable = this.config!.executable; 92 | if (!executable) { 93 | this.showErrorMessage('Cannot validate since a Evi installation could not be found. Use the setting \'evi.eviExecutablePath\' to configure the Evi executable.'); 94 | this.pauseValidation = true; 95 | resolve(); 96 | return; 97 | } 98 | 99 | if (!isAbsolute(executable)) { 100 | // executable should either be resolved to an absolute path or undefined. 101 | // This is just to be sure. 102 | return; 103 | } 104 | 105 | let diagnostics: { [file: string]: vscode.Diagnostic[] } = {}; 106 | callEviLint(textDocument, eviLintType.getDiagnostics).then((result: eviLintDiagnostics) => { 107 | // success 108 | this.diagnosticCollection.clear(); 109 | 110 | result.diagnostics.forEach(diagnostic => { 111 | if(!diagnostics[diagnostic.position.file]) diagnostics[diagnostic.position.file] = []; 112 | 113 | const start: vscode.Position = new vscode.Position(diagnostic.position.line, diagnostic.position.column); 114 | const end: vscode.Position = start.translate(0, diagnostic.position.length); 115 | 116 | let vsdiagnostic: vscode.Diagnostic = new vscode.Diagnostic(new vscode.Range(start, end), diagnostic.message); 117 | vsdiagnostic.source = 'evi'; 118 | // vsdiagnostic.code = some code for CodeActions, 119 | vsdiagnostic.relatedInformation = []; 120 | 121 | // add related information 122 | diagnostic.related.forEach(info => { 123 | const start: vscode.Position = new vscode.Position(info.position.line, info.position.column); 124 | const end: vscode.Position = start.translate(0, info.position.length); 125 | 126 | vsdiagnostic.relatedInformation.push({ 127 | location: { uri: vscode.Uri.file(info.position.file), range: new vscode.Range(start, end) }, 128 | message: info.message, 129 | }); 130 | }); 131 | 132 | // set severity 133 | if(diagnostic.type == "error") vsdiagnostic.severity = vscode.DiagnosticSeverity.Error; 134 | else if(diagnostic.type == "warning") vsdiagnostic.severity = vscode.DiagnosticSeverity.Warning; 135 | 136 | diagnostics[diagnostic.position.file].push(vsdiagnostic); 137 | }); 138 | 139 | for (let file in diagnostics) this.diagnosticCollection.set(vscode.Uri.file(file), diagnostics[file]); 140 | resolve(); 141 | 142 | }).catch((error) => { 143 | // failure 144 | console.warn(`eviLint failure in doValidate: ${error}`); 145 | this.pauseValidation = true; 146 | resolve(); 147 | }); 148 | }); 149 | } 150 | 151 | private async showError(error: any, executable: string): Promise { 152 | let message: string = error.message ? error.message : `Failed to run evi using path: ${executable}. Reason is unknown.`; 153 | if (!message) return; 154 | 155 | return this.showErrorMessage(message); 156 | } 157 | 158 | private async showErrorMessage(message: string): Promise { 159 | const openSettings = 'Open Settings'; 160 | if (await vscode.window.showInformationMessage(message, openSettings) === openSettings) { 161 | vscode.commands.executeCommand('workbench.action.openSettings', 'evi.eviExecutablePath'); 162 | } 163 | } 164 | } 165 | 166 | interface IPhpConfig { 167 | readonly executable: string | undefined; 168 | readonly executableIsUserDefined: boolean | undefined; 169 | // readonly trigger: RunTrigger; 170 | } 171 | 172 | async function getConfig(): Promise { 173 | const section = vscode.workspace.getConfiguration(); 174 | 175 | let executable: string | undefined; 176 | let executableIsUserDefined: boolean | undefined; 177 | const inspect = section.inspect('evi.eviExecutablePath'); 178 | if (inspect && inspect.workspaceValue) { 179 | executable = inspect.workspaceValue; 180 | executableIsUserDefined = false; 181 | } else if (inspect && inspect.globalValue) { 182 | executable = inspect.globalValue; 183 | executableIsUserDefined = true; 184 | } else { 185 | executable = undefined; 186 | executableIsUserDefined = undefined; 187 | } 188 | 189 | if (executable && !isAbsolute(executable)) { 190 | const first = vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]; 191 | if (first) { 192 | executable = vscode.Uri.joinPath(first.uri, executable).fsPath; 193 | } else { 194 | executable = undefined; 195 | } 196 | } else if (!executable) { 197 | executable = await getEviPath(); 198 | } 199 | 200 | // const trigger = RunTrigger.from(section.get(Setting.Run, RunTrigger.strings.onSave)); 201 | // const trigger = RunTrigger.onType; 202 | return { 203 | executable, 204 | executableIsUserDefined, 205 | // trigger 206 | }; 207 | } 208 | 209 | async function getEviPath(): Promise { 210 | try { return await which('evi'); 211 | } catch (e) { return undefined; } 212 | } -------------------------------------------------------------------------------- /tools/vscode-extension/syntaxes/evi.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "include": 3 | { 4 | "scope": "evi", 5 | "prefix": ["#apply", "inc"], 6 | "body": "#apply \"$1\"" 7 | }, 8 | "flag": 9 | { 10 | "scope": "evi", 11 | "prefix": ["#flag", "flag"], 12 | "body": "#flag $1" 13 | }, 14 | "line": 15 | { 16 | "scope": "evi", 17 | "prefix": ["#line", "line"], 18 | "body": "#line ${1:$TM_LINE_NUMBER}" 19 | }, 20 | "file": 21 | { 22 | "scope": "evi", 23 | "prefix": ["#file", "file"], 24 | "body": "#file \"${1:$TM_FILENAME}\"" 25 | }, 26 | "loop": 27 | { 28 | "scope": "evi", 29 | "prefix": "for", 30 | "body": "!!($1;$2;$3;) $4" 31 | }, 32 | "if": 33 | { 34 | "scope": "evi", 35 | "prefix": "if", 36 | "body": "??($1) $2 :: $3" 37 | }, 38 | "func": 39 | { 40 | "scope": "evi", 41 | "prefix": "func", 42 | "body": "@${1:func} ${2:i32} ($3)\n" 43 | }, 44 | "var": 45 | { 46 | "scope": "evi", 47 | "prefix": "var", 48 | "body": "%${1:var} ${2:i32}" 49 | } 50 | } -------------------------------------------------------------------------------- /tools/vscode-extension/syntaxes/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "\\", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "\\:", ":\\" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | { "open": "{", "close": "}" }, 17 | { "open": "[", "close": "]" }, 18 | { "open": "(", "close": ")" }, 19 | { "open": "'", "close": "'", "notIn": ["string", "comment"] }, 20 | { "open": "\"", "close": "\"", "notIn": ["string"] }, 21 | { "open": "\\:", "close": ":\\", "notIn": ["string", "comment"] } 22 | ], 23 | // symbols that can be used to surround a selection 24 | "surroundingPairs": [ 25 | ["{", "}"], 26 | ["[", "]"], 27 | ["(", ")"], 28 | ["\"", "\""], 29 | ["'", "'"] 30 | ], 31 | 32 | "folding": { 33 | "markers": { 34 | "start": "^\\s*#info region", 35 | "end": "^\\s*#info endregion" 36 | }, 37 | }, 38 | 39 | "indentationRules": { 40 | "increaseIndentPattern": "{", 41 | "decreaseIndentPattern": "}" 42 | }, 43 | 44 | // nums hex oct bin dec strings identifiers params symbols 45 | "wordPattern": "(-?(?:0(x|X)[0-f]+)|(0(c|C)[0-7]+)|(0(b|B)[0-1]+)|([0-9]+(\\.[0-9]+)?))|(\\\"(\\\\?.*)\\\")|((\\$|\\#)?[a-zA-Z_]+[a-zA-Z0-9_]*)|(\\$[0-9]+)|(@|%|=|\\$|\\#|\\~|\\?{1,2}|:{1,2}|\\!{1,2})" 46 | } -------------------------------------------------------------------------------- /tools/vscode-extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./out", 4 | "types": [ 5 | "node" 6 | ], 7 | "lib": [ 8 | "ES2021.String" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*" 13 | ] 14 | } --------------------------------------------------------------------------------