Here's something that should do the trick.
It's written in AWK. Any language I picked will be the wrong language. awk (or gawk) is everywhere.
Copy and paste it into a file (say, expand.awk).
awk -f expand.awk crunched.bas > uncrunched.bas
The script:
# License: Creative Commons 0 https://creativecommons.org/share-your-work/public-domain/cc0/
# Assumes all uppercase, and inability to embed quotes in quoted strings.
# Basic philosophy is to break out the quoted strings, and then process
# the individual statements separated by colons. Then put it all back
# together again.
BEGIN {
# list of keywords, EXCEPT for 'TO', it's handled special
keywords="OPEN:CLOSE:CLEAR:INPUT:IF:THEN:ELSE:FOR:NEXT:GOTO:GOSUB:LINE:READ:DEF:DIM:PRINT";
split(keywords, keylist, ":");
}
{
# split out the quoted strings. They will be even fields when done.
split($0, a, "\"");
for(i = 1; i <= length(a); i++) {
if (i % 2 == 0) {
# quoted string, skip it
continue;
}
# expand this chunk of the line
a[i] = expand_line(a[i]);
}
# rebuild the line
line = "";
for(i = 1; i <= length(a); i++) {
p = a[i]
if (i % 2 == 0) {
# quoted string, add the quotes back
p = "\"" p "\"";
}
line = line p;
}
# clean up PRINT" and INPUT"
# if these happen to be in quoted strings, kind of out of luck.
gsub("PRINT\"", "PRINT \"", line);
gsub("INPUT\"", "INPUT \"", line);
# out it goes
print line;
}
function expand_line(line, i, k, l) {
# break up in to statements
split(line, statements, ":");
for(i = 1; i <= length(statements); i++) {
# expand the keywords for each statement
for(k = 1; k <= length(keylist); k++) {
statements[i] = expand(statements[i], keylist[k]);
}
# because of GOTO, TO (of FOR TO NEXT) is handled special
statements[i] = expand_to(statements[i]);
}
# rejoin with the :
line = statements[1];
l = length(statements);
if (l == 1) {
return line;
}
for(i = 2; i <= l; i++) {
line = line ":" statements[i];
}
return line;
}
function expand(line, keyword) {
# for each key word, check for space at each end
rx=keyword "[^ ]";
if (match(line, rx) > 0) {
gsub(keyword, keyword " ", line);
}
rx="[^ ]" keyword;
if (match(line, rx) > 0) {
gsub(keyword, " " keyword, line);
}
return line;
}
function expand_to(line) {
# hide any GOTOs
gsub("GOTO", "xxx", line);
# fix the TO
if(match(line, "TO[^ ]") > 0) {
gsub("TO", "TO ", line);
}
if(match(line, "[^ ]TO") > 0) {
gsub("TO", " TO", line);
}
# put the GOTOs back
gsub("xxx", "GOTO", line);
return line;
}
10 LETA=NOTA
would already give a headache in PET-BASIC - or worse,10 A=BMODC
which in PET BASIC would have to stay like that, while as MSX it needs to be turned into10 A = B MOD C
. I guess it would need to be a tool tuned to a certain dialect and work according to this dialects keyword list. Not to mention the classicGO TO
vs.GOTO
(Oh, and no, I do not know any existing generic tool. I'd just sit down and sketch up a bunch of Perl)