Commit 1e943b4e authored by John van Groningen's avatar John van Groningen
Browse files

delete portToNewSyntax

parent ef3dc036
This file describes differences between Clean 1.3.x and Clean 2.x. It is aimed
to help you to port Clean 1.3.x programs to Clean 2.x
First of all: Clean 2.x is not downward compatible with Clean 1.3.x. Probably
you have to change your 1.3.x sources to get them through the Clean 2.x
To be able to write Clean programs that can be compiled with both compiler
versions we have built a simple preprocessor into the Clean 2.x compiler. This
feature allows you to exclude certain parts of your source, depending on which
compiler version you use. See section "The Preprocessor" for details.
The following sections describe the differences in detail.
New Features in Clean 2.0
Clean 2.x has dynamic types, multiparameter type classes and generic classes.
For these items see the Clean language report.
With Clean 2.x cyclic dependencies between dcl modules are allowed.
Differences in Expression Syntax
There is no strict let-before expression (let!) anymore in Clean 2.x. You still
can enforce strict evaluation using the strict hash let (#!).
Differences in the Type System
1. Array handling has become different, see section "Differences in the
Standard environment
There is a difference in resolving overloading. Consider the following code:
class c a :: a -> a
instance c [Int]
c [1] = [2]
f [x:xs]
= c xs
Although this is accepted by Clean 1.3.x, Clean 2.x will complain
Overloading error [...,..,f]: c no instance available of type [a]
The Clean 2.x compiler applies no type unification after resolving
overloading. So c is in function f applied to a list with a polymorph element
type ([a]). And this is considered to be different from the instance type
[Int]. If you give f the type [Int] -> [Int] the upper code will be accepted.
Clean 2.x handles uniqueness attributes in type synonyms different than
Clean 1.3.x. Consider the following definitions:
:: ListList a :== [[a]]
f :: *(ListList *{Int}) -> *{Int}
f [[a]] = { a & [0]=0 }
What does the ListList type synonym stand for in the type of f? In Clean 1.3.x the
ListList type synonym was expanded to
f :: *[*[*{Int}]] -> *{Int}
whereas Clean 2.x expands it to
f :: *[[*{Int}]] -> *{Int}
This yields a uniqueness error in Clean 2.x because the inner list is shared
but contains a unique object. Clean 1.3.x accepts the upper code.
This problem happens only with type synonyms that have attributes "inbetween".
What does this mean? An "inbetween" attribute is neither the "root" attribute
nor the attribute of an actual argument.
E.g. with the upper type synonym, the formal argument "a" is substituted with
*{Int}. Note that also the "*" is substituted for "a". Further the whole
result of expanding the type synonym gets the "root" attribute. Because we
wrote *(ListList ...) the root attribute is "*". So far the result of
expanding *(ListList *{Int}) is *[u:[*{Int]]. "u" is an attribute "inbetween"
because it is neither the root attribute nor the attribute of a formal
argument. Such attributes are made _non_unique_ in Clean 2.x and this is why
the upper code is not accepted. The code will be accepted if you redefine
ListList to
:: ListList a :== [*[a]]
The String type has become a basic type. As a consequence you cannot import
this type explicitly:
from StdString import :: String
is not valid.
The Clean 1.3.x compiler has chosen lazy arrays whenever there was a choice.
The Clean 2.x compiler will complain in these cases that internal overloading
could not be solved. E.g.
Start = size {1}
is accepted by Clean 1.3.x but not by Clean 2.x.
Some bugs in the uniqueness typing system were fixed.
Differences in the Module System
The syntax and semantics of explicit import statements has been completely
revised. With Clean 2.x it is
possible to discriminate the different namespaces in import statements.
In Clean 1.3.x the following statement
from m import F
could have caused the import of a function F together with a type F and a
class F with all its instances from m. The syntax of Clean 2.x import
statements is given below in the style that is also used in the Clean language
ExplicitImportDef = "from" ModuleName "import" {Imports}-list ";"
Imports = FunctionName
| "::" TypeName [ConstructorsOrFields]
| "class" ClassName [Members]
| "instance" ClassName {TypeName}+
ConstructorsOrFields = "(" ".." ")"
| "{" ".." "}"
| "(" {ConstructorName}-list ")"
| "{" {FieldName}-list "}"
Members = "(" ".." ")"
| "(" {MemberName}-list ")"
[notion] means that the presence of notion is optional
{notion}+ means that notion occurs at least once
{notion}-list means one or more occurrences of notion separated by commas
Terminals are enclosed in apostrophes. All missing nonterminals are simply
For example the following import statement
from m import F, :: T1, :: T2(..), :: T3(C1, C2), :: T4{..},
:: T5{field1, field2}, class C1, class C2(..),
class C3(mem1, mem2)
causes the following declarations to be imported:
the function or macro F, the type T1, the algebraic type T2 with all it's
constructors that are exported by m, the algebraic type T3 with it's
constructors C1 and C2, the record type T4 with all it's fields that are
exported by m, the record type T5 with it's fields field1 and field2,
the class C1, the class C2 with all it's members that are exported by m,
the class C3 with it's members mem1 and mem2.
There is a tool called "coclPort" that is able to automatically convert
Clean sources with 1.3.x import syntax to sources with 2.x syntax.
Hint: The appearance of the compiler error message "... not exported as a
function/macro by the specified module" is not a compiler bug. You probably
forgot the "::" before a type identifier
Differences in the standard environment (StdEnv)
We removed some definitions from the StdEnv.
instance toString [a] // in StdList
instance < (a,b), instance < (a,b,c) // in StdTuple
instance == (a,b), instance == (a,b,c) // in StdTuple
We defined a module port.dcl/icl. This module contains these definitions.
We changed the behaviour of the following functions:
take in StdList
Clean 1.3.x: i<0 => take i x==x
Clean 2.x : i<0 => take i x==[]
instance mod Int in StdInt
In Clean 2.0: sign m==sign (n mod m)
This keeps the invariant n == m*(n div m) + (n mod m)
where div is division truncated to minus infinity
This is the behaviour people expect from a mod operator.
In Clean 1.3 the following invariant was kept:
n == m*(n/m) + (n mod m) where "/" truncates to 0.
Note that a difference only appears if exactly one
argument is negative
We changed the argument order of the functions (s)freads, (s)fseek, freopen
in module StdFile such that the File argument is the last argument.
We fixed some bugs:
- the createArray instances in module StdArray don't crash anymore for
negative size arguments.
- gcd's results are now correct for the case that the result is in the range of
Int. In Clean 1.3.x it was not correct in cases when one argument was
Array handling has become different. In Clean 2.x the Array class is a
multiparameter class, whose first argument type is the array and whose
second argument type is the array element. Therefore the following classes
have vanished:
ArrayElem, _uselectf, _uselectn, _uselectl, _updatei, _createArrayc,select_u
uselect_u, uselectf_u, uselectn_u, uselectl_u, updatei_u, size_u, usize_u
update_u, createArray_u, createArrayc_u, replace_u
Example: Assume a function "f" that does something with arrays that had
the following type in Clean 1.3:
f :: (a b) -> b | select_u b & Array .a
"(a b)" stands for "an array (can be strict, lazy or unboxed) with element b".
In Clean 2.x this simply becomes:
f :: (a b) -> b | Array a b
Miscellaneous Differences
For multiparameter type classes a small change in the syntax for instance
definitions was necessary. In Clean 1.3.x it was assumed that every instance
definition only has one type argument. So in the following 1.3.x instance
instance c T1 T2
the type "(T1 T2)" was meant (the type T1 with the argument T2). If this was
the intention then this should be written in Clean 2.x as
instance c (T1 T2)
otherwise T1 and T2 will be interpreted as two types.
Anonymous uniqueness attributes in type contexts are not allowed in Clean 2.x.
So in the following function type
f :: a | myClass .a
simply remove the point.
There are differences in some libraries. See the library's documentation.
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
into the 2.0 compiler has 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 how you
could use the preprocessor to cope with the principial incompatibilities
in conjunction with arrays. Let's suppose you have a function defined like this
g a i
= a.[i]
and you want to write down the type for that function.
This function has really different principial types in Clean 1.3.x and Clean 2.x.
Your code is still compilable with both compiler versions if you write the
g :: u:(a v:b) .Int -> v:b | select_u b & Array .a, [u <= v]
g :: .(a u:b) v:Int -> u:b | Array a b, [v <= u]
g a i
= a.[i]
For the 2.0 compiler this would be the same as
g :: .(a u:b) v:Int -> u:b | Array a b, [v <= u]
g a i
= a.[i]
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
g :: u:(a v:b) .Int -> v:b | select_u b & Array .a, [u <= v]
g a i
= a.[i]
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 should
neither be nested nor overlapping.
Finally we have written an application "rmPreprop" that can be used to remove
all these preprocessor directives when you don't want them anymore.
We will remove the preprocessor in the future. Regard it as
a temporary feature.
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 (see the documentation about differences between Clean
1.3 and Clean 2.0 programs).
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 have built a primitive preprocessor into the Clean 2.0 compiler
that allows conditional compilation. This enables you to write sources
that can be compiled with Clean 2.0 and Clean 1.3 regardless of
the incompatibility of these compiler versions. coclPort will generate
preprocessor directives, too, e.g consider the following statement:
from module_name import ==
The produced output might look like this:
from module_name import ==
from module_name import class ==, instance == Int, instance == Real
The preprocessor is explained in the documentation about differences
between Clean 1.3 and Clean 2.0.
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 should be sure that this entry indeed
refers to the Clean 2.0 compiler and not e.g. to a Clean 1.3 compiler!
Put the file "coclPort.exe" into the same folder as "cocl.exe"
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.
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
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. The ported import statements will appear at least _somewhere_
in the generated sources.
\ No newline at end of file
definition module merge
mergeFiles :: !*File !*File !*File -> (!.File,!.File,!.File)
implementation module merge
import StdEnv, RWSDebug, StdArrayExtensions
from syntax import Optional, Yes, No
import utilities, portToNewSyntax
mergeFiles :: !*File !*File !*File -> (!.File,!.File,!.File)
mergeFiles old inferred new
# (lines_within_comment, old)
= lines_in_comment old
= merge_files lines_within_comment 0 old inferred new
merge_files :: ![Int] !Int !*File !*File !*File -> (!.File,!.File,!.File)
merge_files lines_within_comment line_nr old inferred new
# (end, old) = fend old
| end
# (inferred, new)
= copy_rest inferred new
= (old, inferred, new)
# (line, old) = my_freadline old
line_l = [ ch \\ ch<-:line ]
| not (begins_with_from line_l) || isMember line_nr lines_within_comment
= merge_files lines_within_comment (line_nr+1) old inferred (fwrites line new)
# new = fwrites ("//1.3\n"+++(complete_line line)) new
(new_line_nr, module_names, old, new)
= copy_original_from_statements line_nr line_l [] old new
// insert inferred part
new = fwrites ("//3.1\n/*2.0\n") new
(inferred, new)
= foldSt insert_inferred_from_stmnt module_names (inferred, new)
= merge_files lines_within_comment new_line_nr old inferred (fwrites "0.2*/\n" new)
copy_original_from_statements line_nr line_l mod_names_accu old new
# (left_space, module_name) = get_ls_and_mn line_l
(line_nr, opt_next_from_statement, old, new) = layout_skip line_nr left_space old new
= case opt_next_from_statement of
-> (line_nr, reverse [module_name:mod_names_accu], old, new)
Yes line_l`
-> copy_original_from_statements line_nr line_l` [module_name:mod_names_accu]
old new
insert_inferred_from_stmnt module_name (inferred, new)
# (first_line_of_import, inferred) = my_freadline inferred
new = foldSt fwrites ["from ", module_name, " ", first_line_of_import] new
= copy_rest_of_import inferred new
begins_with_from line_l
# without_spaces = dropWhile isSpace line_l
= case without_spaces of
['from'] -> True
['from',ch:_] -> not (isAlphanum ch || isMember ch ['`_'])
_ -> False
get_ls_and_mn line_l
# (spaces, rest1) = span isSpace line_l
without_from = drop 4 rest1
(_, rest2) = span isSpace without_from
module_name = takeWhile (not o isSpace) rest2
= (space_count spaces 0, toString module_name)
space_count [] _
= 0
space_count [' ':spaces] modTabWidth
= 1+(space_count spaces ((modTabWidth+1) mod modTabWidth))
space_count ['\t':spaces] modTabWidth
= (cTabWidth-modTabWidth)+(space_count spaces 0)
space_count ['\n':_] _
= 0
layout_skip :: !Int !Int !*File !*File -> (!Int, !Optional [Char], !.File, !.File)
layout_skip line_nr left_space old new
# (end, old) = fend old
| end
= (line_nr, No, old, new)
# (cur_pos, old) = fposition old
(line, old) = my_freadline old
line_l = [ ch \\ ch<-:line ]
spaces = takeWhile isSpace line_l
| space_count spaces 0<=left_space
| begins_with_from line_l
= (line_nr+1, Yes line_l, old, fwrites (complete_line line) new)
= (line_nr+1, No, snd (fseek old cur_pos FSeekSet), new)
= layout_skip (line_nr+1) left_space old (fwrites (complete_line line) new)
copy_rest_of_import :: !*File !*File -> (!.File, !.File)
copy_rest_of_import inferred new
# (end, inferred) = fend inferred
| end
= (inferred ,new)
# (cur_pos, inferred) = fposition inferred
(line, inferred) = my_freadline inferred
| line % (0,5)=="import"
= (snd (fseek inferred cur_pos FSeekSet), new)
= copy_rest_of_import inferred (fwrites line new)
complete_line line
| line.[size line-1]<>'\n'
= line+++"\n"
= line
copy_rest inferred new
# (end, inferred) = fend inferred
| end
= (inferred, new)
# (line, inferred)
= my_freadline inferred
= copy_rest inferred (fwrites line new)
lines_in_comment file
# (cur_pos, file)
= fposition file
(rev_lines_within_comment, file)
= get_lic 0 [] file
= (reverse rev_lines_within_comment, snd (fseek file cur_pos FSeekSet))
get_lic :: !Int ![Int] !*File -> (![Int], !*File)
get_lic line_nr line_nr_accu file
# (end, file)
= fend file
| end
= (line_nr_accu, file)
# (line, file)
= my_freadline file
= [ch \\ ch<-:line]
# (bwc, rest)
= begins_with_comment line_l
| bwc
= consider_comment 1 rest line_nr line_nr_accu file
= get_lic (line_nr+1) line_nr_accu file
begins_with_comment ['//1.3':rest]
= (True, rest)
begins_with_comment line_l
# without_spaces
= dropWhile isSpace line_l
= case without_spaces of
-> (True, rest)
-> (False, [])
consider_comment nesting_level ['*/':rest] line_nr line_nr_accu file
| nesting_level==1
= get_lic line_nr line_nr_accu file
= consider_comment (nesting_level-1) rest line_nr line_nr_accu file
consider_comment nesting_level ['/*':rest] line_nr line_nr_accu file
= consider_comment (nesting_level+1) rest line_nr line_nr_accu file
consider_comment nesting_level [_:rest] line_nr line_nr_accu file
= consider_comment nesting_level rest line_nr line_nr_accu file
consider_comment nesting_level [] line_nr line_nr_accu file
# (end, file)
= fend file
| end
= ([], file)
# (line, file)
= my_freadline file
= [ch \\ ch<-:line]
= case line_l of
| nesting_level==1
-> get_lic (line_nr+1) [line_nr+1:line_nr_accu] file
-> consider_comment (nesting_level-1) rest (line_nr+1) [line_nr+1:line_nr_accu] file
-> consider_comment nesting_level line_l (line_nr+1) [line_nr+1:line_nr_accu] file
my_freadline :: !*File -> (!String, !*File)
my_freadline file
#! (line, file) = freadline file
last_char_ix = size line - 1
last_printable_char_ix = findrArrElt isntNewlineOnAnyPlatform line last_char_ix
| last_printable_char_ix==(-1)
= ("\n", file)
| last_printable_char_ix==last_char_ix
= (line, file)
| last_printable_char_ix==last_char_ix-1 && line.[last_char_ix]=='\n'
= (line, file)
= ({line & [last_printable_char_ix+1] = '\n' } %(0, last_printable_char_ix+1), file)
isntNewlineOnAnyPlatform '\xA' = False
isntNewlineOnAnyPlatform '\xD' = False
isntNewlineOnAnyPlatform _ = True