Commit acd5d55a authored by Martin Wierich's avatar Martin Wierich
Browse files

new modules to build a cocl that reads Clean 1.3 sources and as a sideeffect

produces sources in which the explicit import statements have been
ported to 2.0 syntax ("coclPort" application)
parent dc3967ff
coclPort - a tool to port Clean 1.3 programs to Clean 2.0
----------------------------------------------------------------------------
Folder contents
---------------
coclPort.exe - a tool to port Clean 1.3 programs to Clean 2.0
StdEnvPort - a necessary standard environment
help.txt - this file
Why to use coclPort
-------------------
Clean 2.0 is not downward compatible to Clean 1.3.x: Due to changes in the
syntax there are Clean 1.3.x programs that cannot be compiled with the new
Clean 2.0 compiler. Fortunately these changes in syntax are rather small.
The most important syntax modification is concerned with explicit
import statements. These are statements like "from m import x". With the
new syntax it's possible to write e.g.
from m import T, :: T (C1), :: T2 (..), class ==, instance == Int
Such a statement causes the following symbols to be imported: the function T,
the algebraic type T with it's constructor C1, the algebraic type T2 with all
it's constructors, the class == and it's instance Int. See the Clean 2.0
language report for further information.
Purpose of coclPort is to help you to port Clean 1.3.x programs to
this new syntax.
What coclPort does
------------------
coclPort is indeed a Clean 2.0 compiler that is modified such that it can
handle Clean 1.3 import syntax. Therefore it cannot handle 2.0 import syntax.
To port a 1.3 module
just compile it with the coclPort compiler. Then for this "original" module
a copy of that module is written out in which the explicit import
statements have been adapted to Clean 2.0 syntax. This copy will be written
into a folder named "PortedModules" which will be a subfolder of the folder
that contains the original module.
Due to some other slight changes
in the language it is possible that your Clean 1.3.x modules will not be accepted
by coclPort. For instance the "String" type has become a
basic type (so you can't import it explicitly).
In that case you have to modify the contents of the original module
and try again.
coclPort has some insufficiencies. See section "Insufficiencies".
The preprocessor
----------------
We think that for a while it should be made possible to let every source code
file be compiled with both the 1.3 and the 2.0 compiler. How can this be
achieved when the 2.0 compiler is not fully downward compatible? With a
preprocessor of course. The simple preprocessor that we have built into
coclPort and into the 2.0 compiler as well allows to distinguish parts
of code that are either ignored by
the 1.3 compiler or by the 2.0 compiler. To illustrate this we show what
coclPort will produce for a file that contains the following statement:
from module_name import ==
The produced output might look like this:
//1.3
from module_name import ==
//3.1
/*2.0
from module_name import class ==, instance == Int, instance == Real
0.2*/
For the 2.0 compiler this would be the same as
from module_name import class ==, instance == Int, instance == Real
because the preprocessor will take care that everything between "//1.3" and
"//3.1" will be ignored and that everything between "/*2.0" and "0.2*/"
will _not_ be treated as a comment.
For the 1.3 this would be the same as
from module_name import ==
You can use this feature to keep your files both compilable with 1.3 and
with 2.0. If you don't want your sources cluttered with old 1.3 stuff then
you can use the "rmPreprop" application to extract the pure 1.3 or 2.0
contents.
Note that the "//1.3", "//3.1", "/*2.0" and "0.2*/" brackets have to be
the first characters of the line (no preceeding whitespace characters are
allowed). Otherwise they will be ignored. Furtheron such sections shouldn't
be nested or overlapping.
How to use this program
-----------------------
You have to tell the CleanIDE that for compiling a module it should
launch "coclPort.exe" instead "cocl.exe" (BTW "cocl" means "Concurrent
Clean"). In the CleanIDE select an environment that uses the Clean 2.0
compiler. Now select "Environment/Edit Current" and select the "tools"
button. The field at "compiler:" determines where the CleanIDE finds the
Clean commpiler (cocl.exe). You can use this field to ensure that this entry indeed
refers to the Clean 2.0 compiler and not e.g. to a Clean 1.3 compiler:
If it is the Clean 2.0 compiler then there must be a file named "backend.dll"
in the folder that is shown there. Put the file "coclPort.exe" into the same
folder and change the entry in the field at "compiler:" from "cocl.exe"
into "coclPort.exe". Now the CleanIDE will use coclPort as the compiler.
But coclPort needs another standard environment than cocl. You have to replace
the path to StdEnv to a path to StdEnvPort. You can set paths with the
"Environment/Edit Current" or "Project/Project Options" dialog.
Now simply try to compile your project.
Insufficiencies
---------------
Certain restrictions are imposed on the source code that should be ported:
- For every explicit import statement the "from" token should be the first
non white space character in that line. After some white space characters
the line should then contain the module name (and not e.g. a comment first).
This excludes the following import statements
/* comment */ from module_name import f
from /* comment */ module_name import f
from
module_name import f
An explicit import statement that is outcommented with "//" will work fine,
since it will be ignored anyway:
// from module_name import f
- coclPort has problems when a line within a comment begins
with the "from" token this can be mistaken as an explicit import statement
by this program. As a consequence in the "ported" version there might appear
an explicit import statement within that comment that should appear somewhere
else. Example:
import aphrotisaica /* And I knew I loved her
from that moment on until forever */
However we think that when something goes wrong with this program it should
be easy to adjust the ported modules manually after it has been tried to
compile them.
\ No newline at end of file
definition module portToNewSyntax
from StdMisc import abort
//1.3
from StdFile import Files
from StdString import String
from scanner import SearchPaths
//3.1
/*2.0
from StdFile import :: Files
from scanner import :: SearchPaths
0.2*/
import checksupport
switch_port_to_new_syntax port dont_port :== port
cTabWidth :== 4
writeExplImportsToFile :: !String ![([Declaration],a)] !{#u:DclModule} !*CheckState
-> (!{#u:DclModule},!.CheckState)
createPortedFiles :: !String !SearchPaths !*Files -> (!Bool, !*Files)
implementation module portToNewSyntax
import StdEnv, scanner, Directory, merge, checksupport
switch_port_to_new_syntax port dont_port :== port
cTabWidth :== 4
resultFolderName =: "PortedModules"
createPortedFiles :: !String !SearchPaths !*Files -> (!Bool, !*Files)
createPortedFiles fileName searchPaths files
# (ok, files)
= case findDirOfModule fileName searchPaths files of
(No, files)
-> (False, files)
(Yes path, files)
# (ok, files)
= ensureSubDirExists path fileName files
| not ok
-> (ok, files)
# (ok1, files)
= tryToCreatePortedFile fileName "icl" path files
(ok2, files)
= tryToCreatePortedFile fileName "dcl" path files
-> (ok1&&ok2, files)
(_, files)
= fremove (RelativePath [PathDown "icl.txt"]) files
(_, files)
= fremove (RelativePath [PathDown "dcl.txt"]) files
= (ok, files)
tryToCreatePortedFile :: !String !String !Path !*Files -> (!Bool,!*Files)
tryToCreatePortedFile file_name suffix path ms_files
# with_suffix
= file_name+++"."+++suffix
# (old_module_filename, ms_files)
= pathToPD_String (pathAppend path [PathDown with_suffix]) ms_files
(ok, old, ms_files) = fopen old_module_filename FReadData ms_files
| not ok
= (ok, ms_files)
# (new_module_filename, ms_files)
= pathToPD_String (pathAppend path [PathDown resultFolderName,
PathDown with_suffix]) ms_files
inferred_filename = suffix+++".txt"
(ok1, inferred, ms_files) = fopen inferred_filename FReadText ms_files
(ok2, new, ms_files) = fopen new_module_filename FWriteText ms_files
| not (ok1&&ok2)
= (False, ms_files)
# (old, inferred, new) = mergeFiles old inferred new
(ok3, ms_files) = fclose old ms_files
(ok4, ms_files) = fclose inferred ms_files
(ok5, ms_files) = fclose new ms_files
= (ok3&&ok4&&ok5, ms_files)
findDirOfModule :: !{#Char} !SearchPaths *Files -> (!Optional Path, !*Files)
findDirOfModule fileName searchPaths files
# filtered_locations
= filter (\(moduleName,pd_path) -> moduleName == fileName) searchPaths.sp_locations
| not (isEmpty filtered_locations)
# ((ok, path), files)
= pd_StringToPath (snd (hd filtered_locations)) files
| not ok
= (No, files)
= (Yes path, files)
= loop searchPaths.sp_paths (fileName+++".icl") files
where
loop :: ![String] !String !*Files -> (!Optional Path, !*Files)
loop [] _ files
= (No, files)
loop [pd_path:pd_paths] fileName files
# ((ok, path), files)
= pd_StringToPath pd_path files
| not ok
= (No, files)
# ((dir_error, _), files)
= getFileInfo (pathAppend path [PathDown fileName]) files
| dir_error == NoDirError
= (Yes path, files)
= loop pd_paths fileName files
pathAppend (RelativePath p) x = RelativePath (p++x)
pathAppend (AbsolutePath diskname p) x = AbsolutePath diskname (p++x)
ensureSubDirExists path fileName files
# path_result_folder = pathAppend path [PathDown resultFolderName]
((err_code, _), files) = getFileInfo path_result_folder files
(errorCode, files) = case err_code of
NoDirError -> (NoDirError, files)
_ -> createDirectory path_result_folder files
= (errorCode==NoDirError, files)
writeExplImportsToFile :: !String ![([Declaration],a)] !{#u:DclModule} !*CheckState
-> (!{#u:DclModule},!.CheckState)
writeExplImportsToFile file_name si_explicit dcl_modules cs
# (file, cs)
= openFile file_name cs
(dcl_modules, file)
= foldSt (write_expl_import (flatten (map fst si_explicit))) (reverse si_explicit) (dcl_modules, file)
= (dcl_modules, closeFile file cs)
write_expl_import all_expl_imp_decls (declarations, _) (dcl_modules, file)
# (declaration_strings, dcl_modules)
= mapFilterYesSt (decl_to_opt_string all_expl_imp_decls) (reverse declarations) dcl_modules
= (dcl_modules, fwriteNewSyntax declaration_strings file)
// only for portToNewSyntax
decl_to_opt_string all_expl_imp_decls decl=:{dcl_ident, dcl_index, dcl_kind=STE_Imported ste_kind def_mod_index}
dcl_modules
= imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index ste_kind def_mod_index
dcl_modules
decl_to_opt_string _ {dcl_ident, dcl_kind=STE_FunctionOrMacro _} dcl_modules
= (Yes dcl_ident.id_name, dcl_modules)
decl_to_opt_string all_expl_imp_decls decl dcl_modules
= abort ("decl_to_opt_string failed"--->decl)
// only for portToNewSyntax
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index STE_Constructor def_mod_index
dcl_modules
= (No, dcl_modules)
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index STE_Member def_mod_index
dcl_modules
= (No, dcl_modules)
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index STE_DclFunction def_mod_index
dcl_modules
= (Yes dcl_ident.id_name, dcl_modules)
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index STE_Class def_mod_index
dcl_modules
= (Yes ("class "+++dcl_ident.id_name+++"(..)"), dcl_modules)
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index (STE_Instance _) def_mod_index
dcl_modules
# ({ins_type}, dcl_modules)
= dcl_modules![def_mod_index].dcl_common.com_instance_defs.[dcl_index]
= (Yes ("instance "+++dcl_ident.id_name+++" "+++
separated " " (map type_to_string ins_type.it_types)), dcl_modules)
imported_decl_to_opt_string all_expl_imp_decls dcl_ident dcl_index STE_Type def_mod_index
dcl_modules
# ({td_rhs}, dcl_modules)
= dcl_modules![def_mod_index].dcl_common.com_type_defs.[dcl_index]
dcl_string
= ":: "+++(case td_rhs of
AlgType constructors
-> dcl_ident.id_name+++constructor_bracket def_mod_index all_expl_imp_decls constructors
RecordType _
-> dcl_ident.id_name+++"{..}"
_
-> dcl_ident.id_name)
= (Yes dcl_string, dcl_modules)
// only for portToNewSyntax
type_to_string (TA {type_name} _) = possibly_replace_predef_symbols type_name.id_name
type_to_string (TB type) = toString type
type_to_string (TV {tv_name}) = tv_name.id_name
type_to_string x = abort ("bug nr 945 in module check"--->x)
possibly_replace_predef_symbols s
| s=="_list"
= "[]"
| s % (0,5) == "_tuple"
= (toString ['(':repeatn ((toInt (s%(6, (size s) - 1))) - 1) ','])+++")"
| s=="_array"
= "{}"
| s=="_!array"
= "{!}"
| s=="_#array"
= "{#}"
= s
instance toString BasicType
where
toString BT_Int = "Int"
toString BT_Char = "Char"
toString BT_Real = "Real"
toString BT_Bool = "Bool"
toString BT_Dynamic = "Dynamic"
toString BT_File = "File"
toString BT_World = "World"
toString _ = abort "bug nr 346 in module check"
// only for portToNewSyntax
separated _ []
= ""
separated separator [h:t]
= foldl (\l r->l+++separator+++r) h t
constructor_bracket def_mod_index all_expl_imp_decls constructors
# expl_imp_constructor_strings
= [ ds_ident.id_name \\ {ds_ident} <- constructors
| is_expl_imported_constructor def_mod_index ds_ident all_expl_imp_decls ]
| isEmpty expl_imp_constructor_strings
= ""
= "("+++separated "," expl_imp_constructor_strings+++")"
// only for portToNewSyntax
is_expl_imported_constructor def_mod_index ds_ident []
= False
is_expl_imported_constructor def_mod_index ds_ident [{dcl_ident, dcl_kind=STE_Imported STE_Constructor def_mod_index2}:_]
| dcl_ident==ds_ident && def_mod_index==def_mod_index2
= True
// GOTO next alternative
is_expl_imported_constructor def_mod_index ds_ident [h:t]
= is_expl_imported_constructor def_mod_index ds_ident t
fwriteNewSyntax importStrings file
| isEmpty importStrings
= fwrites "import @#$@@!!" file
# with_commas = (map (\s->s+++", ") (butLast importStrings))++[last importStrings+++";"]
lines = split_in_lines 12 with_commas [] []
lines = [hd lines:[["\t":line]\\ line<-tl lines]]
line_strings = [ foldl (+++) " " (line++["\n"]) \\ line<-lines ]
= fwrites (foldl (+++) "import" line_strings) file
where
max_line_length = 80
split_in_lines i [] inner_accu outer_accu
# accu = if (isEmpty inner_accu) outer_accu [reverse inner_accu:outer_accu]
= reverse accu
split_in_lines i [h:t] inner_accu outer_accu
# s = size h
| s+i>max_line_length
| isEmpty inner_accu
= split_in_lines (s+i) t [h] outer_accu
= split_in_lines (s+cTabWidth) t [h] [inner_accu:outer_accu]
= split_in_lines (s+i) t [h:inner_accu] outer_accu
// only for portToNewSyntax
butLast [] = []
butLast [x] = []
butLast [h:t] = [h: butLast t]
// MW: fake..
openFile file_name cs
# world = bigBang
(ok, newFile, world) = fopen file_name FWriteText world
cs = forget world cs
cs = case ok of
True -> cs
_ # cs_error = checkError "" ("can't open file \""+++file_name+++" in current directory.") cs.cs_error
-> { cs & cs_error=cs_error }
= (newFile, cs)
closeFile file cs
# world = bigBang
(ok, world) = fclose file world
= forget world cs
bigBang :: .World
bigBang = cast 1
// creates a world from scratch
forget :: !.x !.y -> .y
forget x y = y
cast :: !.a -> .b
cast a
= code
{
pop_a 0
}
// ..fake
This file explains how to build a valid cocl that reads
Clean 1.3 sources and as a sideeffect produces sources
in which the explicit import statements have been
ported to 2.0 syntax ("coclPort" application)
The module portToNewSyntax appears twice: in the "compiler"
folder and in the "portToNewSyntax" folder. Iff you want
to build coclPort then set the paths in such a way that
the portToNewSyntax.dcl/icl files from the portToNewSyntax
folder are preferred over the same files in the compiler
folder.
Further set the "switch_import_syntax" macro in module
syntax to "one_point_three"
The "switch_port_to_new_syntax" macro in module
portToNewSyntax is used to ed/disable the porting
facilities:
portToNewSyntax/portToNewSyntax.dcl: port
compiler/portToNewSyntax.dcl: dont_port
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment