From 048e6e5543e8bd7b07f77e2994a6680e13d375ad Mon Sep 17 00:00:00 2001 From: Laurent Mazet Date: Mon, 18 Dec 2023 18:49:33 +0100 Subject: [PATCH] initial version --- .gitignore | 9 ++ bf.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++ debug.c | 5 ++ debug.h | 21 +++++ getcomments.pl | 85 +++++++++++++++++++ makefile | 144 ++++++++++++++++++++++++++++++++ 6 files changed, 486 insertions(+) create mode 100644 .gitignore create mode 100644 bf.c create mode 100644 debug.c create mode 100644 debug.h create mode 100644 getcomments.pl create mode 100644 makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d70163 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.d +*.ld +*.exe +*.log +*.o +.targets +*.gcda +*.gcno +*.gcov diff --git a/bf.c b/bf.c new file mode 100644 index 0000000..e687f94 --- /dev/null +++ b/bf.c @@ -0,0 +1,222 @@ +/* depend: */ +/* cflags: */ +/* linker: debug.o */ + +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +/* macros */ + +#define BUFSIZE 256 +#define MEMSIZE 10 + +/* type definition */ + +/* gobal variables */ + +char *progname = NULL; +int p = 0; +char mem[MEMSIZE] = {0}; + +/* help function */ + +int usage (int ret) +{ + FILE *fd = ret ? stderr : stdout; + fprintf (fd, "usage: %s [-i file] [-h] [-m k&r|ansi|c99] [-o file] [-v]\n", progname); + fprintf (fd, " -i : input file\n"); + fprintf (fd, " -h : help message\n"); + fprintf (fd, " -m : indent mode\n"); + fprintf (fd, " -o : output file\n"); + fprintf (fd, " -v : verbose level (%d)\n", verbose); + + return ret; +} + +/* main function */ + +int main (int argc, char *argv[]) +{ + char *input = NULL; + char *output = NULL; + char *buffer = NULL; + int n = 0; + int size = 0; + FILE *fid = NULL; + + /* get basename */ + char *pt = progname = argv[0]; + while (*pt) { + if ((*pt == '/') || (*pt == '\\')) { + progname = pt + 1; + } + pt++; + } + + int c; + while ((c = getopt(argc, argv, "i:o:hv:")) != EOF) { + switch (c) { + case 'i': + input = optarg; + break; + case 'o': + output = optarg; + break; + case 'v': + verbose = atoi (optarg); + break; + case 'h': + default: + return usage (c != 'h'); + } + } + if (argc - optind != 0) { + VERBOSE (ERROR, fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind])); + return usage (1); + } + + /* check input file */ + if (input) { + fid = fopen (input, "r"); + if (fid == NULL) { + VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for reading\n", progname, input)); + return 1; + } + } else { + fid = stdin; + } + + /* read input file */ + while (!feof (fid)) { + buffer = (char *) realloc (buffer, size + BUFSIZE); + if (buffer == NULL) { + VERBOSE (ERROR, fprintf (stderr, "%s: can't allocate memory\n", progname)); + return 1; + } + memset (buffer + size, 0, BUFSIZE); + n = fread (buffer + size, 1, BUFSIZE, fid); + if (errno != 0) { + VERBOSE (ERROR, fprintf (stderr, "%s: can't read data from file '%s'\n", progname, input)); + return 1; + } + size += BUFSIZE; + } + + /* close input file */ + fclose (fid); + VERBOSE (DEBUG, fprintf (stderr, "%s: read %d bytes\n", progname, size + n - BUFSIZE)); + + /* check output file */ + if (output) { + fid = fopen (output, "w"); + if (fid == NULL) { + VERBOSE (ERROR, fprintf (stderr, "%s: can't open file '%s' for writing\n", progname, output)); + return 1; + } + } else { + fid = stdout; + } + + /* main process */ + int rc = process (buffer, size, fid); + + /* close output file */ + fclose (fid); + + return rc; +} + +/* main process */ +int process (char *buffer, int nb, FILE *out) { + int i; + + for (i = 0; (i < nb) && (buffer[i] != 0); i++) { + + VERBOSE (DEBUG, fprintf (stderr, "%s: read '%c' (%u)\n", progname, buffer[i], buffer[i])); + switch (buffer[i]) { + case '>': /* increase pointer */ + p++; + break; + case '<': /* decrease pointer */ + p--; + break; + case '+': /* increase pointer value */ + if ((p >= 0) && (p < MEMSIZE)) { + mem[p]++; + } else { + VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p)); + return 1; + } + break; + case '-': /* decrease pointer value */ + if ((p >= 0) && (p < MEMSIZE)) { + mem[p]--; + } else { + VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p)); + return 1; + } + break; + case '.': /* output pointer value */ + if ((p >= 0) && (p < MEMSIZE)) { + fprintf (out, "%c", mem[p]); + } else { + VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p)); + return 1; + } + break; + case ',': /* read a byte and store it in memory */ + if ((p >= 0) && (p < MEMSIZE)) { + mem[p] = buffer[++i]; + } else { + VERBOSE (ERROR, fprintf (stderr, "%s: invalid address (%d)\n", progname, p)); + return 1; + } + break; + case '[': /* jump to right bracket if pointer is set to 0 */ + if (mem[p] == 0) { + int bracket = 1; + while ((++i < nb) && (bracket > 0)) { + bracket += (buffer[i] == '[') ? +1 : (buffer[i] == ']') ? -1 :0; + } + if (bracket) { + VERBOSE (ERROR, fprintf (stderr, "%s: brace not closed\n", progname)); + return 1; + } + } else { + return process (buffer + i + 1, nb - i - 1, out); + } + break; + case ']': /* jump to left bracket if pointer is different to 0 */ + if (mem[p] != 0) { + i = -1; + } + break; + case '\n': + case '\r': + break; + default: + VERBOSE (WARNING, fprintf (stderr, "%s: can't understand '%c'\n", progname, buffer[i])); + } + + //VERBOSE (DEBUG, int _i; fprintf (stderr, "%s: p: %d mem:", progname, p); for (_i = 0; _i < MEMSIZE; _i++) fprintf (stderr, " %d", mem[_i]); fprintf (stderr, "\n")); + + } + + return 0; +} + +// test: bf.exe -h +// test: bf.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }' +// test: bf.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }' +// test: bf.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }' +// test: echo '++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.' | bf.exe +// test: echo '++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>++++++++++++++.>+++++++++++++++++.<<++.>+++++++++++++.>--.<<.>+++.+.--.>----.++++++.+.<++.>----.++.<<.>>+.-------.<<.>>++.<.>+++++.<<.>-.+.<.>---.>---.<-.++++++++.>----.<---.>+++++++.<---.++++++++.' | bf.exe +// test: echo ',3>,2>>++++++++++++++++[-<+++<---<--->>>]<<[<[>>+>+<<<-]>>>[<<<+>>>-]<<-]>.' | bf.exe + +/* vim: set ts=4 sw=4 et: */ diff --git a/debug.c b/debug.c new file mode 100644 index 0000000..151e615 --- /dev/null +++ b/debug.c @@ -0,0 +1,5 @@ +#include "debug.h" + +int verbose = 1; + +/* vim: set ts=4 sw=4 et: */ diff --git a/debug.h b/debug.h new file mode 100644 index 0000000..fb1c012 --- /dev/null +++ b/debug.h @@ -0,0 +1,21 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +/* constants */ + +#define DEBUG 3 +#define INFO 2 +#define WARNING 1 +#define ERROR 0 + +/* macros */ + +#define VERBOSE(level, statement...) do { if (level <= verbose) { statement; fflush (stdout); fflush (stderr); } } while(0) + +/* gobal variables */ + +extern int verbose; + +#endif /* __DEBUG_H__ */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/getcomments.pl b/getcomments.pl new file mode 100644 index 0000000..47edf67 --- /dev/null +++ b/getcomments.pl @@ -0,0 +1,85 @@ +#!/usr/bin/perl + +use strict; + +# default value +my $format = "%"; +my $pattern = ""; + +# help message +sub usage() { + + print <) { + my $cmt; + + # process c++ comments + ($cmt, $_) = m{//\s*(.*?)\s*$()} if (m{//} && !m{/\*.*//}); + + # process standard c comments + ($cmt, $_) = m{^.*?/\*\s*(.*?)\s*\*/(.*)}s if (m{/\*.*\*/}s); + + push(@comments, $cmt) if ($cmt); + + # empty buffer if no comment is present + undef($_) if (!m{/[/*]}); + } + + # close file + close (IN); + + # display comment blocks + foreach my $block (@comments) { + if (($block) = ($block =~ /$pattern(.*)/s)) { + ($_ = $format) =~ s/%/$block/gs; + print "$_\n"; + } + } +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..214c2b3 --- /dev/null +++ b/makefile @@ -0,0 +1,144 @@ +# Default flags + +CC = gcc + +#INCLUDES = -I../debug -D__MEMORY_ALLOCATION__ +INCLUDES = +OFLAGS = -O4 -Os +#OFLAGS = -O4 -ffast-math -finline-functions +#OFLAGS = -O4 -finline-functions +#OFLAGS += -mtune=pentium3 -mmmx -msse -msse2 -m3dnow +#OFLAGS += -minline-all-stringops -fsingle-precision-constant +#OFLAGS += -malign-double +CFLAGS += -W -Wall -Wextra -g +#CFLAGS += -std=c99 -D_XOPEN_SOURCE=500 +CFLAGS += $(OFLAGS) $(INCLUDES) $(GCOV) +LDFLAGS += -g $(GCOV) + +# Targets + +ALLEXE = +ALLEXE += bf + +SHELL = bash + +#MAKE = mingw32-make +MAKEFLAGS += -s +include $(wildcard .makefile) + +# Functions + +TITLE = echo -en "\033[0;1m$(strip $(1))\033[0;0m:\t" +PASS = echo -e "\033[1;32m$(strip $(1))\033[0;0m" +WARN = echo -e "\033[1;33m$(strip $(1))\033[0;0m" +FAIL = echo -e "\033[1;31m$(strip $(1))\033[0;0m" + +MKDIR = mkdir -p $(1) && chmod a+rx,go-w $(1) + +INSTALL = test -d `dirname $(2)` || $(call MKDIR, `dirname $(2)`) && cp -pa $(1) $(2) && chmod a+rX,go-w $(2) + +VALID = $(call TITLE, $(1)) && $(2) && $(call PASS, SUCCESS) || { $(call FAIL, FAILED); test; } + +## Generic rules + +all: depends + $(MAKE) $(ALLEXE:%=%.exe) + +count: + wc $(wildcard *.c *.h) $(MAKEFILE_LIST) + +clean: + $(call TITLE, "Cleaning") + touch clean + rm -f clean $(wildcard *.d *.ld *.log *.o *.test *~ .exec_*) + $(call PASS, SUCCESS) + +depends: $(patsubst %.c, %.d, $(wildcard *.c)) $(patsubst %, %.ld, $(ALLEXE)) + +gcovs: + $(MAKE) $(addprefix gcov_,$(ALLEXE)) + +purge: clean + $(call TITLE, "Purging") + touch purge + rm -f purge $(ALLEXE:%=%.exe) + $(call PASS, SUCCESS) + +valgrinds: + $(MAKE) $(addprefix valgrind_,$(ALLEXE)) + +wipe: purge + $(call TITLE, "Wiping") + touch wipe + rm -f wipe $(wildcard *.gcda *.gcno *.gcov) + $(call PASS, SUCCESS) + +tests: all + $(MAKE) $(addprefix test_,$(ALLEXE)) + +## Main rules + +include $(wildcard *.d) +include $(wildcard *.ld) + +gcov_%: + $(MAKE) purge + GCOV="-coverage -O0" $(MAKE) + $(MAKE) test_$(@:gcov_%=%) + gcov `sed -e 's/\.exe:/.c/;s/\.o/.c/g' $(@:gcov_%=%.ld)` + touch gcov + rm -f gcov $(wildcard *.gcda *.gcno) + $(MAKE) purge + grep '#####' *.c.gcov || true + +%.test: %.c + $(call TITLE, "Building $@") +# awk '/\/\* *test:.*\*\// { sub(/^.*\/\* *test: */, ""); sub(/ *\*\/.*$$/, ""); print }' $< > $@ + perl -- getcomments.pl -p='test:\s' -f='%' $< > $@ + $(call PASS, SUCCESS) + +test_%: %.test %.exe + IFS=$$'\n'; RC=0; \ + for test in `cat $< | sed 's,${<:.test=.exe},$(VALGRIND) ./${<:.test=.exe},g'`; do \ + echo "=== $$test ==="; \ + eval $$test; \ + [ $$? -eq 0 ] && echo -e "\033[1;32mSUCCESS\033[0;0m" \ + || { echo -e "\033[1;31mFAILED\033[0;0m"; RC=1; }; \ + done; \ + test "$$RC" -ne 1 + +valgrind_%: %.exe + VALGRIND="valgrind -v --leak-check=full --log-fd=3"; \ + export VALGRIND; \ + $(MAKE) $(@:valgrind_%=test_%) 3>$@.log + +%.d: %.c + $(call TITLE, "Building $@") + $(CC) $(INCLUDES) -MM $< -o $@~ + echo ${<:.c=.o}: $(shell perl -- getcomments.pl -p='depend:\s' -f='%' $<) >> $@~ + mv $@~ $@ + $(call PASS, SUCCESS) + +%.ld: %.c + $(call TITLE, "Building $@") + echo ${<:.c=.exe}: $(shell perl -- getcomments.pl -p='linker:\s' -f='%' $< | awk '{for (i=1;i<=NF;i++) if ($$(i) ~ /.o$$/) printf " %s", $$(i)}') > $@ + $(call PASS, SUCCESS) + +%.o: %.c + $(call TITLE, "Building $@") + $(CC) $(CFLAGS) $(INCLUDES) $(shell perl -- getcomments.pl -p='cflags:\s' -f='%' $<) -c $< -o $@ + $(call PASS, SUCCESS) + + +%.exe: %.o %.d + $(call TITLE, "Building $@") + $(CC) $(LDFLAGS) $< $(shell perl -- getcomments.pl -p='linker:\s' -f='%' ${<:.o=.c}) -o $@ + $(call PASS, SUCCESS) + +## Phony + +.PHONY: all clean count depends gcovs purge tests + +## Precious + +.PRECIOUS: %.d %.o -- 2.30.2