-- root URL for links define: docs-root as: "" -- current pretty-printer indentation level define: indent-level as: 0 -- allow multiline pretty-printing define: multiline-pretty? as: False -- context for a pretty-print define: context as: @top -- output as values? (i.e. instead of 1 + 1) define: value-output? as: False -- indent s up to the current indentation level indented: (s: String) := ($ repeat: (indent-level * 2)) .. s -- an empty line, followed by (indentation - 1) indentation unindent := "\n" .. ($ repeat: ((indent-level - 1) * 2)) -- default pretty-printer; just do @show A pretty: o := { s = o show if: (s starts-with?: "<") then: { s escape type: "internal" } else: { s escape } } call -- primitive pretty-printers A pretty: (c: Char) := c show escape type: "char" A pretty: (b: Boolean) := b show escape type: "keyword boolean" A pretty: (d: Double) := d show escape type: "number double" A pretty: (i: Integer) := i show escape type: "number integer" A pretty: (r: Rational) := r show escape type: "number rational" A pretty: (c: String) := c show escape type: "string" -- pattern pretty-printing; lop off "" normally A pretty: (p: Pattern) := if: value-output? then: { p show escape type: "internal" } else: { p show (drop: " " escape .. (a pretty: as to) (a: A) pretty: (p: Particle) := p type match: { @single -> ("@" .. p name escape (type: "name")) type: "particle" @keyword -> a pretty-particle: p } (a: A) pretty: (l: List) := ("[" .. l (map: { l | a pretty: l }) (join: ", ") .. "]") type: "list" (a: A) pretty: (b: Block) := with: [ context -> @top value-output? -> False indent-level -> indent-level + 1 ] do: { arguments = b arguments (map: { p | a pretty: p }) (join: " ") pretty-contents = b contents (map: { e | a pretty: e }) contents = if: multiline-pretty? then: { exprs = pretty-contents (map: { c | "\n" .. (indented: c) }) join strip-end exprs .. unindent } else: { " " .. pretty-contents (join: "; ") .. " " } condition: { contents strip empty? && b arguments empty? -> "{ }" type: "block" contents strip empty? -> ("{ " .. arguments .. " | }") type: "block" b arguments empty? -> ("{" .. contents .. "}") type: "block" otherwise -> ("{ " .. arguments .. " |" .. contents .. "}") type: "block" } } (a: A) pretty: (e: Expression) := { pretty = e type match: { @(dispatch: type) -> with: value-output? as: False do: { a pretty-dispatch: e type: type } @define -> with: [ value-output? -> False context -> @define ] do: { pat = a pretty: e pattern if: multiline-pretty? then: { with: [ indent-level -> indent-level + 1 context -> @define ] do: { expr = a pretty: e expression pat .. " :=\n" .. (indented: expr) .. "\n" } } else: { with: context as: @define do: { expr = a pretty: e expression pat .. " := " .. expr } } } @set -> with: [ value-output? -> False context -> @set ] do: { (a pretty: e pattern) .. " = " .. (a pretty: e expression) } @list -> with: [ value-output? -> False context -> @list ] do: { "[" .. e contents (map: { l | a pretty: l }) (join: ", ") .. "]" } @(particle: @single) -> a pretty: (a evaluate: e) @(particle: @keyword) -> with: context as: @particle do: { a pretty-particle: e } @primitive -> with: value-output? as: True do: { a pretty: (a evaluate: e) } @block -> with: context as: @block do: { a pretty: (a evaluate: e) } @top -> "this" type: "keyword top" @macro -> [ "macro" type: "keyword macro" "(" .. (a pretty: e pattern) .. ")" a pretty: e expression ] unwords @operator -> [ "operator" type: "keyword operator" e associativity name a pretty: e precedence e operators (map: { o | a linked: `~o as: o names head }) unwords ] unwords @for-macro -> [ "for-macro" type: "keyword for-macro" a pretty: e expression ] unwords @quote -> with: [ value-output? -> False context -> @quote ] do: { "`" .. (a pretty-segment: e expression) } @unquote -> with: [ value-output? -> False context -> @unquote ] do: { "~" .. (a pretty-segment: e expression) } x -> e show (drop: 2) init escape -- remove '( and ) } condition: { value-output? not -> pretty e needs-parens? -> "'(" .. pretty .. ")" otherwise -> "'" .. pretty } } call (e: Expression) needs-parens? := e type match: { @(dispatch: @single) -> e target type /= @top x -> x in?: [@define, @set, @(dispatch: @keyword)] } (a: A) pretty-segment: (e: Expression) := if: e needs-parens? then: { "(" .. (a pretty: e) .. ")" } else: { a pretty: e } (a: A) pretty-dispatch: e type: @single := with: context as: @single do: { msg = (a linked: e as: e name) type: "dispatch single" if: (e target type == @top) then: { msg } else: { (a pretty: e target) .. " " .. msg } } (a: A) pretty-dispatch: e type: @keyword := condition: { e targets empty? -> (a linked: e as: e names (map: @keywordfy) join) type: "dispatch keyword" e names == [":="] -> with: context as: @define do: { pat = a pretty: e targets head exp = e targets (at: 1) key = a linked: e as: ":=" if: multiline-pretty? then: { modify: indent-level as: @(+ 1) do: { expr = a pretty: exp pat .. " " .. key .. "\n" .. (indented: expr) .. "\n" } } else: { pat .. " " .. key .. " " .. (a pretty: exp) } } e names == ["="] -> with: context as: @set do: { pat = a pretty: e targets head exp = e targets (at: 1) key = a linked: e as: "=" pat .. " " .. key .. " " .. (a pretty: exp) } otherwise -> { from = context with: context as: @keyword do: { initial = if: (e targets head type == @top) then: { "" } else: { (a pretty: e targets head) .. " " } rest = (0 ... e names length) (map: { n | name = e names (at: n) value = a pretty: e targets (at: (n + 1)) (a linked: e as: name keywordfy) .. " " .. value }) join: " " (if: (from == @single || from == @keyword) then: { "(" .. initial .. rest .. ")" } else: { initial .. rest }) type: "dispatch keyword" } } call } (a: A) pretty-particle: p := if: p values (all?: @(== @none)) then: { ("@" .. p names (map: { n | n keywordfy escape (type: "names") }) join ) type: "particle" } else: { initial = p values head match: { @none -> "" @(ok: v) -> a pretty: v } ks = (0 ... p names length) map: { n | name = p names (at: n) value = p values (at: (n + 1)) match: { @none -> "_" @(ok: v) -> a pretty: v } name keywordfy escape (type: "names") .. " " .. value } ("@(" .. initial .. ks (join: " ") .. ")") type: "particle" } -- find a url for an expression, and link it with t as the text (a: A) linked: (e: Expression) as: (t: String) := a (url-for: e) match: { @none -> t escape @(ok: u) -> t escape in-tag: ("a href=\"" .. docs-root .. u .. "\"") }