diff options
Diffstat (limited to 'ipl/packs/idol/idol.iol')
-rw-r--r-- | ipl/packs/idol/idol.iol | 863 |
1 files changed, 863 insertions, 0 deletions
diff --git a/ipl/packs/idol/idol.iol b/ipl/packs/idol/idol.iol new file mode 100644 index 0000000..f75ee52 --- /dev/null +++ b/ipl/packs/idol/idol.iol @@ -0,0 +1,863 @@ +# +# global variables +# +global fin,fout,fName,fLine,alpha,alphadot,white,nonwhite,nonalpha +global classes,comp,exec,strict,links,imports,loud,compiles,compatible,ct +# +# gencode first generates specifications for all defined classes +# It then imports those classes' specifications which it needs to +# compute inheritance. Finally, it writes out all classes' .icn files. +# +procedure gencode() + if \loud then write("Class import/export:") + # + # export specifications for each class + # + every cl := classes$foreach_t() do cl$writespec() + # + # import class specifications, transitively + # + repeat { + added := 0 + every super:= ((classes$foreach_t())$foreachsuper() | !imports) do{ + if /classes$lookup(super) then { + added := 1 + fname := filename(super) + readinput(envpath(fname),2) + if /classes$lookup(super) then halt("can't import class '",super,"'") + writesublink(fname) + } + } + if added = 0 then break + } + # + # compute the transitive closure of the superclass graph + # + every (classes$foreach_t())$transitive_closure() + # + # generate output + # + if \loud then write("Generating code:") + writesublink("i_object") + every s := !links do writelink(s) + write(fout) + every out := $!classes do { + name := filename(out$name()) + out$write() + put(compiles,name) + writesublink(name) + } + if *compiles>0 then return cdicont(compiles) + else return +end + +# +# a class defining objects resulting from parsing lines of the form +# tag name ( field1 , field2, ... ) +# If the constructor is given an argument, it is passed to self$read +# +class declaration(public name,fields,tag) + # + # parse a declaration string into its components + # + method read(decl) + decl ? ( + (tab(many(white)) | "") , + # get my tag + (self.tag := =("procedure"|"class"|"method"|"record")) , + (tab(many(white)) | "") , + # get my name + (self.name := tab(many(alpha))) , + # get my fields + (tab(find("(")+1)), + (tab(many(white)) | "") , + ((self.fields := classFields())$parse(tab(find(")")))) + ) | halt("declaration/read can't parse decl ",decl) + end + + # + # write a declaration; at the moment, only used by records + # + method write(f) + write(f,self$String()) + end + # + # convert self to a string + # + method String() + return self.tag || " " || self.name || "(" || self.fields$String() || ")" + end +initially + if \self.name then self$read(self.name) +end + +# +# A class for ordinary Icon global declarations +# +class vardecl(s) + method write(f) + write(f,self.s) + end +end + +# +# A class defining the constants for a given scope +# +class constant(t) + method expand(s) + i := 1 + # + # conditions for expanding a constant: + # must not be within a larger identifier nor within a quote + # + while ((i <- find(k <- $!self,s,i)) & ((i=1) | any(nonalpha,s[i-1])) & + ((*s = i+*k-1) | any(nonalpha,s[i+*k])) & + notquote(s[1:i])) do { + val := \ (self.t[k]) | stop("internal error in expand") + s[i +: *k] := val +# i +:= *val + } + return s + end + method foreach() # in this case, we mean the keys, not the values + suspend key(self.t) + end + method eval(s) + if s2 := \ self.t[s] then return s2 + end + method parse(s) + s ? { + k := trim(tab(find(":="))) | fail + move(2) + tab(many(white)) + val := tab(0) | fail + (*val > 0) | fail + self.t [ k ] := val + } + return + end + method append(cd) + every s := cd$parse do self$parse(s) + end +initially + self.t := table() +end + +# +# A class defining a single constant declaration +# +class constdcl : vardecl() + # suspend the individual constant := value strings + method parse() + self.s ? { + tab(find("const")+6) + tab(many(white)) + while s2 := trim(tab(find(","))) do { + suspend s2 + move(1) + tab(many(white)) + } + suspend trim(tab(0)) + } + end +end + +# +# class body manages a list of strings holding the code for +# procedures/methods/classes +# +class body(fn,ln,vars,text) + method read() + self.fn := fName + self.ln := fLine + self.text := [] + while line := readln() do { + put(self.text, line) + line ? { + tab(many(white)) + if ="end" & &pos > *line then return + else if =("local"|"static"|"initial") & any(nonalpha) then { + self.ln +:= 1 + pull(self.text) + / (self.vars) := [] + put(self.vars, line) + } + } + } + halt("body/read: eof inside a procedure/method definition") + end + method write(f) + if \self.vars then every write(f,!self.vars) + if \compatible then write(f," \\self := self.__state") + if \self.ln then + write(f,"#line ",self.ln + ((*\self.vars)|0)," \"",self.fn,"\"") + every write(f,$!self) + end + method delete() + return pull(self.text) + end + method size() + return (*\ (self.text)) | 0 + end + method foreach() + if t := \self.text then suspend !self.text + end +end + +# +# a class defining operations on classes +# +class class : declaration (supers,methods,text,imethods,ifields,glob) + # imethods and ifields are all lists of these: + record classident(class,ident) + + method read(line,phase) + self$declaration.read(line) + self.supers := idTaque(":") + self.supers$parse(line[find(":",line)+1:find("(",line)] | "") + self.methods:= taque() + self.text := body() + while line := readln("wrap") do { + line ? { + tab(many(white)) + if ="initially" then { + self.text$read() + if phase=2 then return + self.text$delete() # "end" appended manually during writing after + # generation of the appropriate return value + return + } else if ="method" then { + decl := method(self.name) + decl$read(line,phase) + self.methods$insert(decl,decl$name()) + } else if ="end" then { + # "end" is tossed here. see "initially" above + return + } else if ="procedure" then { + decl := method("") + decl$read(line,phase) + /self.glob := [] + put(self.glob,decl) + } else if ="global" then { + /self.glob := [] + put(self.glob,vardecl(line)) + } else if ="record" then { + /self.glob := [] + put(self.glob,declaration(line)) + } else if upto(nonwhite) then { + halt("class/read expected declaration on: ",line) + } + } + } + halt("class/read syntax error: eof inside a class definition") + end + + # + # Miscellaneous methods on classes + # + method has_initially() + return $*self.text > 0 + end + method ispublic(fieldname) + if self.fields$ispublic(fieldname) then return fieldname + end + method foreachmethod() + suspend $!self.methods + end + method foreachsuper() + suspend $!self.supers + end + method foreachfield() + suspend $!self.fields + end + method isvarg(s) + if self.fields$isvarg(s) then return s + end + method transitive_closure() + count := $*self.supers + while count > 0 do { + added := taque() + every sc := $!self.supers do { + if /(super := classes$lookup(sc)) then + halt("class/transitive_closure: couldn't find superclass ",sc) + every supersuper := super$foreachsuper() do { + if / self.supers$lookup(supersuper) & + /added$lookup(supersuper) then { + added$insert(supersuper) + } + } + } + count := $*added + every self.supers$insert($!added) + } + end + # + # write the class declaration: if s is "class" write as a spec + # otherwise, write as a constructor + # + method writedecl(f,s) + writes(f, s," ",self.name) + if s=="class" & ( *(supers := self.supers$String()) > 0 ) then + writes(f," : ",supers) + writes(f,"(") + rv := self.fields$String(s) + if *rv > 0 then rv ||:= "," + if s~=="class" & *(\self.ifields)>0 then { # inherited fields + every l := !self.ifields do rv ||:= l.ident || "," + if /(superclass := classes$lookup(l.class)) then + halt("class/resolve: couldn't find superclass ",sc) + if superclass$isvarg(l.ident) then rv := rv[1:-1]||"[]," + } + writes(f,rv[1:-1]) + write(f,,")") + end + method writespec(f) # write the specification of a class + f := envopen(filename(self.name),"w") + self$writedecl(f,"class") + every ($!self.methods)$writedecl(f,"method") + if self$has_initially() then write(f,"initially") + write(f,"end") + close(f) + end + + # + # write out the Icon code for this class' explicit methods + # and its "nested global" declarations (procedures, records, etc.) + # + method writemethods() + f:= envopen(filename(self.name,".icn"),"w") + every ($!self.methods)$write(f,self.name) + + if \self.glob & *self.glob>0 then { + write(f,"#\n# globals declared within the class\n#") + every i := 1 to *self.glob do (self.glob[i])$write(f,"") + } + close(f) + end + + # + # write - write an Icon implementation of a class to file f + # + method write() + f:= envopen(filename(self.name,".icn"),"a") + # + # must have done inheritance computation to write things out + # + if /self.ifields then self$resolve() + + # + # write a record containing the state variables + # + writes(f,"record ",self.name,"__state(__state,__methods") # reserved fields + rv := "," + rv ||:= self.fields$idTaque.String() # my fields + if rv[-1] ~== "," then rv ||:= "," + every s := (!self.ifields).ident do rv ||:= s || "," # inherited fields + write(f,rv[1:-1],")") + + # + # write a record containing the methods + # + writes(f,"record ",self.name,"__methods(") + rv := "" + + every s := ((($!self.methods)$name()) | # my explicit methods + self.fields$foreachpublic() | # my implicit methods + (!self.imethods).ident | # my inherited methods + $!self.supers) # super.method fields + do rv ||:= s || "," + + if *rv>0 then rv[-1] := "" # trim trailling , + write(f,rv,")") + + # + # write a global containing this classes' operation record + # along with declarations for all superclasses op records + # + writes(f,"global ",self.name,"__oprec") + every writes(f,", ", $!self.supers,"__oprec") + write(f) + + # + # write the constructor procedure. + # This is a long involved process starting with writing the declaration. + # + self$writedecl(f,"procedure") + write(f,"local self,clone") + + # + # initialize operation records for this and superclasses + # + write(f,"initial {\n", + " if /",self.name,"__oprec then ",self.name,"initialize()") + if $*self.supers > 0 then + every (super <- $!self.supers) ~== self.name do + write(f," if /",super,"__oprec then ",super,"initialize()\n", + " ",self.name,"__oprec.",super," := ", super,"__oprec") + write(f," }") + + # + # create self, initialize from constructor parameters + # + writes(f," self := ",self.name,"__state(&null,",self.name,"__oprec") + every writes(f,",",$!self.fields) + if \self.ifields then every writes(f,",",(!self.ifields).ident) + write(f,")\n self.__state := self") + + # + # call my own initially section, if any + # + if $*self.text > 0 then write(f," ",self.name,"initially(self)") + + # + # call superclasses' initially sections + # + if $*self.supers > 0 then { + every (super <- $!self.supers) ~== self.name do { + if (classes$lookup(super))$has_initially() then { + if /madeclone := 1 then { + write(f," clone := ",self.name,"__state()\n", + " clone.__state := clone\n", + " clone.__methods := ",self.name,"__oprec") + } + write(f," # inherited initialization from class ",super) + write(f," every i := 2 to *self do clone[i] := self[i]\n", + " ",super,"initially(clone)") + every l := !self.ifields do { + if l.class == super then + write(f," self.",l.ident," := clone.",l.ident) + } + } + } + } + + # + # return the pair that comprises the object: + # a pointer to the instance (__mystate), and + # a pointer to the class operation record + # + write(f," return idol_object(self,",self.name,"__oprec)\n", + "end\n") + + # + # write out class initializer procedure to initialize my operation record + # + write(f,"procedure ",self.name,"initialize()") + writes(f," initial ",self.name,"__oprec := ",self.name,"__methods") + rv := "(" + every s := ($!self.methods)$name() do { # explicit methods + if *rv>1 then rv ||:= "," + rv ||:= self.name||"_"||s + } + every me := self.fields$foreachpublic() do { # implicit methods + if *rv>1 then rv ||:= "," # (for public fields) + rv ||:= self.name||"_"||me + } + every l := !self.imethods do { # inherited methods + if *rv>1 then rv ||:= "," + rv ||:= l.class||"_"||l.ident + } + write(f,rv,")\n","end") + # + # write out initially procedure, if any + # + if self$has_initially() then { + write(f,"procedure ",self.name,"initially(self)") + self.text$write(f) + write(f,"end") + } + + # + # write out implicit methods for public fields + # + every me := self.fields$foreachpublic() do { + write(f,"procedure ",self.name,"_",me,"(self)") + if \strict then { + write(f," if type(self.",me,") == ", + "(\"list\"|\"table\"|\"set\"|\"record\") then\n", + " runerr(501,\"idol: scalar type expected\")") + } + write(f," return .(self.",me,")") + write(f,"end") + write(f) + } + + close(f) + + end + + # + # resolve -- primary inheritance resolution utility + # + method resolve() + # + # these are lists of [class , ident] records + # + self.imethods := [] + self.ifields := [] + ipublics := [] + addedfields := table() + addedmethods := table() + every sc := $!self.supers do { + if /(superclass := classes$lookup(sc)) then + halt("class/resolve: couldn't find superclass ",sc) + every superclassfield := superclass$foreachfield() do { + if /self.fields$lookup(superclassfield) & + /addedfields[superclassfield] then { + addedfields[superclassfield] := superclassfield + put ( self.ifields , classident(sc,superclassfield) ) + if superclass$ispublic(superclassfield) then + put( ipublics, classident(sc,superclassfield) ) + } else if \strict then { + warn("class/resolve: '",sc,"' field '",superclassfield, + "' is redeclared in subclass ",self.name) + } + } + every superclassmethod := (superclass$foreachmethod())$name() do { + if /self.methods$lookup(superclassmethod) & + /addedmethods[superclassmethod] then { + addedmethods[superclassmethod] := superclassmethod + put ( self.imethods, classident(sc,superclassmethod) ) + } + } + every public := (!ipublics) do { + if public.class == sc then + put (self.imethods, classident(sc,public.ident)) + } + } + end +end + +# +# a class defining operations on methods and procedures +# +class method : declaration (class,text) + method read(line,phase) + self$declaration.read(line) + self.text := body() + if phase = 1 then + self.text$read() + end + method writedecl(f,s) + decl := self$String() + if s == "method" then decl[1:upto(white,decl)] := "method" + else { + decl[1:upto(white,decl)] := "procedure" + if *(self.class)>0 then { + decl[upto(white,decl)] ||:= self.class||"_" + i := find("(",decl) + decl[i] ||:= "self" || (((decl[i+1] ~== ")"), ",") | "") + } + } + write(f,decl) + end + method write(f) + if self.name ~== "initially" then + self$writedecl(f,"procedure") + self.text$write(f) + self.text := &null # after writing out text, forget it! + end +end + +# +# a class corresponding to an Icon table, with special treatment of empties +# +class Table(t) + method size() + return (* \ self.t) | 0 + end + method insert(x,key) + /self.t := table() + /key := x + if / (self.t[key]) := x then return + end + method lookup(key) + if t := \self.t then return t[key] + return + end + method foreach() + if t := \self.t then every suspend !self.t + end +end + +# +# tabular queues (taques): +# a class defining objects which maintain synchronized list and table reps +# Well, what is really provided are loosely-coordinated list/tables +# +class taque : Table (l) + method insert(x,key) + /self.l := [] + if self$Table.insert(x,key) then put(self.l,x) + end + method foreach() + if l := \self.l then every suspend !self.l + end + method insert_t(x,key) + self$Table.insert(x,key) + end + method foreach_t() + suspend self$Table.foreach() + end +end + +# +# support for taques found as lists of ids separated by punctuation +# constructor called with (separation char, source string) +# +class idTaque : taque(punc) + method parse(s) + s ? { + tab(many(white)) + while name := tab(find(self.punc)) do { + self$insert(trim(name)) + move(1) + tab(many(white)) + } + if any(nonwhite) then self$insert(trim(tab(0))) + } + return + end + method String() + if /self.l then return "" + out := "" + every id := !self.l do out ||:= id||self.punc + return out[1:-1] + end +end + +# +# parameter lists in which the final argument may have a trailing [] +# +class argList : idTaque(public varg) + method insert(s) + if \self.varg then halt("variable arg must be final") + if i := find("[",s) then { + if not (j := find("]",s)) then halt("variable arg expected ]") + s[i : j+1] := "" + self.varg := s := trim(s) + } + self$idTaque.insert(s) + end + method isvarg(s) + if s == \self.varg then return s + end + method String() + return self$idTaque.String() || ((\self.varg & "[]") | "") + end +initially + self.punc := "," +end + +# +# Idol class field lists in which fields may be preceded by a "public" keyword +# +class classFields : argList(publics) + method String(s) + if *(rv := self$argList.String()) = 0 then return "" + if /s | (s ~== "class") then return rv + if self$ispublic(self.l[1]) then rv := "public "||rv + every field:=self$foreachpublic() do rv[find(","||field,rv)] ||:= "public " + return rv + end + method foreachpublic() + if \self.publics then every suspend !self.publics + end + method ispublic(s) + if \self.publics then every suspend !self.publics == s + end + method insert(s) + s ? { + if ="public" & tab(many(white)) then { + s := tab(0) + /self.publics := [] + put(self.publics,s) + } + } + self$argList.insert(s) + end +initially + self.punc := "," +end + +# +# procedure to read a single Idol source file +# +procedure readinput(name,phase,ct2) + if \loud then write("\t",name) + fName := name + fLine := 0 + fin := sysopen(name,"r") + ct := \ct2 | constant() + while line := readln("wrap") do { + line ? { + tab(many(white)) + if ="class" then { + decl := class() + decl$read(line,phase) + if phase=1 then { + decl$writemethods() + classes$insert(decl,decl$name()) + } else classes$insert_t(decl,decl$name()) + } + else if ="procedure" then { + if comp = 0 then comp := 1 + decl := method("") + decl$read(line,phase) + decl$write(fout,"") + } + else if ="record" then { + if comp = 0 then comp := 1 + decl := declaration(line) + decl$write(fout,"") + } + else if ="global" then { + if comp = 0 then comp := 1 + decl := vardecl(line) + decl$write(fout,"") + } + else if ="const" then { + ct$append ( constdcl(line) ) + } + else if ="method" then { + halt("readinput: method outside class") + } + else if ="#include" then { + savedFName := fName + savedFLine := fLine + savedFIn := fin + tab(many(white)) + readinput(tab(if ="\"" then find("\"") else many(nonwhite)), + phase,ct) + fName := savedFName + fLine := savedFLine + fin := savedFIn + } + } + } + close(fin) +end + +# +# filter the input translating $ references +# (also eats comments and trims lines) +# +procedure readln(wrap) + count := 0 + prefix := "" + while /finished do { + + if not (line := read(fin)) then fail + fLine +:= 1 + if match("#include",line) then return line + line[ 1(x<-find("#",line),notquote(line[1:x])) : 0] := "" + line := trim(line,white) +# line := selfdot(line) + x := 1 + while ((x := find("$",line,x)) & notquote(line[1:x])) do { + z := line[x+1:0] ||" " # " " is for bal() + case line[x+1] of { + # + # IBM 370 digraphs + # + "(": line[x+:2] := "{" + ")": line[x+:2] := "}" + "<": line[x+:2] := "[" + ">": line[x+:2] := "]" + # + # Invocation operators $! $* $@ $? (for $$ see below) + # + "!"|"*"|"@"|"?": { + z ? { + move(1) + tab(many(white)) + if not (id := tab(many(alphadot))) then { + if not match("(") then halt("readln can't parse ",line) + if not (id := tab(&pos<bal())) then + halt("readln: cant bal ",&subject) + } + Op := case line[x+1] of { + "@": "activate" + "*": "size" + "!": "foreach" + "?": "random" + } + count +:= 1 + line[x:0] := + "(__self"||count||" := "||id||").__methods."|| + Op||"(__self"||count||".__state)"||tab(0) + } + } + # + # x $[ y ] shorthand for x$index(y) + # + "[": { + z ? { + if not (middle := tab((&pos<bal(&cset,'[',']'))-1)[2:0]) then + halt("readln: can't bal([) ",&subject) + tail := tab(0)|"" + line := line[1:x]||"$index("||middle||")"||(tab(0)|"") + } + } + default: { + # + # get the invoking object. + # + reverse(line[1:x])||" " ? { + tab(many(white)) + if not (id := reverse(tab(many(alphadot)))) then { + if not match(")") then halt("readln: can't parse") + if not (id := reverse(tab(&pos<bal(&cset,')','(')))) + then halt("readln: can't bal ",&subject) + } + objlen := &pos-1 + } + count +:= 1 + front := "(__self"||count||" := "||id||").__methods." + back := "__self"||count||".__state" + + # + # get the method name + # + z ? { + ="$" + tab(many(white)) + if not (methodname := tab(many(alphadot))) then + halt("readln: expected a method name after $") + tab(many(white)) + methodname ||:= "(" + if ="(" then { + tab(many(white)) + afterlp := &subject[&pos] + } + else { + afterlp := ")" + back ||:= ")" + } + methlen := &pos-1 + } + if line[x+1] == "$" then { + c := if afterlp[1] ~== ")" then "" else "[]" + methodname[-1] := "!(" + back := "["||back||"]|||" + } else { + c := if (\afterlp)[1] == ")" then "" else "," + } + line[x-objlen : (((*line>=(x+methlen+1))|0)\1)] := + front || methodname || back || c + } + } # case + } # while there's a $ to process + if /wrap | (prefix==line=="") then finished := line + else { + prefix ||:= line || " " # " " is for bal() + prefix ? { + # we are done if the line is balanced wrt parens and + # doesn't end in a continuation character (currently just ,) + if ((*prefix = bal()) & (not find(",",prefix[-2]))) then + finished := prefix[1:-1] + } + } + } # while / finished + return ct$expand(finished) +end |