Menu

Implementation

OmniMark Code

Implementation

The interface appears to work, now let's see if we can implement it.

The first thing to note is that unresolved referents are by definition something out of band. This means that our referent buffer has to keep track of them separately from the fixed data, and also that we cannot use the same mechanism for emitting an unresolved referent that we use for emitting regular data.

The standard solution for emitting out-of-band information are signals. We'll declare a catch with a referent argument. Then we can use signal throw to emit a referent. The reason for having the parent type markup-future for markup-referent-buffer will be explained later.

declare record fragment
 
declare record data-fragment extends fragment
   field markup-buffer data
 
export record markup-future extends fragment
 
export record markup-referent-buffer extends markup-future
   field string   name
   field fragment contents variable
 
 
export global markup-referent-buffer referents variable
 
export markup-referent-buffer function
   referent value string name
as
   using new? referents{name} as ref
   do
      set ref:name to name
      return ref
   done
 
 
export catch markup-referent value markup-future ref
 
export markup source conversion-function
   value markup-referent-buffer b
as
   signal throw markup-referent b

The next piece of functionality to tackle is the ability to set the referent value. The following function accomplishes the task. Note that it automatically accepts and stores referents and not only plain data. There is no need for any equivalent of the with referents-allowed modifier when setting a referent value.

export markup sink conversion-function
   value markup-referent-buffer b
as
   repeat
      local data-fragment frag
 
      set new b:contents to frag
      using output as frag:data
         output #current-input
      exit
 
    catch markup-referent ref
      set new b:contents to ref
   again

The resolve-referents function starts by reproducing its input until it encounters the first markup-referent signal. After that, it has to start buffering the signalled referents alongside the regular data until the end of the input. Since the markup-referent-buffer type already knows how to accomplish this task we'll delegate the bufferring to an anonymous instance of the type — there's no reason to duplicate the code.

export markup sink function
   resolve-referents into value markup sink destination
as
   using output as destination
   repeat
      output #current-input take any*
      exit
 
    catch #markup-point event
      signal rethrow
 
    catch #markup-start event
      signal rethrow
 
    catch #markup-end event
      signal rethrow
 
    catch markup-referent event
      do
         local markup-referent-buffer ref
 
         using output as ref
         do
            signal rethrow
            output #current-input
         done
 
         output resolved ref
      done
   again

Once the end of input is reached, the dynamic function resolved performs the actual work of emitting the current value of the referent. Since referents can contain other referents, this function must be recursive, but its implementation is trivial.

export dynamic markup source function
   resolved value fragment f
as
   not-reached message "Unresolvable referent fragment."
 
 
define overriding markup source function
   resolved value data-fragment f
as
   output f:data
 
 
export overriding markup source function
   resolved value markup-referent-buffer b
as
   assert number of b:contents > 0
      message "Referent '" || b:name || "' has no defined value."
   repeat over b:contents as fragment
      output resolved fragment
   again


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.