Merge branch 'master' into terminal
This commit is contained in:
commit
98f6ba8eab
|
|
@ -1,80 +0,0 @@
|
||||||
# Makefile for SLS project with automatic header dependencies
|
|
||||||
|
|
||||||
CC ?= gcc
|
|
||||||
CFLAGS ?= -std=c99 -Wall -Wextra -Werror -g -Iinclude -MMD -MP
|
|
||||||
LDFLAGS ?=
|
|
||||||
CTESTFLAGS ?= -std=c99 -Wall -Wextra -Wno-unused-function -Werror -g -O0 -Iinclude -MMD -MP
|
|
||||||
|
|
||||||
GIT_COMMIT := $(shell git describe --always --dirty --abbrev=7)
|
|
||||||
CFLAGS += -DGIT_COMMIT_HASH="\"$(GIT_COMMIT)\""
|
|
||||||
|
|
||||||
SRCDIR := src
|
|
||||||
OBJDIR := obj
|
|
||||||
BINDIR := bin
|
|
||||||
TESTDIR := tests
|
|
||||||
|
|
||||||
TARGET := $(BINDIR)/sls
|
|
||||||
TEST_TARGET := $(BINDIR)/sls_tests
|
|
||||||
|
|
||||||
SOURCES := $(wildcard $(SRCDIR)/*.c)
|
|
||||||
OBJECTS := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SOURCES))
|
|
||||||
NON_MAIN_OBJECTS := $(filter-out $(OBJDIR)/main.o,$(OBJECTS))
|
|
||||||
|
|
||||||
TEST_SOURCES := $(wildcard $(TESTDIR)/*.c)
|
|
||||||
TEST_OBJECTS := $(patsubst $(TESTDIR)/%.c,$(OBJDIR)/%.o,$(TEST_SOURCES))
|
|
||||||
|
|
||||||
# Include dependency files if they exist
|
|
||||||
-include $(OBJECTS:.o=.d) $(TEST_OBJECTS:.o=.d)
|
|
||||||
|
|
||||||
.PHONY: all build run test clean
|
|
||||||
|
|
||||||
# Default: build main program
|
|
||||||
all: $(TARGET)
|
|
||||||
|
|
||||||
# Compile object files
|
|
||||||
build: $(OBJECTS)
|
|
||||||
|
|
||||||
# Rule to compile .c -> .o (handles both src and tests)
|
|
||||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
$(OBJDIR)/%.o: $(TESTDIR)/%.c | $(OBJDIR)
|
|
||||||
$(CC) $(CTESTFLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
# Link main program
|
|
||||||
$(TARGET): $(OBJECTS) | $(BINDIR)
|
|
||||||
$(CC) $(LDFLAGS) $^ -o $@ -lm
|
|
||||||
|
|
||||||
# Run main program
|
|
||||||
run: $(TARGET)
|
|
||||||
@echo "Running $(TARGET)..."
|
|
||||||
./$(TARGET)
|
|
||||||
|
|
||||||
test_cases: ../SLS_Tests/yaml_to_c_tests.py ../SLS_Tests/cases.yaml
|
|
||||||
python3 ../SLS_Tests/yaml_to_c_tests.py ../SLS_Tests/cases.yaml ./tests/lexer_tests.c
|
|
||||||
|
|
||||||
# Build test runner executable
|
|
||||||
$(TEST_TARGET): $(TEST_OBJECTS) $(NON_MAIN_OBJECTS) | $(BINDIR)
|
|
||||||
$(CC) $(LDFLAGS) $^ -o $@ -lm
|
|
||||||
|
|
||||||
build_tests: test_cases $(TEST_TARGET)
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
debug: build_tests
|
|
||||||
gdb ./$(TEST_TARGET)
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
test: test_cases $(TEST_TARGET)
|
|
||||||
@echo "Running tests..."
|
|
||||||
./$(TEST_TARGET)
|
|
||||||
|
|
||||||
# Create directories if missing
|
|
||||||
$(BINDIR):
|
|
||||||
mkdir -p $(BINDIR)
|
|
||||||
|
|
||||||
$(OBJDIR):
|
|
||||||
mkdir -p $(OBJDIR)
|
|
||||||
|
|
||||||
# Remove build artifacts
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJDIR) $(BINDIR)
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
# MSVC nmake build file for SLS
|
|
||||||
|
|
||||||
CC = cl
|
|
||||||
CFLAGS = /std:c11 /Zi /I include
|
|
||||||
LDFLAGS =
|
|
||||||
|
|
||||||
SRCDIR = src
|
|
||||||
OBJDIR = obj
|
|
||||||
BINDIR = bin
|
|
||||||
TESTDIR = tests
|
|
||||||
|
|
||||||
TARGET = $(BINDIR)\sls.exe
|
|
||||||
TEST_TARGET = $(BINDIR)\sls_tests.exe
|
|
||||||
|
|
||||||
# === LIST FILES MANUALLY (edit as needed) ===
|
|
||||||
SOURCES = \
|
|
||||||
$(SRCDIR)\main.c \
|
|
||||||
$(SRCDIR)\repl.c \
|
|
||||||
$(SRCDIR)\file.c \
|
|
||||||
$(SRCDIR)\lexer.c \
|
|
||||||
$(SRCDIR)\string.c
|
|
||||||
|
|
||||||
OBJECTS = \
|
|
||||||
$(OBJDIR)\main.obj \
|
|
||||||
$(OBJDIR)\repl.obj \
|
|
||||||
$(OBJDIR)\file.obj \
|
|
||||||
$(OBJDIR)\lexer.obj \
|
|
||||||
$(OBJDIR)\string.obj
|
|
||||||
|
|
||||||
TEST_SOURCES = \
|
|
||||||
$(TESTDIR)\extra_tests.c \
|
|
||||||
$(TESTDIR)\lexer_test_helpers.c \
|
|
||||||
$(TESTDIR)\lexer_tests.c \
|
|
||||||
$(TESTDIR)\string_tests.c \
|
|
||||||
$(TESTDIR)\tests.c
|
|
||||||
|
|
||||||
TEST_OBJECTS = \
|
|
||||||
$(OBJDIR)\extra_tests.obj \
|
|
||||||
$(OBJDIR)\lexer_test_helpers.obj \
|
|
||||||
$(OBJDIR)\lexer_tests.obj \
|
|
||||||
$(OBJDIR)\string_tests.obj \
|
|
||||||
$(OBJDIR)\tests.obj
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Default target
|
|
||||||
all: build
|
|
||||||
|
|
||||||
# === BUILD MAIN PROGRAM ===
|
|
||||||
build: $(TARGET)
|
|
||||||
|
|
||||||
$(TARGET): dirs $(OBJECTS)
|
|
||||||
link $(OBJECTS) /OUT:$@ $(LDFLAGS)
|
|
||||||
|
|
||||||
# === COMPILE RULES ===
|
|
||||||
$(OBJDIR)\main.obj: $(SRCDIR)\main.c
|
|
||||||
$(CC) $(CFLAGS) /c $(SRCDIR)\main.c /Fo$(OBJDIR)\main.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\repl.obj: $(SRCDIR)\repl.c
|
|
||||||
$(CC) $(CFLAGS) /c $(SRCDIR)\repl.c /Fo$(OBJDIR)\repl.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\file.obj: $(SRCDIR)\file.c
|
|
||||||
$(CC) $(CFLAGS) /c $(SRCDIR)\file.c /Fo$(OBJDIR)\file.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\lexer.obj: $(SRCDIR)\lexer.c
|
|
||||||
$(CC) $(CFLAGS) /c $(SRCDIR)\lexer.c /Fo$(OBJDIR)\lexer.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\string.obj: $(SRCDIR)\string.c
|
|
||||||
$(CC) $(CFLAGS) /c $(SRCDIR)\string.c /Fo$(OBJDIR)\string.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\extra_tests.obj: $(TESTDIR)\extra_tests.c
|
|
||||||
$(CC) $(CFLAGS) /c $(TESTDIR)\extra_tests.c /Fo$(OBJDIR)\extra_tests.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\lexer_test_helpers.obj: $(TESTDIR)\lexer_test_helpers.c
|
|
||||||
$(CC) $(CFLAGS) /c $(TESTDIR)\lexer_test_helpers.c /Fo$(OBJDIR)\lexer_test_helpers.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\lexer_tests.obj: $(TESTDIR)\lexer_tests.c
|
|
||||||
$(CC) $(CFLAGS) /c $(TESTDIR)\lexer_tests.c /Fo$(OBJDIR)\lexer_tests.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\string_tests.obj: $(TESTDIR)\string_tests.c
|
|
||||||
$(CC) $(CFLAGS) /c $(TESTDIR)\string_tests.c /Fo$(OBJDIR)\string_tests.obj
|
|
||||||
|
|
||||||
$(OBJDIR)\tests.obj: $(TESTDIR)\tests.c
|
|
||||||
$(CC) $(CFLAGS) /c $(TESTDIR)\tests.c /Fo$(OBJDIR)\tests.obj
|
|
||||||
|
|
||||||
# === RUN EXECUTABLE ===
|
|
||||||
run: $(TARGET)
|
|
||||||
$(TARGET)
|
|
||||||
|
|
||||||
# === TEST BUILD & RUN ===
|
|
||||||
test: $(TEST_TARGET)
|
|
||||||
$(TEST_TARGET)
|
|
||||||
|
|
||||||
$(TEST_TARGET): dirs $(TEST_OBJECTS) $(OBJDIR)\lexer.obj $(OBJDIR)\string.obj
|
|
||||||
link $(TEST_OBJECTS) $(OBJDIR)\lexer.obj $(OBJDIR)\string.obj /OUT:$@ $(LDFLAGS)
|
|
||||||
|
|
||||||
# === CREATE DIRECTORIES ===
|
|
||||||
dirs:
|
|
||||||
if not exist $(OBJDIR) mkdir $(OBJDIR)
|
|
||||||
if not exist $(BINDIR) mkdir $(BINDIR)
|
|
||||||
|
|
||||||
# === CLEAN ===
|
|
||||||
clean:
|
|
||||||
rmdir /s /q $(OBJDIR)
|
|
||||||
rmdir /s /q $(BINDIR)
|
|
||||||
|
|
@ -4,20 +4,20 @@ This is the C implementation for the YREA SLS interpreter.
|
||||||
|
|
||||||
## Compiling, Running, and Testing
|
## Compiling, Running, and Testing
|
||||||
|
|
||||||
|
Interpreter binary location:
|
||||||
|
- Linux: `./bin/sls`
|
||||||
|
- Windows: `.\bin\sls.exe`
|
||||||
|
|
||||||
### Linux (GCC)
|
### Linux (GCC)
|
||||||
|
|
||||||
Build Project: `make build`
|
Build Project: `python3 build.py all`
|
||||||
Build and Run Project: `make run`
|
Build and Run Project: `python3 build.py run`
|
||||||
Build and Run Tests: `make test`
|
Build and Run Tests: `python3 build.py test`
|
||||||
|
|
||||||
Interpreter binary location: `./bin/sls`
|
|
||||||
|
|
||||||
### Windows (MSVC)
|
### Windows (MSVC)
|
||||||
|
|
||||||
Use a developer shell for all nmake commands.
|
*__For Windows users:__ Use a VS developer shell for all build commands.*
|
||||||
|
|
||||||
Build Project: `nmake /f Makefile.nmake build`
|
Build Project: `python build.py all`
|
||||||
Build and Run Project: `nmake /f Makefile.nmake run`
|
Build and Run Project: `python build.py run`
|
||||||
Build and Run Tests: `nmake /f Makefile.nmake test`
|
Build and Run Tests: `python build.py test`
|
||||||
|
|
||||||
Interpreter binary location: `.\bin\sls.exe`
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# CONFIG
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
SRC_DIR = Path("src")
|
||||||
|
TEST_DIR = Path("tests")
|
||||||
|
OBJ_DIR = Path("obj")
|
||||||
|
BIN_DIR = Path("bin")
|
||||||
|
|
||||||
|
TARGET = BIN_DIR / "sls"
|
||||||
|
TEST_TARGET = BIN_DIR / "sls_tests"
|
||||||
|
|
||||||
|
# Unix gcc/clang flags
|
||||||
|
COMMON_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Werror", "-Iinclude", "-g"]
|
||||||
|
TEST_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Wno-unused-function", "-Werror",
|
||||||
|
"-Iinclude", "-g", "-O0"]
|
||||||
|
|
||||||
|
# Windows MSVC flags
|
||||||
|
MSVC_FLAGS = ["/std:c11", "/Zi", "/Iinclude"]
|
||||||
|
MSVC_TEST_FLAGS = MSVC_FLAGS + []
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# COMPILER DETECTION
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def detect_compiler():
|
||||||
|
if os.name == "nt":
|
||||||
|
return ("cl", "msvc")
|
||||||
|
return ("gcc", "gcc")
|
||||||
|
|
||||||
|
CC, CC_KIND = detect_compiler()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# PYTHON DETECTION
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def detect_python():
|
||||||
|
if os.name == "nt":
|
||||||
|
return "python"
|
||||||
|
return "python3"
|
||||||
|
|
||||||
|
PYTHON = detect_python()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# GIT COMMIT HASH
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def git_commit_hash():
|
||||||
|
try:
|
||||||
|
result_hash = subprocess.check_output(
|
||||||
|
["git", "describe", "--always", "--dirty", "--abbrev=7"],
|
||||||
|
cwd=".",
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True
|
||||||
|
).strip()
|
||||||
|
result_date = subprocess.check_output(
|
||||||
|
["git", "show", "-s", "--format=%ci"],
|
||||||
|
cwd=".",
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True
|
||||||
|
).strip()
|
||||||
|
return f"{result_hash} {result_date}"
|
||||||
|
except Exception:
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
GIT_HASH = git_commit_hash()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# UTILS
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def mkdir(p: Path):
|
||||||
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd):
|
||||||
|
print(">>", " ".join(cmd))
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def is_up_to_date(src, obj, dep):
|
||||||
|
if not obj.exists():
|
||||||
|
return False
|
||||||
|
if dep.exists() and dep.stat().st_mtime > obj.stat().st_mtime:
|
||||||
|
return False
|
||||||
|
return src.stat().st_mtime < obj.stat().st_mtime
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# BUILD RULES
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def compile_source(src: Path, is_test=False):
|
||||||
|
mkdir(OBJ_DIR)
|
||||||
|
obj = OBJ_DIR / (src.stem + ".o")
|
||||||
|
dep = OBJ_DIR / (src.stem + ".d")
|
||||||
|
|
||||||
|
if is_up_to_date(src, obj, dep):
|
||||||
|
return obj
|
||||||
|
|
||||||
|
if CC_KIND == "msvc":
|
||||||
|
flags = MSVC_TEST_FLAGS if is_test else MSVC_FLAGS
|
||||||
|
cmd = [CC] + flags + ["/Fo" + str(obj), "/c", str(src),
|
||||||
|
f"/DGIT_COMMIT_HASH=\"{GIT_HASH}\""]
|
||||||
|
else:
|
||||||
|
flags = TEST_FLAGS if is_test else COMMON_FLAGS
|
||||||
|
cmd = [CC] + flags + [
|
||||||
|
f"-DGIT_COMMIT_HASH=\"{GIT_HASH}\"",
|
||||||
|
"-MMD", "-MP",
|
||||||
|
"-c", str(src), "-o", str(obj)
|
||||||
|
]
|
||||||
|
|
||||||
|
run(cmd)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def link_executable(objects, output: Path):
|
||||||
|
mkdir(BIN_DIR)
|
||||||
|
if CC_KIND == "msvc":
|
||||||
|
cmd = [CC] + list(map(str, objects)) + ["/Fe" + str(output)]
|
||||||
|
else:
|
||||||
|
cmd = [CC] + list(map(str, objects)) + ["-lm", "-o", str(output)]
|
||||||
|
run(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# TEST CASE GENERATION
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def generate_tests():
|
||||||
|
script = Path("../SLS_Tests/yaml_to_c_tests.py")
|
||||||
|
yaml = Path("../SLS_Tests/cases.yaml")
|
||||||
|
out = TEST_DIR / "lexer_tests.c"
|
||||||
|
|
||||||
|
if not script.exists() or not yaml.exists():
|
||||||
|
print("Test generation skipped (missing files).")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if out.exists() and out.stat().st_mtime > yaml.stat().st_mtime:
|
||||||
|
return True
|
||||||
|
|
||||||
|
run([PYTHON, str(script), str(yaml), str(out)])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# TARGETS
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def build_main():
|
||||||
|
sources = list(SRC_DIR.glob("*.c"))
|
||||||
|
objects = [compile_source(s, is_test=False) for s in sources]
|
||||||
|
link_executable(objects, TARGET)
|
||||||
|
|
||||||
|
|
||||||
|
def build_tests():
|
||||||
|
generate_tests()
|
||||||
|
test_sources = list(TEST_DIR.glob("*.c"))
|
||||||
|
main_sources = [s for s in SRC_DIR.glob("*.c") if s.name != "main.c"]
|
||||||
|
|
||||||
|
test_objects = [compile_source(s, is_test=True) for s in test_sources]
|
||||||
|
shared_objects = [compile_source(s, is_test=False) for s in main_sources]
|
||||||
|
|
||||||
|
link_executable(test_objects + shared_objects, TEST_TARGET)
|
||||||
|
|
||||||
|
|
||||||
|
def clean():
|
||||||
|
shutil.rmtree(OBJ_DIR, ignore_errors=True)
|
||||||
|
shutil.rmtree(BIN_DIR, ignore_errors=True)
|
||||||
|
print("Cleaned.")
|
||||||
|
|
||||||
|
|
||||||
|
def run_main():
|
||||||
|
build_main()
|
||||||
|
print("\n--- Running program ---\n")
|
||||||
|
subprocess.call([str(TARGET)])
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
build_tests()
|
||||||
|
print("\n--- Running tests ---\n")
|
||||||
|
subprocess.call([str(TEST_TARGET)])
|
||||||
|
|
||||||
|
|
||||||
|
def debug_tests():
|
||||||
|
build_tests()
|
||||||
|
if os.name == "nt":
|
||||||
|
print("Debugging on Windows requires Visual Studio debugger.")
|
||||||
|
else:
|
||||||
|
subprocess.call(["gdb", str(TEST_TARGET)])
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# ENTRY POINT
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python3 build.py [all|build|test|run|debug|clean]")
|
||||||
|
return
|
||||||
|
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
|
match cmd:
|
||||||
|
case "all" | "main":
|
||||||
|
build_main()
|
||||||
|
case "build":
|
||||||
|
build_main()
|
||||||
|
case "run":
|
||||||
|
run_main()
|
||||||
|
case "test":
|
||||||
|
run_tests()
|
||||||
|
case "debug":
|
||||||
|
debug_tests()
|
||||||
|
case "clean":
|
||||||
|
clean()
|
||||||
|
case _:
|
||||||
|
print(f"Unknown target: {cmd}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue