diff options
| -rw-r--r-- | .gitignore | 11 | ||||
| -rw-r--r-- | Dockerfile | 11 | ||||
| -rw-r--r-- | README.md | 25 | ||||
| -rw-r--r-- | meson.build | 79 | ||||
| -rw-r--r-- | premake5.lua | 178 | ||||
| -rwxr-xr-x | scripts/run.sh | 6 | ||||
| -rwxr-xr-x | scripts/run_suite.sh | 6 | ||||
| -rwxr-xr-x | scripts/test_examples.sh | 4 | ||||
| -rw-r--r-- | src/args.c (renamed from src/cli/args.c) | 2 | ||||
| -rw-r--r-- | src/args.h (renamed from src/cli/args.h) | 0 | ||||
| -rw-r--r-- | src/ast.c (renamed from src/parser/ast.c) | 2 | ||||
| -rw-r--r-- | src/ast.h (renamed from src/parser/ast.h) | 2 | ||||
| -rw-r--r-- | src/diag.c (renamed from src/diag/diag.c) | 2 | ||||
| -rw-r--r-- | src/diag.h (renamed from src/diag/diag.h) | 2 | ||||
| -rw-r--r-- | src/file.c (renamed from src/io/file.c) | 4 | ||||
| -rw-r--r-- | src/file.h (renamed from src/io/file.h) | 0 | ||||
| -rw-r--r-- | src/lexer.c (renamed from src/lexer/lexer.c) | 2 | ||||
| -rw-r--r-- | src/lexer.h (renamed from src/lexer/lexer.h) | 0 | ||||
| -rw-r--r-- | src/main.c | 14 | ||||
| -rw-r--r-- | src/nasm.c (renamed from src/codegen/nasm.c) | 2 | ||||
| -rw-r--r-- | src/nasm.h (renamed from src/codegen/nasm.h) | 2 | ||||
| -rw-r--r-- | src/parser.c (renamed from src/parser/parser.c) | 4 | ||||
| -rw-r--r-- | src/parser.h (renamed from src/parser/parser.h) | 4 | ||||
| -rw-r--r-- | src/sema.c (renamed from src/sema/sema.c) | 2 | ||||
| -rw-r--r-- | src/sema.h (renamed from src/sema/sema.h) | 4 | ||||
| -rw-r--r-- | tests/codegen_test.c | 4 | ||||
| -rw-r--r-- | tests/lexer_test.c | 2 | ||||
| -rw-r--r-- | tests/parser_test.c | 2 | ||||
| -rw-r--r-- | tests/sema_test.c | 4 |
29 files changed, 136 insertions, 244 deletions
@@ -1,15 +1,8 @@ -bin/ -bin-int/ - -Makefile -*.make -*.sln -*.vcxproj -*.vcxproj.* +build/ +build-*/ compile_commands.json compile_flags.txt .clangd .DS_Store - @@ -1,18 +1,11 @@ FROM --platform=linux/amd64 debian:trixie-slim -ARG PREMAKE_VERSION=5.0.0-beta8 - RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ nasm \ binutils \ - make \ - curl \ - ca-certificates \ + meson \ + ninja-build \ && rm -rf /var/lib/apt/lists/* -RUN curl -fsSL "https://github.com/premake/premake-core/releases/download/v${PREMAKE_VERSION}/premake-${PREMAKE_VERSION}-linux.tar.gz" \ - | tar -xz -C /usr/local/bin \ - && chmod +x /usr/local/bin/premake5 - WORKDIR /hdass @@ -37,18 +37,23 @@ The program still directly controls the registers used for the system calls. Not The full language reference, shit like directives, declarations, statements, expressions, registers and extensions, is explained in [docs/language.md](docs/language.md). ## Building -The build is driven by [premake5](https://premake.github.io/). Generate the makefiles and build the compiler: +The build is driven by [Meson](https://mesonbuild.com/). Configure a build directory and compile the compiler: ```bash -premake5 gmake -make config=debug +meson setup build +meson compile -C build ``` -This produces the `hdass` binary at `bin/<config>-<system>/hdass` (for example `bin/debug-macosx/hdass` or `bin/debug-linux/hdass`). The available configurations are `debug`, `release` and `dist`. To run the unit tests: +This produces the `hdass` binary at `build/hdass`. The default build turns on the address and undefined-behaviour sanitizers; for an optimised build without them, configure a separate directory: ```bash -make config=debug -./bin/<config>-<system>/tests +meson setup build-release --buildtype=release -Db_sanitize=none +meson compile -C build-release ``` +To run the unit tests: +```bash +meson test -C build +``` +If [cppcheck](https://cppcheck.sourceforge.io/) is installed, `ninja -C build cppcheck` runs static analysis over the sources. -hdass emits x86-64 assembly, so to actually assemble and run its output you need an x86-64 Linux toolchain. The bundled Docker environment provides a consistent one on any host, including Apple Silicon, where the amd64 image runs under emulation. The image is a Debian base with `nasm`, `ld` (binutils), a C toolchain and `premake5` preinstalled. +hdass emits x86-64 assembly, so to actually assemble and run its output you need an x86-64 Linux toolchain. The bundled Docker environment provides a consistent one on any host, including Apple Silicon, where the amd64 image runs under emulation. The image is a Debian base with `nasm`, `ld` (binutils), a C toolchain and Meson/Ninja preinstalled. Start the container (this builds the image the first time): ```bash @@ -62,8 +67,8 @@ docker compose exec hdass bash From inside the container you can build the compiler and take a program all the way to a running executable: ```bash -premake5 gmake && make config=debug -./bin/debug-linux/hdass examples/hello_world.hdass -o hello.asm +meson setup build-linux && meson compile -C build-linux +./build-linux/hdass examples/hello_world.hdass -o hello.asm nasm -f elf64 hello.asm -o hello.o ld -e main hello.o -o hello ./hello @@ -71,7 +76,7 @@ ld -e main hello.o -o hello To transpile, assemble, link and run every program in [examples/](examples/) and check its output, use the end-to-end test script (also from inside the container): ```bash -make config=debug && ./scripts/test_examples.sh +meson setup build-linux && meson compile -C build-linux && ./scripts/test_examples.sh ``` Or, from the host, run the whole suite (unit tests plus the example tests) in one shot, bringing the container up if needed: diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..b445e94 --- /dev/null +++ b/meson.build @@ -0,0 +1,79 @@ +project( + 'hdass', + 'c', + version: '0.1.0', + license: 'MIT', + meson_version: '>=1.0.0', + default_options: [ + 'c_std=c17', + 'warning_level=3', + 'b_sanitize=address,undefined', + 'b_lundef=false', + ], +) + +cc = meson.get_compiler('c') + +add_project_arguments( + cc.get_supported_arguments( + '-Wconversion', + '-Wsign-conversion', + '-Wshadow', + '-Wstrict-prototypes', + '-Wmissing-prototypes', + '-fno-omit-frame-pointer', + ), + language: 'c', +) + +src_inc = include_directories('src') + +core = static_library( + 'hdass_core', + files( + 'src/args.c', + 'src/ast.c', + 'src/diag.c', + 'src/file.c', + 'src/lexer.c', + 'src/nasm.c', + 'src/parser.c', + 'src/sema.c', + ), + include_directories: src_inc, +) + +executable( + 'hdass', + 'src/main.c', + include_directories: src_inc, + link_with: core, +) + +tests = executable( + 'tests', + files( + 'tests/main.c', + 'tests/codegen_test.c', + 'tests/lexer_test.c', + 'tests/parser_test.c', + 'tests/sema_test.c', + ), + include_directories: [src_inc, include_directories('tests')], + link_with: core, +) + +test('unit', tests) + +cppcheck = find_program('cppcheck', required: false) +if cppcheck.found() + run_target( + 'cppcheck', + command: [ + cppcheck, + '--project=@0@'.format(meson.project_build_root() / 'compile_commands.json'), + '--enable=warning,style,performance,portability', + '--error-exitcode=1', + ], + ) +endif diff --git a/premake5.lua b/premake5.lua deleted file mode 100644 index 178c730..0000000 --- a/premake5.lua +++ /dev/null @@ -1,178 +0,0 @@ -output_dir = "%{cfg.buildcfg}-%{cfg.system}" - -function setup_target() - targetdir ("bin/" .. output_dir) - objdir ("bin-int/" .. output_dir .. "/%{prj.name}") - staticruntime "On" -end - -function setup_c_target() - setup_target() - - filter { "system:macosx or system:linux" } - buildoptions { - "-Wall", - "-Wextra", - "-Wpedantic", - "-Wconversion", - "-Wsign-conversion", - "-Wshadow", - "-Wstrict-prototypes", - "-Wmissing-prototypes", - } - filter {} - - filter "configurations:debug" - runtime "Debug" - symbols "On" - - filter { "system:macosx or system:linux" } - buildoptions { - "-g", - "-fno-omit-frame-pointer", - "-fsanitize=address,undefined", - } - - linkoptions { - "-fsanitize=address,undefined", - } - filter {} - - filter { "configurations:release", "configurations:dist" } - runtime "Release" - optimize "Speed" - filter {} - - filter "configurations:dist" - symbols "Off" - filter {} -end - -newaction { - trigger = "clean", - description = "Remove build output and generated project files", - - execute = function() - os.rmdir("bin") - os.rmdir("bin-int") - os.remove("Makefile") - - for _, file in ipairs(os.matchfiles("*.make")) do - os.remove(file) - end - - print("Cleaned build output and generated project files") - end -} - -newaction { - trigger = "check", - description = "Run static analysis with cppcheck", - - execute = function() - local result = os.execute("make clean") - - if result ~= true and result ~= 0 then - error("Failed to clean project") - end - - result = os.execute( - "bear -- make config=debug" - ) - - if result ~= true and result ~= 0 then - error("Failed to generate compile_commands.json") - end - - result = os.execute( - "cppcheck " .. - "--project=compile_commands.json " .. - "--file-filter=src/** " .. - "--file-filter=tests/** " .. - "--enable=warning,style,performance,portability " .. - "--error-exitcode=1" - ) - - if result ~= true and result ~= 0 then - error("cppcheck found issues") - end - end -} - -workspace "hdass" - architecture "x64" - startproject "hdass" - multiprocessorcompile "On" - - configurations { - "debug", - "release", - "dist", - } - - filter "system:windows" - defines "HDASS_WINDOWS" - - filter "system:linux" - defines "HDASS_LINUX" - - filter "system:macosx" - architecture "ARM64" - defines "HDASS_MACOS" - - filter {} - -project "hdass" - kind "ConsoleApp" - language "C" - cdialect "C17" - - setup_c_target() - - files { - "src/**.h", - "src/**.c", - } - - includedirs "src" - - filter "system:windows" - systemversion "latest" - - filter "configurations:debug" - defines "HDASS_DEBUG" - - filter "configurations:release" - defines "HDASS_RELEASE" - - filter "configurations:dist" - defines "HDASS_DIST" - - filter {} - -project "tests" - kind "ConsoleApp" - language "C" - cdialect "C17" - - setup_c_target() - - files { - "src/**.h", - "src/**.c", - "tests/**.h", - "tests/**.c", - } - - removefiles "src/main.c" - - includedirs { - "src", - "tests", - } - - filter "system:windows" - systemversion "latest" - - filter {} - diff --git a/scripts/run.sh b/scripts/run.sh index 01d3882..2bdb450 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -17,9 +17,9 @@ docker compose up -d >/dev/null docker compose exec -T -e SRC="$file" -e NAME="$name" hdass bash -c ' set -e cd /hdass - premake5 gmake >/dev/null - make config=debug >/dev/null - ./bin/debug-linux/hdass "$SRC" -o "/tmp/$NAME.asm" + [ -d build-linux ] || meson setup build-linux >/dev/null + meson compile -C build-linux >/dev/null + ./build-linux/hdass "$SRC" -o "/tmp/$NAME.asm" echo cat "/tmp/$NAME.asm" echo diff --git a/scripts/run_suite.sh b/scripts/run_suite.sh index 48cf67f..38598ad 100755 --- a/scripts/run_suite.sh +++ b/scripts/run_suite.sh @@ -10,7 +10,7 @@ else fi printf '%s━━ building compiler ━━%s\n' "$bold" "$reset" -if ! build=$(premake5 gmake 2>&1 && make config=debug 2>&1); then +if ! build=$({ [ -d build-linux ] || meson setup build-linux; } 2>&1 && meson compile -C build-linux 2>&1); then printf '%s\n' "$build" printf ' %s✘ build failed%s\n' "$red" "$reset" exit 1 @@ -18,11 +18,11 @@ fi printf ' %s✔ compiler built%s\n\n' "$green" "$reset" printf '%s━━ unit tests ━━%s\n' "$bold" "$reset" -if units=$(./bin/debug-linux/tests 2>/dev/null); then +if units=$(./build-linux/tests 2>/dev/null); then printf ' %s✔%s %s\n\n' "$green" "$reset" "$(printf '%s' "$units" | tail -1)" else printf '%s\n' "$units" - ./bin/debug-linux/tests || true + ./build-linux/tests || true printf ' %s✘ unit tests failed%s\n' "$red" "$reset" exit 1 fi diff --git a/scripts/test_examples.sh b/scripts/test_examples.sh index c13ee1d..f8d1462 100755 --- a/scripts/test_examples.sh +++ b/scripts/test_examples.sh @@ -10,10 +10,10 @@ else bold=; dim=; red=; green=; reset= fi -hdass="${HDASS:-./bin/debug-linux/hdass}" +hdass="${HDASS:-./build-linux/hdass}" if [ ! -x "$hdass" ]; then echo "${red}error:${reset} '$hdass' not found; build it first with:" >&2 - echo " premake5 gmake && make config=debug" >&2 + echo " meson setup build-linux && meson compile -C build-linux" >&2 exit 1 fi diff --git a/src/cli/args.c b/src/args.c index 501406f..b31def5 100644 --- a/src/cli/args.c +++ b/src/args.c @@ -2,7 +2,7 @@ #include <string.h> #include <stdbool.h> -#include "cli/args.h" +#include "args.h" #define HDASS_VERSION "0.1.0" diff --git a/src/cli/args.h b/src/args.h index f80b736..f80b736 100644 --- a/src/cli/args.h +++ b/src/args.h diff --git a/src/parser/ast.c b/src/ast.c index 63d8a08..28bb98e 100644 --- a/src/parser/ast.c +++ b/src/ast.c @@ -1,6 +1,6 @@ #include <stdlib.h> -#include "parser/ast.h" +#include "ast.h" void free_expr(struct Expr* expr) { diff --git a/src/parser/ast.h b/src/ast.h index 288bddf..074c9b2 100644 --- a/src/parser/ast.h +++ b/src/ast.h @@ -4,7 +4,7 @@ #include <stdint.h> #include <stdbool.h> -#include "lexer/lexer.h" +#include "lexer.h" enum StoreSize { diff --git a/src/diag/diag.c b/src/diag.c index cc61e89..6962046 100644 --- a/src/diag/diag.c +++ b/src/diag.c @@ -2,7 +2,7 @@ #include <stdlib.h> #include <stdbool.h> -#include "diag/diag.h" +#include "diag.h" static bool colored(void) { diff --git a/src/diag/diag.h b/src/diag.h index 7a99266..e79b465 100644 --- a/src/diag/diag.h +++ b/src/diag.h @@ -1,6 +1,6 @@ #pragma once -#include "lexer/lexer.h" +#include "lexer.h" struct Source { diff --git a/src/io/file.c b/src/file.c index 3d7083c..a122468 100644 --- a/src/io/file.c +++ b/src/file.c @@ -1,8 +1,8 @@ #include <stdio.h> #include <stdlib.h> -#include "diag/diag.h" -#include "io/file.h" +#include "diag.h" +#include "file.h" static void report_file_error(const char* verb, const char* path) { diff --git a/src/io/file.h b/src/file.h index 7d15bd0..7d15bd0 100644 --- a/src/io/file.h +++ b/src/file.h diff --git a/src/lexer/lexer.c b/src/lexer.c index 4d468e6..0d26f9a 100644 --- a/src/lexer/lexer.c +++ b/src/lexer.c @@ -1,7 +1,7 @@ #include <string.h> #include <stdbool.h> -#include "lexer/lexer.h" +#include "lexer.h" static bool is_alpha(char character) { diff --git a/src/lexer/lexer.h b/src/lexer.h index 73de02f..73de02f 100644 --- a/src/lexer/lexer.h +++ b/src/lexer.h @@ -1,12 +1,12 @@ #include <stdio.h> -#include "io/file.h" -#include "cli/args.h" -#include "diag/diag.h" -#include "sema/sema.h" -#include "lexer/lexer.h" -#include "codegen/nasm.h" -#include "parser/parser.h" +#include "file.h" +#include "args.h" +#include "diag.h" +#include "sema.h" +#include "lexer.h" +#include "nasm.h" +#include "parser.h" int main(int argc, char** argv) { diff --git a/src/codegen/nasm.c b/src/nasm.c index 198cb5c..15f5f01 100644 --- a/src/codegen/nasm.c +++ b/src/nasm.c @@ -5,7 +5,7 @@ #include <string.h> #include <stdbool.h> -#include "codegen/nasm.h" +#include "nasm.h" static void emit_const_expr(struct Expr* expr, FILE* out) { diff --git a/src/codegen/nasm.h b/src/nasm.h index b9c2c90..1a93b31 100644 --- a/src/codegen/nasm.h +++ b/src/nasm.h @@ -2,6 +2,6 @@ #include <stdio.h> -#include "parser/ast.h" +#include "ast.h" void generate_nasm(struct Program* program, FILE* out); diff --git a/src/parser/parser.c b/src/parser.c index 97b8569..4c01c86 100644 --- a/src/parser/parser.c +++ b/src/parser.c @@ -2,8 +2,8 @@ #include <stdlib.h> #include <string.h> -#include "diag/diag.h" -#include "parser/parser.h" +#include "diag.h" +#include "parser.h" struct Parser { diff --git a/src/parser/parser.h b/src/parser.h index 82d306a..82a3019 100644 --- a/src/parser/parser.h +++ b/src/parser.h @@ -2,7 +2,7 @@ #include <stdbool.h> -#include "lexer/lexer.h" -#include "parser/ast.h" +#include "ast.h" +#include "lexer.h" bool parse_program(struct Lexer* lexer, struct Program* out); diff --git a/src/sema/sema.c b/src/sema.c index 748ccbe..12f2689 100644 --- a/src/sema/sema.c +++ b/src/sema.c @@ -3,7 +3,7 @@ #include <string.h> #include <stdarg.h> -#include "sema/sema.h" +#include "sema.h" static bool token_is(struct Token token, const char* text) { diff --git a/src/sema/sema.h b/src/sema.h index 468d1be..f28c49c 100644 --- a/src/sema/sema.h +++ b/src/sema.h @@ -2,7 +2,7 @@ #include <stdbool.h> -#include "diag/diag.h" -#include "parser/ast.h" +#include "ast.h" +#include "diag.h" bool analyze_program(struct Source source, struct Program* program); diff --git a/tests/codegen_test.c b/tests/codegen_test.c index e2969db..1da711c 100644 --- a/tests/codegen_test.c +++ b/tests/codegen_test.c @@ -1,9 +1,9 @@ #include <stdio.h> #include <string.h> -#include "parser/parser.h" -#include "codegen/nasm.h" +#include "nasm.h" #include "tests.h" +#include "parser.h" static void generate_to_buffer(struct Program* program, char* buffer, size_t size) { diff --git a/tests/lexer_test.c b/tests/lexer_test.c index a44b1f0..0810c3b 100644 --- a/tests/lexer_test.c +++ b/tests/lexer_test.c @@ -1,7 +1,7 @@ #include <string.h> #include <stdbool.h> -#include "lexer/lexer.h" +#include "lexer.h" #include "tests.h" static bool token_matches(struct Token token, enum TokenType type, const char* text) diff --git a/tests/parser_test.c b/tests/parser_test.c index e1199eb..b51093e 100644 --- a/tests/parser_test.c +++ b/tests/parser_test.c @@ -1,8 +1,8 @@ #include <string.h> #include <stdbool.h> -#include "parser/parser.h" #include "tests.h" +#include "parser.h" static bool text_is(struct Token token, const char* text) { diff --git a/tests/sema_test.c b/tests/sema_test.c index 643ffdf..3c12001 100644 --- a/tests/sema_test.c +++ b/tests/sema_test.c @@ -1,8 +1,8 @@ #include <stdbool.h> -#include "parser/parser.h" -#include "sema/sema.h" +#include "sema.h" #include "tests.h" +#include "parser.h" static bool analyze_source(const char* source) { |
