ANSI C to K&R converter written in Icon (source code provided)
Alan Grant Finlay
alanf at bruce.cs.monash.OZ.AU
Sun Jan 13 18:03:38 AEST 1991
After wasting my time trying to fix up a converter to work for my
C sources I decided to write my own. Icon seems to be the ideal language
for this job (provided you have a compiler/interpreter). I originally
thought I would do the job properly (i.e. using a C grammar) but after some
reflection I was soon put off (C's grammar is truly awful). The result is
yet another a converter that works for the author's programs. However one
advantage of this converter is that the algorithm is quite easy to follow
and could be easily adapted by Icon programmers to handle a greater subset of
C.
The program has the following limitations:
1) function prototypes are recognised by the sequence ");" with no
intervening spaces, newlines or comments.
2) function prototypes may not contain comments within the parameter list.
3) function definitions may have comments, spaces and newlines within the
parameter list however the output will win no awards for legibility.
4) the presence of function parameters in a function definition will mess up
the conversion of that parameter list (usually just removes the parameters).
There is a workaround as demonstrated by the following example:
#ifdef ANSI
void addts(void (*ts)())
{
#else
void addts(ts)
void (*ts)();
{
#endif
<the function body>
#ifdef ANSI
}
#else
}
#endif
This workaround requires that the non ANSI compiler ignores text which is
excluded by "#ifdef"s.
The algorithm is to divide the source text into a stream of substrings labelled
as either "considered" or "ignored". A pipeline is set up to process the
stream as follows:
control lines -> {all lines beginning with # are ignored}
comments -> {comments are ignored}
brackets -> {everything within curly brackets is ignored}
declarations -> {ignore all except function declarations (top level)}
compress -> {joins together consecutive segments of same label}
prototypes -> {prototype declarations have the parameters removed}
compress -> {joins together consecutive segments of same label}
parameter lists -> {function definition parameters are converted to K&R}
compress -> {joins together consecutive segments of same label}
The workaround in (4) above can now be seen to depend upon the brackets step.
The source follows next:
---<cut here>----------------------//----------------------------------------
# Program to convert C programs with ansi style function prototypes to the
# equivalent K&R form. Only top level declarations are converted.
# Written 7/1/91 by Alan Finlay, Computer Science, Monash University.
#
record ignored(body) # A program is processed as a sequence of
record considered(body) # ignored and considered parts.
global idset # identifier characters
global nidset # skip these to find next identifier
global spcset # white space characters
procedure main()
idset:= &lcase ++ &ucase ++ '0123456789_'
nidset:= ~idset
spcset:= ' \n\t\r'
every text:= compress(parms) do writes(text.body)
end
procedure parms()
# rearrange ansi style parameters to suit K&R syntax.
par:= "false" # not doing parameters now
parlist:= [] # parameter list is empty
currpar:= "" # Current parameter is bare
every x:= compress(protos) do
if type(x)=="ignored" then suspend x
else x.body ? while not pos(0) do
if par=="false" then
if text:= tab(find("()"))||move(2) then
suspend considered(text)
else {
if text:= tab(find("("))||move(1) then par:= "true"
else text:= tab(0)
suspend considered(text)
}
else {
if text:= tab(upto(',)')) then {
currpar||:=text;
# check for (void)
void:= "false"
currpar ? if (tab(many(spcset))|0) & ="void" &
(tab(many(spcset))|0) & pos(0) then
void:= "true"
if void=="true" & *parlist=0 &
&subject[&pos]==")" then {
currpar:= ""
par:= "false"
}
else {
# end of a parameter, extract the identifier
currpar ? {
if any(nidset) then tab(i:= many(nidset))
while tab(many(idset)) & (k:= i) &
tab(i:= many(nidset))
}
# update parlist and output the identifier
###<should not be needed> /i:= 1; /k:= 1 # for strange parameters only
if i=*currpar+1 then i:= k
j:= (currpar ? many(spcset)) | 1
put(parlist," "||currpar[j:0]||";\n")
currpar||:= move(1)
suspend considered(currpar[i:0])
currpar:=""
if &subject[&pos-1]==")" then {
# can release the saved parameters
suspend considered("\n")
suspend considered(!parlist)
parlist:= []
par:= "false"
}
}
}
else {
text:= tab(0) # the parameter continues
currpar||:= text
}
}
end
procedure protos()
# remove parameter types from prototypes.
# only recognises prototypes which end with ");" as prototypes.
# only works for prototypes which are not interrupted by comments etc.
# must be compressed afterwards for parms to work.
every x:= compress(decs) do
if type(x)=="ignored" then suspend x
else x.body ? while not pos(0) do
if text:= tab(upto('('))||move(1) then {
if not tab(find(");")) then text||:= tab(0)
suspend considered(text)
}
else {
text:= tab(0)
suspend considered(text)
}
end
procedure compress(seq)
# joins together adjacent text.
textc:= ""; texti:= ""
every x:= seq() do
if type(x)=="ignored" then { # save ignored and expel considered
texti||:= x.body
if *textc~=0 then suspend considered(textc)
textc:= ""
}
else { # save considered and expel ignored
textc||:= x.body
if *texti~=0 then suspend ignored(texti)
texti:= ""
}
if textc~=="" then return considered(textc) # only one of these
if texti~=="" then return ignored(texti) # can apply.
end
procedure decs()
# remove top level data declarations.
dec:= "false" # not in a declaration now
every x:= brackets() do
if type(x)=="ignored" then suspend x
else x.body ? while not pos(0) do
if dec=="false" then {
if text:= tab(find("typedef" | "auto" | "static" |
"extern" | "register" )) then dec:= "true"
else text:= tab(0)
suspend considered(text)
}
else {
if text:= tab(find(";"))||move(1) then dec:= "false"
else text:= tab(0)
suspend ignored(text)
}
end
procedure brackets()
# remove any text between { and } after comments removed.
bal:= 0 # start with balanced brackets
every x:= comments() do
if type(x)=="ignored" then suspend x
else x.body ? while not pos(0) do
if text:= tab(upto('{}')) then
if &subject[&pos]=="{" then {
bal+:= 1; text||:= move(1)
if bal=1 then suspend considered(text)
else suspend ignored(text)
}
else {
bal-:= 1; move(1)
suspend ignored(text)
if bal=0 then suspend considered("}")
else suspend ignored("}")
}
else {
text:= tab(0)
if bal=0 then suspend considered(text)
else suspend ignored(text)
}
end
procedure comments()
# Read std input and remove comments.
# For now compiler control lines are removed here also.
com:= "false" # not in a comment now
while line:= read()||"\n" do
if line[1]=="#" then suspend ignored(line)
else line ? while not pos(0) do
if com=="false" then {
if text:= tab(find("/*")) then com:= "true"
else text:= tab(0)
suspend considered(text)
}
else {
if text:= tab(find("*/"))||move(2) then com:= "false"
else text:= tab(0)
suspend ignored(text)
}
end
More information about the Alt.sources
mailing list