#! /usr/bin/awk -f
# ASAP - AWK Simple and Awful Preprocessor
# Copyright (C) 2007 Matous Jan Fialka
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU general Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU general Public License for
# more details.
#
# You should have received a copy of the GNU general Public License along with
# this program. If not, see .
# TODO: MACROS
# TODO: #eval
# TODO: #if #ifnot #elif #elifnot
# TODO: #ifdef #ifndef #elifdef #elifndef
# TODO: #else #endif
# MACROS:
#
# ::= "#include" |
# "#pragma |
# "#eval" { | value } |
# "#define" [ ] |
# "#undef" |
# "#if" { | } |
# "#ifnot" { | } |
# "#elif" | |
# "#elifnot" { | } |
# "#ifdef" |
# "#ifndef" |
# "#elifdef" |
# "#elifndef" |
# "#else" |
# "#endif" |
# "#return" |
# "#quit" |
# "#warning" [ ] |
# "#error" ;
#
#
#
# POSIX 1003.2 REGULAR EXPRESSIONS:
#
# ::= REGEX "^([_]*)?[a-zA-Z][0-9_a-zA-Z\-]*$"
# ::= REGEX "^[\-]?[0-9]+$"
# ::= REGEX "^.*$"
#
#
#
# PRAGMA DIRECTIVES:
#
# strict - #include produces non-critical errors too
# once - #include includes each filename only once
#
#
#
# BUILTIN VARIABLES:
#
# __ARGC__ - argument count
# __ARGN__ - argument number
# __ARGV__ - argument value
# __FILE__ - filename
# __LINE__ - file line number
# __DATA__ - file line data
# __EXIT__ - exit code
BEGIN {
FSTDIN = "/dev/fd/0"
FSTDOUT = "/dev/fd/1"
FSTDERR = "/dev/fd/2"
FSTDWARN = FSTDERR
NULL = ""
LFNL = "\n"
ERR[1] = "no such file or directory"
ERR[130] = "#include output descriptor on input"
ERR[131] = "#include standard input overflow"
ERR[132] = "#include target already included"
ERR[133] = "#include no such file or directory"
ERR[140] = "#pragma directive syntax error"
ERR[150] = "#define variable syntax error"
ERR[151] = "#define read-only variable"
ERR[160] = "#undef variable syntax error"
ERR[161] = "#undef read-only variable"
PRA["strict"] = 0
PRA["once"] = 0
DEF["__ARGV__"] = 1
DEF["__ARGC__"] = 1
DEF["__ARGN__"] = 1
DEF["__ARGD__"] = 1
DEF["__FILE__"] = 1
DEF["__LINE__"] = 1
DEF["__DATA__"] = 1
DEF["__EXIT__"] = 1
VAR["__ARGV__"] = ajoin(ARGV, " ")
VAR["__ARGC__"] = ARGC
VAR["__ARGN__"] = 0
VAR["__ARGD__"] = NULL
VAR["__FILE__"] = FSTDIN
VAR["__LINE__"] = 0
VAR["__DATA__"] = NULL
VAR["__EXIT__"] = minclude(DEF, VAR, PRA)
exit
}
END {
exit die(VAR)
}
function die(vars)
{
if(vars["__EXIT__"] > 254) {
printf "User error (%s: %d): %s" LFNL,
vars["__FILE__"], vars["__LINE__"],
ERR[vars["__EXIT__"]] >> FSTDERR
}
else if(vars["__EXIT__"] > 127) {
printf "*** [%s]" LFNL, vars["__DATA__"] >> FSTDERR
printf "Runtime error (%s: %d): %s" LFNL,
vars["__FILE__"], vars["__LINE__"],
ERR[vars["__EXIT__"]] >> FSTDERR
}
else if(vars["__EXIT__"] > 1) {
printf "Syntax error (%d/ %d): %s" LFNL,
vars["__ARGN__"], vars["__ARGC__"],
ERR[vars["__EXIT__"]] >> FSTDERR
}
else if(vars["__EXIT__"] > 0) {
printf "Error: %s" LFNL,
ERR[vars["__EXIT__"]] >> FSTDERR
}
return vars["__EXIT__"]
}
function minclude(d, v, p , f, n, c, i , m, a, t, j, q)
{
if(fisstdout(v["__FILE__"]) || fisstderr(v["__FILE__"])) {
v["__FILE__"] = apop(f)
v["__LINE__"] = apop(n)
v["__DATA__"] = apop(c)
merror(v, 130)
}
if(ahasfstdin(i) && (fisstdin(v["__FILE__"]))) {
if(p["strict"]) {
v["__FILE__"] = apop(f)
v["__LINE__"] = apop(n)
v["__DATA__"] = apop(c)
merror(v, 131)
} else
return 0
}
if(ahas(i, v["__FILE__"])) {
if(p["once"]) {
if(p["strict"]) {
v["__FILE__"] = apop(f)
v["__LINE__"] = apop(n)
v["__DATA__"] = apop(c)
merror(v, 132)
} else
return 0
} else
close(v["__FILE__"])
}
v["__LINE__"] = 0
v["__DATA__"] = NULL
if(fexists(v["__FILE__"])) {
if(! ahas(i, v["__FILE__"]))
apush(i, v["__FILE__"])
j = 0
while((getline v["__DATA__"] < v["__FILE__"]) > 0) {
v["__LINE__"]++
if((v["__DATA__"] ~ /^[ \t]*#.*[^\\]?\\$/) || \
((v["__DATA__"] ~ /^.*[^\\]?\\$/) && j)) {
v["__DATA__"] = substr(v["__DATA__"], 1,
length(v["__DATA__"]) - 1)
apush(q, chopstrl(v["__DATA__"]))
j = 1
continue
}
if(! aempty(q)) {
if(j)
apush(q, chopstrl(v["__DATA__"]))
v["__DATA__"] = ajoin(q, NULL)
delete q
j = 0
}
if(v["__DATA__"] ~ /^[ \t]*\\?\\#/) {
munquote(d, v)
printf "%s" LFNL, meval(d, v) >> FSTDOUT
}
else if(v["__DATA__"] ~ /^[ \t]*#include[ \t]+/) {
apush(f, v["__FILE__"])
apush(n, v["__LINE__"])
apush(c, v["__DATA__"])
v["__DATA__"] = chopstr(shift(1, v["__DATA__"]))
v["__FILE__"] = meval(d, v)
v["__EXIT__"] = minclude(d, v, p, f, n, c, i)
v["__FILE__"] = apop(f)
v["__LINE__"] = apop(n)
v["__DATA__"] = apop(c)
if(v["__EXIT__"])
merror(v, 133)
}
else if(v["__DATA__"] ~ /^[ \t]*#pragma[ \t]+/) {
a = chopstr(shift(1, v["__DATA__"]))
if(! visvalid(a))
merror(v, 140)
mpragma(p, a)
}
else if(v["__DATA__"] ~ /^[ \t]*#define[ \t]+/) {
a = chopstr(shift(1, v["__DATA__"]))
t = shift(1, a)
a = substrto(" ", a)
if(! visvalid(a))
merror(v, 150)
if(visreadonly(a))
merror(v, 151)
mdefine(d, v, a, t)
}
else if(v["__DATA__"] ~ /^[ \t]*#undef[ \t]+/) {
a = chopstr(shift(1, v["__DATA__"]))
if(! visvalid(a))
merror(v, 160)
if(visreadonly(a))
merror(v, 161)
mundef(d, v, a)
}
else if(v["__DATA__"] ~ /^[ \t]*#return$/) {
break
}
else if(v["__DATA__"] ~ /^[ \t]*#quit$/) {
merror(v, 0)
}
else if(v["__DATA__"] ~ /^[ \t]*#warning[ \t]+/) {
mwarning(v, chopstr(shift(1, v["__DATA__"])))
}
else if(v["__DATA__"] ~ /^[ \t]*#error[ \t]+/) {
ERR[255] = chopstr(shift(1, v["__DATA__"]))
merror(v, 255)
}
else if(v["__DATA__"] ~ /^[ \t]*#/) {
}
else {
printf "%s" LFNL, meval(d, v) >> FSTDOUT
}
}
close(v["__FILE__"])
} else
return 1
return 0
}
function munquote(defs, vars)
{
vars["__DATA__"] = gensub(/^([ \t]*\\?)\\(.*)/, "\\1\\2", NULL,
vars["__DATA__"])
return vars["__DATA__"]
}
function meval(defs, vars , m, x, d, a, i, l, r)
{
if(m != NULL) {
split(vars["__DATA__"], a, m)
l = alength(a)
r = NULL
if(l > 0) {
for(i = 1; i < l; ++i) {
r = r a[i]
if(a[i] ~ /\\+$/) {
gsub(/\\$/, NULL, r)
r = r m
}
else {
if(mifdef(defs, m))
r = r vars[m]
else
r = r m
}
}
r = r a[l]
}
vars["__DATA__"] = r
}
else {
l = asortil(defs, x)
for(i = l; i > 0; i--)
vars["__DATA__"] = meval(defs, vars, x[i])
}
return vars["__DATA__"]
}
function mpragma(pras, pra , i)
{
for(i in pras)
if(i == pra)
return (++pras[i])
return 0
}
function mdefine(defs, vars, v , a)
{
defs[v] = 1
vars[v] = ((a == NULL) ? 1 : a)
return vars[v]
}
function mundef(defs, vars, v)
{
delete vars[v]
delete defs[v]
return
}
function mifdef(defs, v)
{
return (defs[v] == 1)
}
function mwarning(vars, warning)
{
printf "Warning (%s: %d): %s" LFNL,
vars["__FILE__"], vars["__LINE__"],
warning >> FSTDWARN
return
}
function merror(vars, err)
{
vars["__EXIT__"] = err
exit
}
function visvalid(a)
{
return (a ~ /^([_]*)?[a-zA-Z][0-9_a-zA-Z\-]*$/)
}
function visreadonly(a)
{
return (a ~ /^__(ARG(V|C|N|D)|FILE|LINE|DATA|EXIT)__$/)
}
function shift(number , text, separator, array)
{
if(number == NULL || number <= 0)
number = 1
if(separator == NULL)
separator = FS
if(separator == " ")
separator = "[ \t]+"
while(number-- > 0) {
sub("^" separator, NULL, text)
if(split(text, array, separator) < 2) {
text = NULL
break
} else
text = substr(text, match(text, separator) + RLENGTH)
}
return text
}
function chopstrl(text)
{
gsub(/^[ \t]+/, NULL, text)
return text
}
function chopstrr(text)
{
gsub(/[ \t]+$/, NULL, text)
return text
}
function chopstr(text)
{
return chopstrl(chopstrr(text))
}
function substrto(pattern, text)
{
if(pattern == " ")
pattern = "[ \t]+"
sub(pattern ".*", NULL, text)
return text
}
function aempty(array , i)
{
for(i in array)
return 0
return 1
}
function alength(array , len, i)
{
if(!aempty(array)) {
len = 0
for(i in array)
len++
return len
} else
return 0
}
function apush(array, value , i)
{
array[alength(array) + 1] = value
return value
}
function apop(array, i , r)
{
if(!aempty(array)) {
i = alength(array)
r = array[i]
delete array[i]
return r
} else
return NULL
}
function ajoin(array, sep , x, r, l)
{
l = alength(array)
if(l)
for(x = 1; x <= l; ++x)
r = r ((r != NULL) ? sep : NULL) array[x]
return r
}
function ahas(array, v , i)
{
for(i in array)
if(array[i] == v)
return 1
return 0
}
function asortil(a, t , i, j)
{
j = 1
for(i in a)
t[j++] = i
return asortl(t)
}
function asortl(a, i , l, j, t)
{
l = alength(a)
for(i = 2; i <= l; ++i) {
for(j = i; length(a[j - 1]) > length(a[j]); --j) {
t = a[j]
a[j] = a[j - 1]
a[j - 1] = t
}
}
return l
}
function ahasfstdin(array , i)
{
for(i in array)
if(fisstdin(array[i]))
return 1
return 0
}
function fexists(file , line)
{
if(fisstdin(file))
return 1
if((getline line < file) > 0) {
close(file)
return 1
}
return 0
}
function fisstdin(file)
{
if(file == "-" ||
file == "/dev/tty" ||
file == "/dev/stdin" ||
file == "/dev/fd/0" ||
file == "/proc/self/fd/0")
return 1
return 0
}
function fisstdout(file)
{
if(file == "/dev/stdout" ||
file == "/dev/fd/1" ||
file == "/proc/self/fd/1")
return 1
return 0
}
function fisstderr(file)
{
if(file == "/dev/stderr" ||
file == "/dev/fd/2" ||
file == "/proc/self/fd/2")
return 1
return 0
}