The assignment was to explore the capabilities of C to reproduce the block structuring scope rules of Pascal. The table below summarizes the valid and invalid calls among the functions in parts 1, 2 and 3 of the question.
Part 1 Part 2 Part 3
calls --> | f1 | f2 | f3 | f4 | f5 |
f1 | Yes | Yes | Yes | No | Yes |
f2 | Yes | Yes | No | Yes | No |
f3 | Yes | Yes | Yes | No | Yes |
f4 | Yes | Yes | No | Yes | No |
f5 | Yes | Yes | Yes | No | Yes |
Only one file is needed. A good solution is:
void f1(); void f2() {...} void f3() {...} void f1() {...}
but better is:
void f1(); void f2() {...} static void f3() {...} void f1() {...}
since any call to f3 in the body of f2 would be implicitly be declared
extern
, which then clashes with the static
definition.
Two files are needed:
file 1: | file 2: | |
extern void f2(); void f1(); static void f3() {...} void f1() {...} | extern void f1(); void f2(); static void f4() {...} void f2() {...} | |
A three file version is not quite as good:
file 1: | file 2: | file 3: | ||
extern void f2(); extern void f3(); void f1() {...} | extern f1(); void f2(); static void f4() {...} void f2() {...} | extern void f1(); extern void f2(); void f3() {...} | ||
since the linker will allow both f2 and f4 to call f3, even though it is not declared in file 2.
This turned out to be possible, even though an argument can be made that it should be impossible. A three file version is:
file 1: | file 2: | file 3: | ||
extern void f2(); extern void f3(); void f1() {...} | extern f1(); void f2(); static void f4() {...} void f2() {...} | extern void f1(); extern void f2(); void f3(); static void f5() {...} void f3() {...} | ||
The problem here is that f2 and f4 can still call f3 through the linker mechanism with implict definitions. The best solution only has two files:
file 1: | file 2: | |
extern void f2(); static void f3(); void f1() {...} static void f5() {...} static void f3() {...} | extern void f1(); void f2(); static void f4() {...} void f2() {...} | |
To turn the original Pascal into Modula 2, while preserving the valid and invalid calls, separate f2 and f3 in different modules:
definition module M1; export f1; import f2 from M2; procedure f3; { so that f1 can call f3 } end; implementation module M1; procedure f1; begin ... end; procedure f3; begin ... end; end; definition module M2; export f2; import f1 from M1; end; implementation module M2; procedure f2; begin ... end; end;