From 6ab57afeea8db09e555a92c2aa97043df943932e Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Mon, 7 Oct 2019 12:14:00 -0400 Subject: [PATCH] added crude linter to compiler script and updated TODO --- Rsconscript | 2 +- TODO.md | 1 + acc | 86 --------------- alcc | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 87 deletions(-) delete mode 100755 acc create mode 100755 alcc diff --git a/Rsconscript b/Rsconscript index a22c492..246a6f7 100644 --- a/Rsconscript +++ b/Rsconscript @@ -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 2defc37..1446e0f 100644 --- 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 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -' # 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +' # 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 < 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}" + -- 2.52.0