#<p>
# This file defines a series of classes that can be used associate
# Unicon language entities with comments and components.
#</p>
#<p>
# The intent is to provide a basis for generating documentation
# (e.g. HTML) from Unicon source files. The classes defined
# here embody all the relevant information needed to generate
# documentation about a Unicon program and/or library.
#</p>
#<p>
# <b>Author:</b> Steve Wampler (<i>sbw@tapestry.tucson.az.us</i>)
#</p>
#<p>
# This file is in the <i>public domain</i>.
#</p>
package UniDoc
import util
import lang
# <p>
# A sequence of things
# </p>
class Sequence : Object (contents, comments)
# <p>
# Produce the size of the Sequence.
# </p>
method size()
return *contents
end
# <p>
# Change the contents of the Sequence.
# </p>
method set(newContents)
contents := newContents
end
# <p>
# Add an item to the Sequence
# </p>
method add(item)
put(contents, item)
end
# <p>
# Generate all the items in the Sequence
# </p>
method get()
suspend \!contents
end
# <p>
# Succeeds if <b>item</b> is contained in the Sequence
# </p>
method contains(item)
return item === !contents
end
# <p>
# Change the comments associated with the Sequence
# </p>
method setComments(newComments)
comments := \newComments | Comments()
end
# <p>
# Produce the Comments() associated with the Sequence
# </p>
method getComments()
return comments
end
# <p>
# Add a new line to the current comment paragraph.
# </p>
method addComment(newComment)
comments.addComment(newComment)
end
# <p>
# Start a new comment paragraph
# </p>
method startNewComments(newComment)
comments.newBlock()
addComment(newComment)
end
# <p>
# Succeeds if any item in the sequence has comments attached to it.
# (Used in UniHTML class to determine whether or not parameter
# and field sequences need to be expanded in detail.)
# </p></p>
# Fails if no comments attached to any item.
# </p>
method itemsHaveComments()
local item, c
every item := !contents do {
if c := (\item).getComments() then {
if c.size() > 0 then return
}
}
end
initially
/contents := []
/comments := Comments()
end
# <p>
# A Set of named things.
# </p>
class Set : Object (contents, comments)
# <p>
# Produce the size of the Set
# </p>
method size()
return *contents
end
# <p>
# Change the Set contents
# </p>
method set(newContents)
contents := newContents
end
# <p>
# Insert an item into the Set
# </p>
method add(iName, item)
contents[iName] := item
end
# <p>
# Delete an item from the Set, given that item's name
# </p>
method del(iName)
contents[iName] := &null
end
# <p>
# Generate a sorted (by name) sequence of the items in the Set
# </p>
method get()
suspend \(!sort(contents))[2]
end
# <p>
# Get a specific item from the Set, given that item's name
# </p>
method getByName(iName)
return contents[iName]
end
# <p>
# Generate a sorted sequence of the names of the items in the Set
# </p>
method getNames()
suspend \(!sort(contents))[1]
end
# <p>
# Succeed if item is in the Set
# </p>
method contains(item)
return item === !contents
end
# <p>
# Succeed if the named item is in the Set
# </p>
method containsName(iName)
return iName == key(contents)
end
# <p>
# Change the comments associated with this Set
# </p>
method setComments(newComments)
comments := \newComments | Comments()
end
# <p>
# Produce this Set's Comments()
# </p>
method getComments()
return comments
end
# <p>
# Add a line to the this Set's current comment paragraph
# </p>
method addComment(newComment)
comments.addComment(newComment)
end
# <p>
# Start a new comment paragraph
# </p>
method startNewComments(newComment)
comments.newBlock()
addComment(newComment)
end
initially
/contents := table()
/comments := Comments()
end
# <p>
# Base class for representing a Unicon language entity.
# </p>
class UEntity : Object (name, parent, comments, srcFile)
# <p>
# Name this entity
# </p>
method setName(newName)
name := newName
end
# <p>
# Produce this entity's name
# </p>
method getName()
return \name
end
# <p>
# Remember the parent entity for this entity.
# </p>
method setParent(newParent)
parent := newParent
end
# <p>
# Produce this entity's parent entity.
# </p>
method getParent()
return \parent | ""
end
# <p>
# Remember the source file name for this entity.
# </p>
method setSrcFile(fName)
srcFile := fName
end
# <p>
# Produce the source file for this entity.
# </p>
method getSrcFile()
return \srcFile
end
# <p>
# Produce a nice name for the type of this entity.
# </p>
method getFormType()
return UniDoc::getFormType(self)
end
# <p>
# Change the comments associated with this entity
# </p>
method setComments(newComments)
comments := \newComments | Comments()
end
# <p>
# Produce the Comments() associated with this entity
# </p>
method getComments()
return comments
end
# <p>
# Add a comment to this entity's current comment paragraph
# </p>
method addComment(newComment)
comments.add(newComment)
end
# <p>
# Start a new comment paragraph for this entity
# </p>
method startNewComments(newComment)
comments.newBlock()
comments.add(newComment)
end
# <p>
# Add an entire comment paragraph to this entity's comments
# </p>
method mergeComments(newComments)
comments.append(newComments)
end
initially
/comments := Comments()
end
# <p>
# Any simple name that might be commented (typically parameters
# and field names)
# </p>
class UName : UEntity (name, parent, comments, category, defValue, typeValue)
# <p>
# Remember the category of this name (i.e. what it represents)
# </p>
method setCategory(newCategory)
category := newCategory
end
# <p>
# Produce the category for this name
# </p>
method getCategory()
return category
end
# <p>
# If this name has an associated default value, remember it.
# </p>
method setDefValue(newDefValue)
defValue := newDefValue
end
# <p>
# Produce the default value, if any.
# </p>
method getDefValue()
return \defValue
end
# <p>
# If this name has an associated type, remember it.
# </p>
method setTypeValue(newTypeValue)
typeValue := newTypeValue
end
# <p>
# Produce the type value, if any.
# </p>
method getTypeValue()
return \typeValue
end
initially
/comments := Comments()
end
# <p>
# A global variable (does not include procedures, records, classes, etc.)
# </p>
class UGlobal : UEntity (name, parent, comments, defValue, typeValue)
# <p>
# If this name has an associated default value, remember it.
# </p>
method setDefValue(newDefValue)
defValue := newDefValue
end
# <p>
# Produce the default value, if any.
# </p>
method getDefValue()
return \defValue
end
# <p>
# If this name has an associated type, remember it.
# </p>
method setTypeValue(newTypeValue)
typeValue := newTypeValue
end
# <p>
# Produce the type value, if any.
# </p>
method getTypeValue()
return \typeValue
end
initially
/comments := Comments()
end
# <p>
# A method (composed of parameters)
# </p>
class UMethod : UEntity (name, parent, comments, params)
# <p>
# Set the parameter list for this method
# </p>
method setParams(newParams)
params := newParams
end
# <p>
# Produce the parameter list for this method
# </p>
method getParams()
return params
end
# <p>
# Add a parameter to the parameter list.
# </p>
method addParam(newParam)
params.add(newParam)
end
initially
/comments := Comments()
/params := Sequence()
end
# <p>
# A procedure (composed of parameters)
# </p>
class UProc : UEntity (name, parent, comments, params)
# <p>
# Set the parameter list for this method
# </p>
method setParams(newParams)
params := newParams
end
# <p>
# Produce the parameter list for this method
# </p>
method getParams()
return params
end
# <p>
# Add a parameter to the parameter list.
# </p>
method addParam(newParam)
params.add(newParam)
end
initially
/comments := Comments()
/params := Sequence()
end
# <p>
# A record (composed of fields)
# </p>
class URecord : UEntity (name, parent, comments, fields)
# <p>
# Set the field list for this method
# </p>
method setParams(newFields)
fields := newFields
end
# <p>
# Produce the field list for this method
# </p>
method getParams()
return fields
end
# <p>
# Add a field to the field list.
# </p>
method addField(newField)
fields.add(newField)
end
initially
/comments := Comments()
/fields := Sequence()
end
# <p>
# An import
# </p>
class UImport : UEntity (name, parent, comments)
initially
/comments := Comments()
end
# <p>
# A link
# </p>
class ULink : UEntity (name, parent, comments)
initially
/comments := Comments()
end
# <p>
# A Class (composed of superClasses, fields, methods, and constructor)
# </p>
class UClass : UEntity ( name, parent, comments, constructor,
superClasses, fields, methods, pack, file)
# <p>
# Set this class' name
# </p>
method setName(newName)
newName ?:= {
tabSkip("::")
tab(0)
}
name := newName
end
# <p>
# Return the full name (with package name included) for this class.
# </p>
method getFullName()
if \pack then {
return pack.getName()||"::"||getName()
}
return "::"||getName()
end
# <p>
# Set the Sequence() of superclass names.
# </p>
method setSuperClasses(newSuperClasses)
superClasses := newSuperClasses
end
# <p>
# Get the Sequence() of superclass names.
# </p>
method getSuperClasses()
return superClasses
end
# <p>
# Append a superclass name to the Sequence() of superclass names.
# </p>
method addSuperClass(superClass)
superClasses.add(superClass)
end
# <p>
# Set the Sequence() of parameters (really fields). Given this
# name to be compatible with other entities that have parameter
# or field lists associated with them.)
# </p>
method setParams(newParams)
fields := newParams
end
# <p>
# Produce the Sequence() of parameters.
# </p>
method getParams()
return fields
end
# <p>
# Append a field to the Sequence() of parameters.
# </p>
method addField(newField)
fields.add(newField)
end
# <p>
# Set the Set() of methods defined in this class.
# </p>
method setMethods(newMethods)
methods := newMethods
end
# <p>
# Produce the Set() of methods defined in this class.
# </p>
method getMethods()
return methods
end
# <p>
# Add a method to the Set()of methods.
# </p>
method addMethod(aMethod)
methods.add(aMethod.getName(),aMethod)
end
# <p>
# Does this class define a method with the given name? <i>Does not
# include inherited methods.</i>
# </p>
method hasMethod(aMethodName)
return methods.containsName(aMethodName)
end
# <p>
# Produce the method with the given name if any. <i>Does not
# include inherited methdos.</i>
# </p>
method getMethod(aMethodName)
return methods.getByName(aMethodName)
end
# <p>
# Set the constructor (initially clause).
# </p>
method setConstructor(newConstructor)
constructor := newConstructor
end
# <p>
# Produce the constructor.
# </p>
method getConstructor()
return constructor
end
# <p>
# Set the package that this class belongs to. <i>Every class belongs
# to a package, even if its the default package.</i>
# </p>
method setPackage(aPack)
pack := aPack
end
# <p>
# Produce the package containing this class.
# </p>
method getPackage()
return pack
end
# <p>
# Set the file that contains this class.
# </p>
method setFile(aFile)
file := aFile
end
# <p>
# Produce the file that contains this class.
# </p>
method getFile()
return file
end
initially
/comments := Comments()
/superClasses := Sequence()
/methods := Set()
/fields := Sequence()
if \name then {
setName(name)
}
end
# <p>
# A class constructor (initially clause).
# </p><p>
# If <b>params === &null</b>, then initially clause had no parameter list
# (so constructor defaults to class fields)
# otherwise use params (if any) as constructor params.
# </p>
#
class UConstructor : UEntity (name, parent, comments, params)
method setParams(newParams)
params := newParams
end
method getParams()
return params
end
method addParam(newParam)
/params := Sequence()
params.add(newParam)
end
initially
/comments := Comments()
end
# <p>
# A package (composed of files, imports, procedures, classes, globals,
# and records)
# </p>
class UPackage : UEntity (name, parent, comments, files, procs, imports,
classes, globals, records)
# <p>
# Set the Set() of files that contain code defined in this package.
# </p>
method setFiles(newFiles)
files := newFiles
end
# <p>
# Produce the Set() of files that contain code defined in this package.
# </p>
method getFiles()
return files
end
# <p>
# Add a new file to the Set() of files that contain code defined in
# this package.
# </p>
method addFile(newFile)
files.add(newFile.getName(), newFile)
end
# <p>
# Delete a file from the Set() of files that contain code defined
# in this package. <i>Usually used to remove files from the
# default package set.</i>
method delFile(fName)
files.del(fName)
end
# <p>
# Produce a sorted (by name) list of files that contain code
# defined in this package.
# </p>
method getFileNames()
local clist
every put(clist := [], files.getNames())
return sort(clist)
end
# <p>
# Set the Set() of imports.
# </p>
method setImports(newImports)
imports := newImports
end
# <p>
# Produce the Set() of imports.
# </p>
method getImports()
return imports
end
# <p>
# Add an import to this package.
# </p>
method addImport(newImport)
imports.add(newImport.getName(), newImport)
end
# <p>
# Produce a sorted list of the names of this package's imports.
# </p>
method getImportNames()
local clist
every put(clist := [], imports.getNames())
return sort(clist)
end
# <p>
# Set the procedures defined in this package.
# </p>
method setProcedures(newProcs)
procs := newProcs
end
# <p>
# Produce the procedures defined in this package.
# </p>
method getProcedures()
return procs
end
# <p>
# Add a procedure to this package.
# </p>
method addProcedure(newProc)
procs.add(newProc.getName(), newProc)
end
# <p>
# Produce a sorted list of the names of this package's procedures.
# </p>
method getProcedureNames()
local clist
every put(clist := [], procs.getNames())
return sort(clist)
end
# <p>
# Set the classes defined in this package.
# </p>
method setClasses(newClasses)
classes := newClasses
end
# <p>
# Produce the classes in this package.
# </p>
method getClasses()
return classes
end
# <p>
# Given the name of a class, return that class if it's
# defined in this package.
# </p>
method getClass(cName)
return classes.getByName(cName)
end
# <p>
# Add a class to this package.
# </p>
method addClass(newClass)
classes.add(newClass.getName(), newClass)
end
# <p>
# Produce a list (sorted by name) of this package's classes.
# </p>
method getClassNames()
local clist
every put(clist := [], classes.getNames())
return sort(clist)
end
# <p>
# Set the globals defined in this package.
# </p>
method setGlobals(newGlobals)
globals := newGlobals
end
# <p>
# Produce the globals defined in this package.
# </p>
method getGlobals()
return globals
end
# <p>
# Add a global to this package.
# </p>
method addGlobal(newGlobal)
globals.add(newGlobal.getName(), newGlobal)
end
# <p>
# Produce a list (sorted by name) of this package's globals.
# </p>
method getGlobalNames()
local clist
every put(clist := [], globals.getNames())
return sort(clist)
end
# <p>
# Set the records defined in this package.
# </p>
method setRecords(newRecords)
records := newRecords
end
# <p>
# Produce the records defined in this package.
# </p>
method getRecords()
return records
end
# <p>
# Add a record to this package.
# </p>
method addRecord(newRecord)
records.add(newRecord.getName(), newRecord)
end
# <p>
# Produce a sorted list of the names of the records defined in this
# package.
# </p>
method getRecordNames()
local clist
every put(clist := [], records.getNames())
return sort(clist)
end
initially
/files := Set()
/imports := Set()
/procs := Set()
/classes := Set()
/globals := Set()
/records := Set()
/comments := Comments()
end
# <p>
# A file (composed of anything except another file)
# </p>
class UFile : UEntity (name, parent, comments, imports, links, pack,
procs, classes, globals, records)
# <p>
# Set the imports listed in this file.
# </p>
method setImports(newImports)
imports := newImports
end
# <p>
# Produce the imports listed in this file.
# </p>
method getImports()
return imports
end
# <p>
# Add an import to this file.
# </p>
method addImport(newImport)
imports.add(newImport)
end
# <p>
# Produce a sorted list of the names of files imported by this file.
# </p>
method getImportNames()
local clist
every put(clist := [], imports.get().getName())
return sort(clist)
end
# <p>
# Set the links listed in this file.
# </p>
method setLinks(newLinks)
links := newLinks
end
# <p>
# Produce the links listed in this file.
# </p>
method getLinks()
return links
end
# <p>
# Add a link to this file.
# </p>
method addLink(newLink)
links.add(newLink)
end
# <p>
# Produce a sorted list of the names of files lined by this file.
# </p>
method getLinkNames()
local clist
every put(clist := [], links.get().getName())
return sort(clist)
end
# <p>
# Set the package that this file belongs in.
# </p>
method setPackage(newPack)
pack := newPack
end
# <p>
# Produce the package associated with this file.
# </p>
method getPackage()
return pack
end
# <p>
# Produce the name of the package associated with this file.
# </p>
method getPackageName()
return pack.getName()
end
# <p>
# Set the procedures listed in this file.
# </p>
method setProcedures(newProcs)
procs := newProcs
end
# <p>
# Produce the procedures listed in this file.
# </p>
method getProcedures()
return procs
end
# <p>
# Add an procedure to this file.
# </p>
method addProcedure(newProc)
procs.add(newProc.getName(), newProc)
end
# <p>
# Produce a sorted list of the names of the procedures in this file.
# </p>
method getProcedureNames()
local clist
every put(clist := [], procs.getNames())
return sort(clist)
end
# <p>
# Set the classes listed in this file.
# </p>
method setClasses(newClasses)
classes := newClasses
end
# <p>
# Produce the classes listed in this file.
# </p>
method getClasses()
return classes
end
# <p>
# Produce the class with the given name if it's defined in this file.
# </p>
method getClass(cName)
return classes.getByName(cName)
end
# <p>
# Add a class to this file.
# </p>
method addClass(newClass)
classes.add(newClass.getName(), newClass)
end
# <p>
# Produce a sorted list of the names of classes defined by this file.
# </p>
method getClassNames()
local clist
every put(clist := [], classes.getNames())
return sort(clist)
end
# <p>
# Set the globals listed in this file.
# </p>
method setGlobals(newGlobals)
globals := newGlobals
end
# <p>
# Produce the globals listed in this file.
# </p>
method getGlobals()
return globals
end
# <p>
# Add a global to this file.
# </p>
method addGlobal(newGlobal)
globals.add(newGlobal.getName(), newGlobal)
end
# <p>
# Produce a sorted list of the names of globals listed by this file.
# </p>
method getGlobalNames()
local clist
every put(clist := [], globals.getNames())
return sort(clist)
end
# <p>
# Set the records listed in this file.
# </p>
method setRecords(newRecords)
records := newRecords
end
# <p>
# Produce the records listed in this file.
# </p>
method getRecords()
return records
end
# <p>
# Add a record to this file.
# </p>
method addRecord(newRecord)
records.add(newRecord.getName(), newRecord)
end
# <p>
# Produce a sorted list of the names of records defined by this file.
# </p>
method getRecordNames()
local clist
every put(clist := [], records.getNames())
return sort(clist)
end
initially
/imports := Sequence()
/links := Sequence()
# There's at most one package for a file, so no sequence needed!
/procs := Set()
/classes := Set()
/globals := Set()
/records := Set()
/comments := Comments()
end
# <p>
# A linked list of comment blocks. Each comment block represents
# a "paragraph" of comments. Also, comments for the same entity
# that have been collected from different lexical sites reside
# in separate blocks on this chain.
# </p>
class Comments: Object (head, tail)
# <p>
# How many comments are there?
# </p>
method size()
local block, s
s := 0
block := head
while \block do {
s +:= block.size()
block := block.nextblock
}
return s
end
# <p>
# Add a comment to the current comment block.
# </p>
method add(newComment)
tail.put(newComment)
end
# <p>
# Generates the chain of comment blocks.
# Fails if no comment blocks available.
# </p>
method get()
local block
block := head
while \block do {
if block.size() > 0 then {
suspend block
}
block := block.nextblock
}
end
# <p>
# Start a new comment block
# </p>
method newBlock()
tail.nextblock := CommentBlock()
tail := tail.nextblock
end
# <p>
# Append another list of comments to this list.
# </p>
method append(newComments)
tail.nextblock := newComments.get()
end
# <p>
# Produce the first "sentence" of this sequence of comments.
# Later, the definition of "sentence" will be improved.
# </p>
method getFirstSentence()
return head.getFirstSentence()
end
initially
tail := head := CommentBlock()
end
# <p>
# A paragraph's worth of comments.
# </p>
class CommentBlock: Object (legacy, comments, nextblock)
# <p>
# Produce the number of lines in this paragraph.
# </p>
method size()
return *comments
end
# <p>
# Succeed if this comment paragraph appears to be a 'legacy'
# comment paragraph. A <i>legacy</i> comment paragraph is
# one that does not start with an HTML key, though it can
# still contain HTML clauses.
# </p>
method isLegacy()
return \legacy
end
# <p>
# Add a comment to the paragraph.
# </p>
method put(newComment)
if firstNonBlank() then { # see if this is a legacy comment or not.
if match("<", lTrim(newComment)) then {
legacy := &null
}
else {
legacy := "yes"
}
}
::put(comments, newComment)
end
# <p>
# Succeeds if the newComment has been preceded
# only by blank comments.
# </p>
# <i>This is intended for internal use only!</i>
method firstNonBlank()
local c
every c := !comments do {
if *trim(c, ' \t\n') > 0 then fail
}
return
end
# <p>
# Generate all the comments in this paragraph
# </p>
method get()
suspend !comments
end
# <p>
# Produce the first "sentence" of this comment block. Later,
# the definition of "sentence" will be improved.
# </p>
method getFirstSentence()
return \comments[1]
end
initially
comments := []
nextblock := &null
end
#<p>
# Produce a more useful name for the type of form <tt>obj</tt>.
#</p>
procedure getFormType(obj)
return case obj.className() of {
"UniDoc::UName" : obj.getCategory()
"UniDoc::UGlobal" : "global"
"UniDoc::UMethod" : "method"
"UniDoc::UProc" : "procedure"
"UniDoc::URecord" : "record"
"UniDoc::UImport" : "import"
"UniDoc::ULink" : "link"
"UniDoc::UClass" : "class"
"UniDoc::UConstructor" : "constructor"
"UniDoc::UPackage" : "package"
"UniDoc::UFile" : "file"
}
end
This page produced by UniDoc on 2021/04/15 @ 23:59:45.