On 27/08/2013 06:39, Michael Norrish wrote:
On 26/08/13 22:34, David Matthews wrote:
You would have to write your own version of PolyML.prettyPrint and your own REPL to use it. Neither of these are very complicated and I can point you at the existing code as a basis. PolyML.print and PolyML.makestring, if you actually use them, would use the original pretty-printer. You would not need your own PolyML.addPrettyPrinter. That just adds a function that generates a "pretty" data structure to a particular type and doesn't care whether the data structure includes your own ContextProperty values.
Yes, I?d certainly be interested in seeing how to write my own REPL. I?ve long meant to try this as it would allow us to use a custom lexer as well. Please let me know where to look for the relevant docs/sample code. I assume that without an addPrettyPrinter function, this custom REPL would not be extensible in the same way that the default one is. I don?t think this would be a big deal in practice.
The REPL is just a few lines in basis/FinalPolyML.sml . It's the readEvalPrint and handledLoop functions. The core boils down to
let val code = PolyML.compiler(readin, [CPNameSpace nameSpace, CPOutStream printOut]) handle Fail s => ( (* Compilation failed *) raise Fail s ) in code () (* Report exceptions in running code. *) handle exn => ( (* Run failed *) raise exn ) end
The real work is in PolyML.compiler. This takes a function that returns the next character in the input and a list of options. It returns a function that, when called, runs the compiled code and performs any side-effects. That includes printing the results. Actually, nearly everything, including printing can be overridden by adding specific options. The current list is in the code in FinalPolyML.sml with comments explaining what the options do and how they default.
User-defined pretty printing is done at a lower level than the REPL. Whenever a type identifier (a "type name" in the Definition) is created the compiler creates at run-time a ref cell and fills it in with a default function that, when called, builds a pretty tree for a value of that type. Type identifiers are typically created by datatype bindings but can also be created by opaque signature matches and functor applications. What addPrettyPrint does is to set the ref for a particular type to some user-defined function. It has to be treated specially by the compiler because it needs to get the run-time ref from type information that is only present at compile-time. If you need to post-process the output from the compiler there are options such as CPOutStream that is used for all output or CPNameSpace that puts values, types etc into the name-space and deals with printing.
Setting this up to do what you want may be a bit of an effort initially but the idea is that the interface to PolyML.compiler should remain stable with any changes being limited to adding new options to allow for additional features.
I can see the advantage of changing PrettyString to include an explicit length; it's just that doing that would involve changes to nearly all the existing pretty-print functions. That's not just in Poly/ML code but any user code as well. It might be simpler to add a new constructor to the pretty datatype. I'll give it some thought but just at the moment I want to get the current version released before making any more changes.
I agree that you?d want to keep the existing constructor without the size information. With luck, adding a new constructor with explicit size information would be minimally intrusive.
OK. It's not trivial because there are two versions of the pretty datatype, one inside the compiler and one in user code, and these have to be kept in step. I'll look into this once the current version is released.
David