Yes, I did use # as a comment in awk and a preprocessor line in C. I only had to use a few simple #define's take make my polyglot program work. Many other things have the same syntax in both languages, such as the control structures and printf(). Here's a basic C/awk program that shows how easily it can be done:
Code: #include <stdio.h>
#include <stdlib.h>
#define BEGIN int main()
BEGIN {
printf("Hello world!\n");
exit(0);
}
For practical use, awk is good for more than just splitting lines. I generally use sed instead for splitting lines. Here's the source code generator script that I mentioned:Code: #! /usr/bin/awk -f
# num name words flags
# Example:
# 3 read 5 0
BEGIN {
maxnum = 0
}
/^[^#]/ && ! /^$/ {
num=$1;
name=$2;
words=$3;
flags=$4;
if (NF < 4) {
printf("Warning: line %d has too few fields; skipping.\n", NR) >"/dev/stderr";
next
} else if (NF > 4) {
printf("Warning: line %d has too many fields; skipping.\n", NR) >"/dev/stderr";
next
}
if (calls[num, "name"] != "") {
printf("syscall %d (%s) is redefined! (Redefinition at line %d)\n", num, name, NR) > "/dev/stderr";
err = 1
exit 1
}
calls[num, "name"] = name;
calls[num, "words"] = words;
calls[num, "flags"] = flags;
if (num > maxnum) { maxnum = num; }
}
END {
if (err) exit;
# preamble
print("/*\n * NOTICE: This file is auto-generated.\n * DO NOT MODIFY THIS FILE!\n */\n");
print("#include \"sysent.h\"");
print("#include \"punix.h\"");
print("");
# prototypes for all syscalls
printf("void sys_NONE();\n");
for (i = 0; i <= maxnum; ++i) {
if (calls[i, "name"] == "") {
calls[i, "name"] = "NONE";
calls[i, "words"] = "0";
calls[i, "flags"] = "0";
} else {
printf("void sys_%s();\n", calls[i, "name"]);
}
}
print("");
# sysent[] array
print("STARTUP(const struct sysent sysent[]) = {");
for (i = 0; i <= maxnum; ++i) {
printf("\t{ %d, sys_%s, %s },", calls[i, "words"], calls[i, "name"], calls[i, "flags"]);
if ((i % 5) == 0) {
printf("\t/* %d */", i);
}
print "";
}
print("};");
print("");
print("const int nsysent = sizeof(sysent) / sizeof(struct sysent);");
print("");
# sysname[] array
print("STARTUP(const char *const sysname[]) = {");
for (i = 0; i <= maxnum; ++i) {
printf("\t\"%s\",", calls[i, "name"]);
if ((i % 5) == 0) {
printf("\t/* %d */", i);
}
print "";
}
print("};");
}
It takes lines containing the system call number, name, number of argument words, and additional flags, and it outputs an expanded (non-sparse) C array containing the same information. For example, if you give it this input:Code: 1 exit 1 0
2 fork 0 0
3 read 5 0
4 write 5 0
5 open 4 0
6 close 1 0
#9 link 4 0
#10 unlink 2 0
12 chdir 2 0
you'll get this output:Code: /*
* NOTICE: This file is auto-generated.
* DO NOT MODIFY THIS FILE!
*/
#include "sysent.h"
#include "punix.h"
void sys_NONE();
void sys_exit();
void sys_fork();
void sys_read();
void sys_write();
void sys_open();
void sys_close();
void sys_chdir();
STARTUP(const struct sysent sysent[]) = {
{ 0, sys_NONE, 0 }, /* 0 */
{ 1, sys_exit, 0 },
{ 0, sys_fork, 0 },
{ 5, sys_read, 0 },
{ 5, sys_write, 0 },
{ 4, sys_open, 0 }, /* 5 */
{ 1, sys_close, 0 },
{ 0, sys_NONE, 0 },
{ 0, sys_NONE, 0 },
{ 0, sys_NONE, 0 },
{ 0, sys_NONE, 0 }, /* 10 */
{ 0, sys_NONE, 0 },
{ 2, sys_chdir, 0 },
};
const int nsysent = sizeof(sysent) / sizeof(struct sysent);
STARTUP(const char *const sysname[]) = {
"NONE", /* 0 */
"exit",
"fork",
"read",
"write",
"open", /* 5 */
"close",
"NONE",
"NONE",
"NONE",
"NONE", /* 10 */
"NONE",
"chdir",
};
I used awk's associative arrays to do this. Its automatic line-reading loop, pattern matching, and word splitting help too. About one third of the script's lines are for printing source code or errors/warnings, so I'd be surprised if it could be made much shorter in another language like Python.