]> git.mdlowis.com Git - projs/tide.git/commitdiff
added crude linter to compiler script and updated TODO
authorMichael D. Lowis <mike.lowis@gentex.com>
Mon, 7 Oct 2019 16:14:00 +0000 (12:14 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Mon, 7 Oct 2019 16:14:00 +0000 (12:14 -0400)
Rsconscript
TODO.md
acc [deleted file]
alcc [new file with mode: 0755]

index a22c492e09cf11a4cffcc8bf64d2cfb281380489..246a6f71b2f10a4288e88bd1d33853ee9e3151c9 100644 (file)
@@ -10,7 +10,7 @@ end
 build do
   env = Environment.new
   env["prefix"] = ENV["PREFIX"] || env["prefix"]
-  env["CC"] = "./acc"
+  env["CC"] = "./alcc"
 #  env["CFLAGS"] += ["-g", "-fsanitize=undefined,address"]
 #  env["LDFLAGS"] += ["-g", "-fsanitize=undefined,address"]
   env["CPPPATH"] += %w[. inc]
diff --git a/TODO.md b/TODO.md
index 2defc373be77098bcef499beca8b54ccdf99ecd6..1446e0fa6d76d987aa030977df0a5b7a1ef9217c 100644 (file)
--- a/TODO.md
+++ b/TODO.md
@@ -4,6 +4,7 @@
 
 ## STAGING
 
+* tide: buf_findstr has an infinite loop condition
 * tide: refactor byword, byline, etc api
 * tide: refactor undo/redo api in view.c
 * all: eliminate multiple return statements
diff --git a/acc b/acc
deleted file mode 100755 (executable)
index 82be42e..0000000
--- a/acc
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright 2019 Michael D. Lowis
-#-------------------------------------------------------------------------------
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-# Wrapper script for system compiler that enables some flags by default and scans
-# for library dependencies.
-#
-# Compilation Features:
-#   * Enables --std=c99 -pedantic
-#   * Enables -Wall -Wextra -Werror
-#   * Defines AUTOLIB() macro to specify dependencies in sources and headers
-#   * Auto-includes commonly include headers such as stddef.h, stdio.h, etc.
-#
-# Linking Features:
-#   * Scans objects for AUTOLIB()-ed libs, and adds the -l flags for them
-
-# predeclare our variables
-declare -a objects
-declare -a libpaths
-compile=false
-runtime='
-#define _POSIX_C_SOURCE 200809L
-#define _XOPEN_SOURCE 700
-#define AUTOLIB(n) \
-    int __autolib_##n __attribute__ ((weak));
-#include <stddef.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <fcntl.h>
-' # end of runtime definition
-
-# scan the rest of the options for lib paths, libs and objects
-for i do
-    case "$i" in
-        *.[ao]) # Add objects and static libs to object list
-            objects+=("$i") ;;
-
-        -L*) # Add libpaths to the search list
-            libpaths+=("${i#-L}") ;;
-
-        -c|-E) # Mark this as compilation/preprocess only
-            compile=true ;;
-    esac
-done
-
-# if we're compiling,  generate the header, compile, and exit
-if $compile; then
-    genfile=$(mktemp)
-    printf '%s\n' "$runtime" > "$genfile"
-    cc -isystem "${genfile%/*}" -include "${genfile##*/}" --std=c99 -pedantic -Wall -Wextra -Werror "$@"
-    status=$?
-    rm "$genfile"
-    exit "$status"
-fi
-
-# scan objects/libs for referenced libraries
-scan_for_libs(){
-    [[ $# -ne 0 ]] && nm "$@" | sed -n '/__autolib/ s/.*_\+autolib_// p' | sort -u
-}
-
-# if we made it here, we must be linking. scan for dependencies
-mapfile -t libraries < <(scan_for_libs "${objects[@]}")
-cc "$@" "${libraries[@]/#/-l}"
diff --git a/alcc b/alcc
new file mode 100755 (executable)
index 0000000..e7d1b3f
--- /dev/null
+++ b/alcc
@@ -0,0 +1,301 @@
+#!/usr/bin/env bash
+#
+# Copyright 2019 Michael D. Lowis
+#-------------------------------------------------------------------------------
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# Compiler wrapper script that runs a crude linting process on each source file
+# before passing arguments along to the compiler. The linter rules are inspired
+# by The Power of 10 Rules defined by NASA/JPL
+# (http://web.eecs.umich.edu/~imarkov/10rules.pdf)
+#
+# Below are the NASA/JPL rules that this script attempts to check along with
+# descriptions of what the script checks for and ways it differs from the
+# original rule.
+#
+# 1) "Restrict all code to very simple control flow constructs - do not use goto
+#     statements, setjmp or longjmp constructs, or direct or indirect recursion."
+#
+#       * error on use of goto, setjmp, and longjmp
+#       * error on multiple return statements in a single function
+#       * recursion is *not* checked for
+#
+# 2) "No function should be longer than what can be printed on a single sheet of
+#     paper in a standard format with one line per statement and one line per
+#     declaration. Typically, this means no more than about 60 lines of code per
+#     function."
+#
+#       * warn on functions greater than 60 lines.
+#
+# 3) "The code's assertion density should average to minimally two assertions per
+#     function. Assertions must be used to check for anomalous conditions that
+#     should never happen in real-life executions. Assertions must be side-effect
+#     free and should be defined as Boolean tests. When an assertion fails, an
+#     explicit recovery action must be taken such as returning an error condition
+#     to the caller of the function that executes the failing assertion. Any
+#     assertion for which a static checking tool can prove that it can never
+#     fail or never hold violates this rule."
+#
+#       * warn when public functions have no assertions (assert, ensure, require)
+#
+# 4) "The use of the preprocessor must be limited to the inclusion of header files
+#     and simple macro definitions. Token pasting, variable argument lists
+#     (ellipses), and recursive macro calls are not allowed. All macros must
+#     expand into complete syntactic units. The use of conditional compilation
+#     directives must be kept to a minimum."
+#
+#       * error on use of #if or #undef
+#       * (TODO) error on use of function like macros
+#
+# 5) "All code must be compiled, from the first day of development, with all
+#     compiler warnings enabled at the most pedantic setting available. All code
+#     must compile without warnings. All code must also be checked daily with at
+#     least one, but preferably more than one, strong static source code analyzer
+#     and should pass all analyses with zero warnings."
+#
+#       * invokes compiler -Wall -Wextra -Werror -std=c99 -pedantic
+
+declare -a sources
+declare -a objects
+declare -a libpaths
+compile=false
+runtime='
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700
+#define AUTOLIB(n) \
+    int __autolib_##n __attribute__ ((weak));
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+' # end of runtime definition
+
+# scan the rest of the options for lib paths, libs and objects
+for i do
+    case "$i" in
+        *.c) # Add source to the list
+            sources+=("$i") ;;
+
+        *.[ao]) # Add objects and static libs to object list
+            objects+=("$i") ;;
+
+        -L*) # Add libpaths to the search list
+            libpaths+=("${i#-L}") ;;
+
+        -c|-E) # Mark this as compilation/preprocess only
+            compile=true ;;
+    esac
+done
+
+script=$(cat <<EOS
+    # initialize some state
+    BEGIN {
+        MAXLINES = 50
+        syms["setjmp"] = 1
+        syms["longjmp"] = 1
+        syms["goto"] = 1
+        keyw["if"] = 1
+        keyw["for"] = 1
+        keyw["while"] = 1
+        cpp["undef"] = 1
+        cpp["if"] = 1
+        asserts["assert"] = 1
+        asserts["require"] = 1
+        asserts["ensure"] = 1
+    }
+
+    # define helper functions
+    function warn(msg){
+        print FILENAME ":" FNR ": warning:", msg
+    }
+
+    function error(msg){
+        print FILENAME ":" FNR ": error:", msg
+        code = 1
+    }
+
+    function get_syms(line, ary){
+        gsub(/\W+/, " ")
+        split(\$0, ary, " ")
+    }
+
+    function scan_func(){
+        if (M[i] == "return")
+        {
+            nreturns += 1
+        }
+        else if (asserts[M[i]])
+        {
+            nasserts += 1
+        }
+    }
+
+    function process_idents(opening, closing){
+        level += opening
+        for (i = 0; i < length(M); i++)
+        {
+            if (paren && (M[i] == "static"))
+            {
+                privatefn = 1
+            }
+            else if (keyw[M[i]])
+            {
+                control = 1
+            }
+            else if (syms[M[i]])
+            {
+                error("use of prohibited symbol '" M[i] "'");
+            }
+            else if (level > 0)
+            {
+                scan_func()
+            }
+        }
+        level -= closing
+    }
+
+    function close_function(){
+        if (nreturns > 1)
+        {
+            error("functions should have a maximum of one return statement")
+        }
+
+        if ((FNR - linenum) > MAXLINES)
+        {
+            warn("function is " (FNR - linenum) " lines long. blocks larger than 50 lines are considered too complex")
+        }
+
+        if (level < 0)
+        {
+            error("too many closing braces detected")
+        }
+
+        if (!privatefn && (nasserts == 0))
+        {
+            warn("public function has no assertions")
+        }
+
+        privatefn = 0
+        nasserts = 0
+        nreturns = 0
+        infunc = 0
+    }
+
+    # if we're in a comment block, look for the end
+    (incomment && match(\$0, /\*\//)) {
+        incomment = 0
+    }
+
+    # scan preprocessor statements
+    match(\$0, /^\s*#\s*(\w+)/, M) {
+        if (cpp[M[1]])
+        {
+            error("use of #" M[1] " is prohibited")
+        }
+    }
+
+    # scan all identifiers for prohibited symbols
+    !incomment && /^\s*[^#]/ {
+        control = 0
+        paren = match(\$0, /\(/)
+        gsub(/"(\\\\"|[^"])*"/, "")      # ignore string literals
+        sub(/\/\/.*$/, "")             # ignore line comments
+        gsub(/\/\*.*\*\//, "")         # ignore block comments on single line
+        incomment = sub(/\/\*.*$/, "") # ignore block comment starts
+        sub(/^.*\*\//, "")             # ignore block comment stops
+        opening = gsub(/{/, "")
+        closing = gsub(/}/, "")
+        get_syms(\$0, M);
+
+        # reset state if not in a function
+        if (level == 0)
+        {
+            linenum = FNR + 1
+            privatefn = 0
+            nasserts = 0
+            nreturns = 0
+            infunc = 0
+        }
+
+        if (!level && paren && opening)
+        {
+            warn("function braces should be on their own lines")
+        }
+
+        process_idents(opening, closing)
+
+        if (control && opening)
+        {
+            warn("braces of control constructs should be on their own lines")
+        }
+
+        if (infunc && level <= 0)
+        {
+            close_function()
+        }
+
+        if (level == 0 && paren)
+        {
+            infunc = 1
+        }
+    }
+
+    # set return code based on errors detected
+    END {
+        if (level > 0)
+        {
+            error("reached EOF with open block");
+        }
+        exit code
+    }
+EOS
+)
+
+# run the linter and bail if it fails
+if [[ ${#sources[@]} -gt 0 ]]; then
+    #printf "%s\n" "$script"
+    if ! gawk "$script" "${sources[@]}"; then
+        exit 1
+    fi
+fi
+
+# if we're compiling,  generate the header, compile, and exit
+if $compile; then
+    genfile=$(mktemp)
+    printf '%s\n' "$runtime" > "$genfile"
+    cc -isystem "${genfile%/*}" -include "${genfile##*/}" --std=c99 -pedantic -Wall -Wextra -Werror "$@"
+    status=$?
+    rm "$genfile"
+    exit "$status"
+fi
+
+# scan objects/libs for referenced libraries
+scan_for_libs(){
+    [[ $# -ne 0 ]] && nm "$@" | sed -n '/__autolib/ s/.*_\+autolib_// p' | sort -u
+}
+
+# if we made it here, we must be linking. scan for dependencies
+mapfile -t libraries < <(scan_for_libs "${objects[@]}")
+cc "$@" "${libraries[@]/#/-l}"
+