Module: environment-protocols Synopsis: Environment protocols Author: Andy Armstrong, Chris Page Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// Naming protocols define open generic print-environment-object (stream :: , server :: , object :: , #key, #all-keys) => (); define open generic print-environment-object-name (stream :: , server :: , object :: , #key, #all-keys) => (); define generic print-anonymous-object (stream :: , server :: , name :: , #key namespace) => (); define generic print-module-name-object (stream :: , server :: , name :: , namespace :: false-or(), #key qualify-names? :: ) => (); define open generic environment-object-basic-name (server :: , object :: , #key, #all-keys) => (name :: false-or()); define open generic environment-object-display-name (server :: , object :: , namespace :: false-or(), #key, #all-keys) => (name :: false-or()); define open generic environment-object-unique-name (server :: , object :: , namespace :: false-or(), #key, #all-keys) => (name :: ); /// Print constants define constant $default-qualify-names = #t; define constant $unknown = "unknown"; define constant $unknown-project = "unknown-project"; define constant $unknown-library = "unknown-library"; define constant $unknown-module = "unknown-module"; define constant $unknown-object-name = "{unknown object}"; define constant $list-separator = ", "; define constant $interactive-record = "[interactive]"; define constant $warning-max-lines = 6; /// Basic names define method environment-object-basic-name (server :: , object :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => (name :: false-or()) let name = namespace & environment-object-name(server, object, namespace); if (name) environment-object-primitive-name(server, name) else let home-name = environment-object-home-name(server, object); if (home-name) environment-object-basic-name (server, home-name, namespace: namespace, qualify-names?: qualify-names?) else let id = environment-object-id(server, object); instance?(id, ) & id.id-name end end end method environment-object-basic-name; define method environment-object-basic-name (server :: , name :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => (name :: false-or()) if (~qualify-names? | begin let library = select (namespace by instance?) => namespace; => environment-object-library(server, namespace); otherwise => #f; end; library & library == name-namespace(server, name) end) environment-object-primitive-name(server, name) | $unknown-module end end method environment-object-basic-name; define method environment-object-basic-name (server :: , name :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => (name :: false-or()) if (~qualify-names? | (namespace & namespace == name-namespace(server, name))) environment-object-primitive-name(server, name) end end method environment-object-basic-name; define method environment-object-basic-name (server :: , object :: , #key, #all-keys) => (name == #f) #f end method environment-object-basic-name; define method environment-object-basic-name (server :: , collection :: , #key, #all-keys) => (name == #f) #f end method environment-object-basic-name; define method environment-object-basic-name (server :: , character :: , #key, #all-keys) => (name :: ) concatenate("'", environment-object-primitive-name(server, character), "'") end method environment-object-basic-name; define method environment-object-basic-name (server :: , string :: , #key, #all-keys) => (name :: ) format-to-string("%=", environment-object-primitive-name(server, string)) end method environment-object-basic-name; define method environment-object-basic-name (server :: , symbol :: , #key, #all-keys) => (name :: ) format-to-string("#%=", environment-object-primitive-name(server, symbol)) end method environment-object-basic-name; define method environment-object-basic-name (server :: , string :: , #key, #all-keys) => (name :: ) environment-object-primitive-name(server, string) end method environment-object-basic-name; define method environment-object-basic-name (server :: , number :: , #key, #all-keys) => (name :: ) environment-object-primitive-name(server, number) end method environment-object-basic-name; define method environment-object-basic-name (server :: , library :: , #key, #all-keys) => (name :: ) environment-object-primitive-name(server, library) | $unknown-library end method environment-object-basic-name; define method environment-object-basic-name (server :: , project :: , #key, #all-keys) => (name :: ) environment-object-primitive-name(server, project) | $unknown-project end method environment-object-basic-name; define method environment-object-basic-name (server :: , warning :: , #key namespace, qualify-names? :: = $default-qualify-names, full-message? :: , #all-keys) => (name :: false-or()) ignore(namespace); if (~full-message?) case full-message? => compiler-warning-full-message(server, warning); otherwise => compiler-warning-short-message(server, warning); end end end method environment-object-basic-name; /// Print methods define method print-specializers (stream :: , server :: , specializers :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () write(stream, "("); let separator = #f; for (specializer in specializers, separator = "" then $list-separator) write(stream, separator); print-environment-object-name (stream, server, specializer, namespace: namespace, qualify-names?: qualify-names?); end; write(stream, ")") end method print-specializers; define method print-module-name-object (stream :: , server :: , name :: , library :: , #key qualify-names? :: = $default-qualify-names) => () let name-library = name-namespace(server, name); let primitive-name = environment-object-primitive-name(server, name); if (qualify-names? & (~library | name-library ~= library)) format(stream, "%s:%s", primitive-name | $unknown-module, environment-object-primitive-name(server, name-library) | $unknown-library) else write(stream, primitive-name | $unknown-module) end end method print-module-name-object; define method print-module-name-object (stream :: , server :: , name :: , module :: , #key qualify-names? :: = $default-qualify-names) => () let library = environment-object-library(server, module); print-module-name-object (stream, server, name, library, qualify-names?: qualify-names?) end method print-module-name-object; define method print-module-name-object (stream :: , server :: , name :: , context-namespace == #f, #key qualify-names? :: = $default-qualify-names) => () let primitive-name = environment-object-primitive-name(server, name); if (qualify-names?) let name-library = name-namespace(server, name); format(stream, "%s:%s", primitive-name | $unknown-module, environment-object-primitive-name(server, name-library) | $unknown-library) else write(stream, primitive-name | $unknown) end end method print-module-name-object; define method print-source-location (stream :: , location :: , #key line-number :: false-or() = #f) => () let record = location.source-location-source-record; let first-line = record.source-record-start-line; let name = select (record by instance?) => record.source-record-name | $interactive-record; => locator-name(record.source-record-location); end; let (start-line, end-line) = if (line-number) values(line-number, line-number) else let start-offset = location.source-location-start-offset; let end-offset = location.source-location-end-offset; values(start-offset.source-offset-line, end-offset.source-offset-line) end; if (start-line == end-line) format(stream, "%s:%s", name, start-line + first-line) else format(stream, "%s:%s-%s", name, start-line + first-line, end-line + first-line) end end method print-source-location; /// print-environment-object define method print-environment-object (stream :: , server :: , object :: , #key namespace) => () let name = environment-object-basic-name(server, object); if (name) write(stream, name) else print-anonymous-object(stream, server, object, namespace: namespace) end end method print-environment-object; /// print-anonymous-object define method print-anonymous-object (stream :: , server :: , object :: , #key namespace) => () write(stream, environment-object-primitive-name(server, object) | $unknown-object-name) end method print-anonymous-object; define method print-anonymous-object (stream :: , server :: , object :: , #key namespace) => () let application = ensure-application-server(server.server-project, object); let class = application-object-class(server, object); let user-object? = instance?(object, ) & ~instance?(object, ); //---*** This is a hack, we should move all of the printing out //---*** of the runtime manager if (user-object? & class) write(stream, "{"); print-environment-object-name(stream, server, class, qualify-names?: #f); print-anonymous-object-contents (stream, server, object, namespace: namespace); print-environment-object-id(stream, server, object); write(stream, "}") else if (application) write(stream, environment-object-primitive-name(application, object)) else next-method() end end end method print-anonymous-object; define method print-anonymous-object-contents (stream :: , server :: , object :: , #key namespace) => () #f end method print-anonymous-object-contents; define method print-anonymous-object-contents (stream :: , server :: , condition :: , #key namespace) => () let (string, arguments) = condition-format-options(server, condition); if (string & arguments) //---*** Ideally we'd use format, but it doesn't understand //---*** collection-objects, plus we need to use print-environment-object-name //---*** on every object with the correct context. We should //---*** create environment-format or something. // apply(format, stream, string, arguments); write(stream, ": "); print-environment-object-name(stream, server, string, namespace: namespace); write(stream, ", arguments: "); print-environment-object-name(stream, server, arguments, namespace: namespace) end end method print-anonymous-object-contents; define method print-anonymous-object-contents (stream :: , server :: , object :: , #key namespace) => () let name = environment-object-primitive-name(server.server-project, object); name & format(stream, ": %=", name) end method print-anonymous-object-contents; define method print-environment-object-id (stream :: , server :: , object :: ) => () let project = server.server-project; let id = environment-object-id(project, object); //---*** Enable this in all editions when the runtime manager correctly //---*** maintains object identity. if (release-internal?() & instance?(id, )) write(stream, ": "); write(stream, integer-to-string(id)) end end method print-environment-object-id; define generic print-environment-object-id-name (stream :: , server :: , object :: , #key qualify-names? :: = $default-qualify-names) => (printed? :: ); define method print-environment-object-id-name (stream :: , server :: , object :: , #key qualify-names? :: = $default-qualify-names) => (printed? :: ) let id = environment-object-id(server, object); if (instance?(id, )) write(stream, id.id-name); if (qualify-names?) select (id by instance?) => #f; => write(stream, id.id-library.id-name); => let module = id.id-module; write(stream, module.id-name); write(stream, ":"); write(stream, module.id-library.id-name); end end; #t end end method print-environment-object-id-name; define method print-environment-object-id-name (stream :: , server :: , object :: , #key qualify-names? :: = $default-qualify-names) => (printed? :: ) let gf = method-generic-function(server, object); if (gf) print-environment-object-id-name (stream, server, gf, qualify-names?: qualify-names?) end end method print-environment-object-id-name; /// print-environment-object-name methods define method print-environment-object-name (stream :: , server :: , object :: , #rest args, #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () let basic-name = apply(environment-object-basic-name, server, object, args); if (basic-name) write(stream, basic-name) else let name = namespace & environment-object-name(server, object, namespace); if (name) write(stream, environment-object-primitive-name(server, name)) else let home-name = environment-object-home-name(server, object); if (home-name) print-environment-object-name (stream, server, home-name, namespace: namespace, qualify-names?: qualify-names?) else print-environment-object-id-name (stream, server, object, qualify-names?: qualify-names?) | print-environment-object (stream, server, object, namespace: namespace) end end end end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , name :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () print-module-name-object (stream, server, name, namespace, qualify-names?: qualify-names?) end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , name :: , #key namespace: context-namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () let primitive-name = environment-object-primitive-name(server, name); let namespace = name-namespace(server, name); if (qualify-names? & (~context-namespace | namespace ~= context-namespace)) format(stream, "%s:", primitive-name); print-environment-object-name(stream, server, namespace, namespace: context-namespace, qualify-names?: qualify-names?) else write(stream, primitive-name) end end method print-environment-object-name; /*---*** Something smart like this might be nice, but it is better ---*** if the backend does it, because it can be faster. define variable *collection-print-length* = 10; define method print-environment-object-name (stream :: , server :: , collection :: , #key namespace, #all-keys) => () let elements = collection-elements(server, collection, range: range(from: 0, to: *collection-print-length*)); let separator = #f; for (element in elements) separator & write(stream, separator); print-environment-object-name(stream, server, element, namespace: namespace); unless (separator) separator := $list-separator end; end end method print-environment-object-name; */ define method print-environment-object-name (stream :: , server :: , method-object :: , #key qualify-names? :: = $default-qualify-names, namespace, show-function-name? :: = #t, #all-keys) => () local method object-name (object :: ) => (name :: false-or()) if (namespace) environment-object-name(server, object, namespace) end | environment-object-home-name(server, object) end method object-name, method method-name (object :: ) => (name :: false-or()) let gf = method-generic-function(server, object); (gf & object-name(gf)) | object-name(object) end method method-name; let project = server.server-project; if (project.project-compiler-database) if (show-function-name?) let name = method-name(method-object); if (name) print-environment-object-name (stream, server, name, namespace: namespace, qualify-names?: qualify-names?); write(stream, " ") else print-environment-object-id-name (stream, server, method-object, qualify-names?: qualify-names?) end end; print-specializers (stream, server, method-specializers(server, method-object), namespace: namespace, qualify-names?: qualify-names?) else let name = environment-object-primitive-name(project, method-object); if (name) write(stream, name) else print-anonymous-object(stream, server, method-object, namespace: namespace) end end end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , domain-object :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () next-method(); write(stream, " "); print-specializers (stream, server, domain-specializers(server, domain-object), namespace: namespace, qualify-names?: qualify-names?) end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , object :: , #key namespace, qualify-names? :: = $default-qualify-names, #all-keys) => () let value = singleton-value(server, object); write(stream, "== "); if (value) print-environment-object (stream, server, value, namespace: namespace, qualify-names?: qualify-names?) else write(stream, $unknown-object-name) end end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , warning :: , #key namespace, full-message? :: , #all-keys) => () ignore(namespace); if (full-message?) write(stream, environment-object-type-name(warning)); let owner = warning-owner(server, warning); let location = environment-object-source-location(server, warning); if (location) write(stream, " at "); print-source-location(stream, location) end; write(stream, ":\n\n") end; let message = case full-message? => compiler-warning-full-message(server, warning); otherwise => compiler-warning-short-message(server, warning); end; write(stream, message); let location = environment-object-source-location(server, warning); if (full-message? & location) new-line(stream); let record = location.source-location-source-record; let start-offset = location.source-location-start-offset; let start-line = start-offset.source-offset-line; let (lines, upper-dec, lower-dec) = extract-lines(location); if (~lines) print-source-location(stream, location) else local method output-line (number :: , line :: false-or()) => () print-source-location(stream, location, line-number: number); line & format(stream, ": %s\n", line) end method output-line; format(stream, "\n"); output-line(start-line - 1, upper-dec); let no-of-lines = lines.size; if (no-of-lines <= $warning-max-lines) for (line in lines, number from start-line) output-line(number, line) end else let half-count = floor/($warning-max-lines, 2); for (index from 0 below half-count) output-line(start-line + index, lines[index]) end; output-line(start-line + no-of-lines - half-count, "[...]"); for (index from (no-of-lines - half-count + 1) below no-of-lines) output-line(start-line + index, lines[index]) end end; output-line(start-line + no-of-lines, lower-dec) end end end method print-environment-object-name; define method print-environment-object-name (stream :: , server :: , thread :: , #key namespace) => () let application = ensure-application-server(server.server-project, thread); if (application) let index = thread-index(application, thread); let name = environment-object-primitive-name(application, thread); format(stream, "Thread %d: %s", index, name) else next-method() end end method print-environment-object-name; /// Displaying names define method print-environment-object-to-string (server :: , object :: , #rest args, #key namespace, #all-keys) => (name :: ) let stream = make(, direction: #"output"); apply(print-environment-object, stream, server, object, args); as(, stream-contents(stream)) end method print-environment-object-to-string; define method print-environment-object-name-to-string (server :: , object :: , #rest args, #key namespace, #all-keys) => (name :: ) apply(environment-object-basic-name, server, object, args) | begin let stream = make(, direction: #"output"); apply(print-environment-object-name, stream, server, object, args); as(, stream-contents(stream)) end end method print-environment-object-name-to-string; define method environment-object-display-name (server :: , object :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) apply(print-environment-object-name-to-string, server, object, namespace: namespace, args) end method environment-object-display-name; /// Unique names define method environment-object-unique-name (server :: , object :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) apply(environment-object-display-name, server, object, namespace, args) end method environment-object-unique-name; define method environment-object-unique-name (server :: , object :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) let name = next-method(); format-to-string("%s %s", as-lowercase(environment-object-type-name(object)), name) end method environment-object-unique-name; define method environment-object-unique-name (server :: , object :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) let name = namespace & environment-object-name(server, object, namespace); if (instance?(name, )) apply(environment-object-display-name, server, name, namespace, args) else next-method() end end method environment-object-unique-name; define method environment-object-unique-name (server :: , module :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) environment-object-typed-name(server, module, namespace: namespace) end method environment-object-unique-name; define method environment-object-unique-name (server :: , library :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) environment-object-typed-name(server, library, namespace: namespace) end method environment-object-unique-name; define method environment-object-unique-name (server :: , object :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) environment-object-typed-name(server, object, namespace: namespace) end method environment-object-unique-name; define method environment-object-unique-name (server :: , function :: , namespace :: false-or(), #rest args, #key, #all-keys) => (name :: ) //--- We really shouldn't have concrete superclasses... if (instance?(function, )) next-method() else environment-object-typed-name(server, function, namespace: namespace) end end method environment-object-unique-name; define function environment-object-typed-name (server :: , object :: , #key namespace :: false-or()) => (name :: ) let name = environment-object-display-name(server, object, namespace); format-to-string("%s %s", as-lowercase(environment-object-type-name(object)), name) end function environment-object-typed-name; /// Application state define function thread-state-label (project :: , state :: ) => (label :: ) select (state) #"frozen" => "frozen"; #"running" => "running"; otherwise => as(, state); end end function thread-state-label; define function application-state-label (project :: ) => (label :: ) let application = project.project-application; let state = application & application.application-state; select (state) #f, #"uninitialized" => "uninitialized"; #"running" => "running"; #"stopped" => "paused"; #"closed" => "stopped"; otherwise => as(, state); end end function application-state-label; /// Compiler warnings define constant $newline-code = as(, '\n'); //--- stolen from dfmc-reader //--- Should be moved to source records and shared from there. define function extract-lines (loc :: ) => (line-strings, upper-decorator, lower-decorator) let text = block () source-record-contents(loc.source-location-source-record) exception () #f end; if (~text) values(#f, #f, #f); else let line-count = loc.source-location-end-offset.source-offset-line - loc.source-location-start-offset.source-offset-line; let line-one-start-index = compute-line-start-character (text, loc.source-location-start-offset.source-offset-line); collecting (line-strings) local method walk-lines (cursor, lines) if (lines <= line-count) let (line-string, next-cursor) = copy-next-line(text, cursor); collect-into(line-strings, line-string); walk-lines(next-cursor, lines + 1); end; end; walk-lines(line-one-start-index, 0); let line-strings = collected(line-strings); let start-col = loc.source-location-start-offset.source-offset-column; let end-col = loc.source-location-end-offset.source-offset-column; if (line-count = 0) values(line-strings, make-closed-upper-decorator(start-col, end-col), make-closed-lower-decorator(start-col, end-col)) else values(line-strings, make-open-upper-decorator(start-col, size(line-strings.first)), make-open-lower-decorator(end-col)) end; end; end; end function extract-lines; // These functions construct the "decorator" lines used to indicate // the extent of a source location on a given line. define function make-closed-upper-decorator (start-col :: , end-col :: ) => (decorator :: ) collecting (as ) for (i from 0 below start-col) collect(' ') end; for (i from start-col below end-col) collect('-') end; end; end function make-closed-upper-decorator; define function make-closed-lower-decorator (start-col :: , end-col :: ) => (decorator :: ) make-closed-upper-decorator(start-col, end-col); end function make-closed-lower-decorator; define function make-open-upper-decorator (start-col :: , end-col :: ) => (decorator :: ) make-closed-upper-decorator(start-col, end-col); end function make-open-upper-decorator; define function make-open-lower-decorator (end-col :: ) => (decorator :: ) make-closed-upper-decorator(0, end-col); end function make-open-lower-decorator; define function compute-line-start-character (string, lines :: ) => (character :: ) let line-count = 1; for (i from 0, char in string, until: line-count == lines) if (char == $newline-code) line-count := line-count + 1; end; finally i end; end function; define function copy-next-line (string, start) => (line-string, next-start) let stop = size(string); collecting (line-string :: ) for (i from start below stop, until: string[i] == 10) collect-into(line-string, as(, string[i])); finally values(collected(line-string), i + 1); end; end; end function copy-next-line;