Menu

Reformulation

OmniMark Code

Reformulation

One of the benefits of having a first-class referent type is that there is no more need to keep all active referents in the single built-in shelf referents. We can instead export the type itself and let the user keep the track of her referents in the way most appropriate for the problem at hand. That also frees the user from the obligation to invent a unique name for each referent.

Here's a new version of the insert-neighbour-links example that makes use of first-class referents:

global markup-referent-buffer section-referents variable
 
define markup source function
   insert-neighbour-links (value markup source sections)
as
   using output as resolve-referents into #current-output
   do markup-parse sections
      new section-referents
      output "%c"
      using output as section-referents lastmost
         output ""
   done
 
element "section"
   local markup-element-event link initial { create-element-event (declared-elements of target-dtd){"link"}
                                                       attributes { attribute "id" with key "link-end" }
                                           }
 
   increment section-count
   using output as section-referents lastmost
   do
       signal throw #markup-start link
       output "Section " || "d" % number of section-referents || ": %v(title)"
       signal throw #markup-end link
   done
 
   signal throw #markup-start #current-markup-event
   output section-referents [number of section-referents - 1]
      when number of section-referents > 1
   output new section-referents
   output #content
   signal throw #markup-end #current-markup-event

The new version does not name its referents. It instead uses the section-referents shelf to store and access them.

Another possibility would be to skip the use of markup-referent-buffer in favour of a purpose-built subtype of markup-future. This can be useful in situations where the referent value cannot be easily calculated at any one time.

The insert-neighbour-links example has been rather simplistic so far. For example, it assumes that the section identifier and title could be fetched from two attributes of a single element. Suppose instead that the referent value must be combined from two different child elements named <id> and <title>. In that case, we cannot construct the final referent value in any of the three element rules, we can only provide its pieces.

The solution is simply to provide the pieces of the final referent value as we obtain them. We don't need to combine them into the full referent value until the resolution time anyway, when the function resolved will be invoked. We can override this function to put the referent value together as follows:

declare record section-referent extends markup-future
   field integer       number
   field string        id
   field markup-buffer title
 
define overriding markup source function
   resolved value section-referent section
as
   do when section:number != 0
      local markup-element-event link initial { create-element-event (declared-elements of target-dtd){"link"}
                                                          attributes { section:id with key "link-end" }
                                              }
 
      signal throw #markup-start link
      output "Section " || "d" % section:number || ": " || section:title
      signal throw #markup-end link
   done

Note that an uninitialized section-referent, whose number field is not set, resolves to an empty string.

The insert-neighbour-links filter now needs to be adjusted to work with the special section-referent instances instead of the generic markup-referent-buffer ones. The following will work:

define markup source function
   insert-neighbour-links (value markup source sections)
as
   using output as resolve-referents into #current-output
   do markup-parse sections
      output "%c"
   done
 
global section-referent previous-section
global section-referent current-section
global section-referent next-section
 
element "section"
   set current-section to next-section
   set current-section:number to previous-section:number + 1
   set next-section to new section-referent {}
 
   signal throw #markup-start #current-markup-event
   output previous-section || next-section || #content
   signal throw #markup-end #current-markup-event
 
   set previous-section to current-section
 
element "id" when parent is "section"
   set current-section:id to "%c"
 
element "title" when parent is "section"
   using output as current-section:title
      output #content


Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.