|
declare namespace b="de/bottlecaps/railroad/xq/ast-to-ebnf.xq";
|
|
|
|
(:~
|
|
: The indentation in front of continuation line content.
|
|
:)
|
|
declare variable $b:t1 := 12;
|
|
declare variable $b:t2 := 40;
|
|
|
|
(:~
|
|
: Replace indentation of a block of text by a fixed number of spaces.
|
|
:
|
|
: @param $text
|
|
: @param $indent
|
|
:)
|
|
declare function b:re-indent($text as xs:string, $indent as xs:integer) as xs:string?
|
|
{
|
|
let $trimmed := replace($text, "(^\s+)|(\s+$)", "")
|
|
let $lines := tokenize($trimmed, " ")
|
|
where exists($lines)
|
|
return
|
|
let $min-indent :=
|
|
min
|
|
(
|
|
for $line in $lines[position() > 1]
|
|
where normalize-space($line) != ""
|
|
return string-length($line) - string-length(replace($line, "^ +", ""))
|
|
)
|
|
let $old-indent := string-join(("^", for $x in 1 to $min-indent return " "), "")
|
|
let $new-indent := string-join(for $x in 1 to $indent return " ", "")
|
|
return
|
|
string-join
|
|
(
|
|
(
|
|
$lines[1],
|
|
for $line in $lines[position() > 1]
|
|
return
|
|
(
|
|
if (normalize-space($line) = "") then
|
|
""
|
|
else if ($old-indent = "^") then
|
|
concat($new-indent, $line)
|
|
else
|
|
replace($line, $old-indent, $new-indent)
|
|
)
|
|
),
|
|
" "
|
|
)
|
|
};
|
|
|
|
(:~
|
|
: Render a sequence of nodes, returning a single string. Recursively pass
|
|
: grammar fragments from the todo list to the done list.
|
|
:
|
|
: @param $done the sequence of lines (strings) already rendered.
|
|
: @param $todo the sequence of nodes to be rendered.
|
|
: @return the rendered result.
|
|
:)
|
|
declare function b:break-lines($done, $todo) as element(ebnf)
|
|
{
|
|
if (empty($todo)) then
|
|
element ebnf
|
|
{
|
|
for $d at $i in $done
|
|
return (<lf>
</lf>[$i > 1], $d)
|
|
}
|
|
else
|
|
let $item := $todo[1]
|
|
let $todo := $todo[position() > 1]
|
|
return
|
|
typeswitch ($item)
|
|
case element(tab) return
|
|
let $n := xs:integer($item/@col - string-length($done[last()]) - 1)
|
|
return
|
|
if ($n = 0) then
|
|
b:break-lines($done, $todo)
|
|
else if ($n > 0) then
|
|
let $spaces := string-join(for $i in (1 to $n) return " ", "")
|
|
return b:break-lines(($done[position() < last()], element line {$done[last()]/node(), text{$spaces}}), $todo)
|
|
else if (normalize-space($done[last()]) != "") then
|
|
let $spaces := string-join(for $i in (1 to xs:integer($item/@col) - 1) return " ", "")
|
|
return b:break-lines(($done, element line {$spaces}), $todo)
|
|
else
|
|
let $spaces := string-join(for $i in (1 to xs:integer($item/@col) - 1) return " ", "")
|
|
return b:break-lines(($done[position() < last()], element line {$spaces}), $todo)
|
|
case element(fragment) return
|
|
b:break-lines
|
|
(
|
|
(
|
|
$done[position() < last()],
|
|
element line{$done[last()]/node(), text{" "}[$done[last()] != ""], $item/node()}
|
|
),
|
|
$todo
|
|
)
|
|
case element(name) return
|
|
b:break-lines
|
|
(
|
|
(
|
|
$done[position() < last()],
|
|
element line{$done[last()]/node(), text{" "}[$done[last()] != ""], $item}
|
|
),
|
|
$todo
|
|
)
|
|
case text() return
|
|
b:break-lines
|
|
(
|
|
(
|
|
$done[position() < last()],
|
|
element line{$done[last()]/node(), text{" "}[$done[last()] != ""], $item}
|
|
),
|
|
$todo
|
|
)
|
|
case xs:string return
|
|
b:break-lines
|
|
(
|
|
(
|
|
$done[position() < last()],
|
|
element line{$done[last()]/node(), text{" "}[$done[last()] != ""], text{$item}}
|
|
),
|
|
$todo
|
|
)
|
|
case processing-instruction() return
|
|
if (local-name($item) = "TOKENS" and $item = "") then
|
|
b:break-lines($done, (<tab col="1"/>, "
<?TOKENS?>
", <tab col="1"/>, $todo))
|
|
else if (local-name($item) = "ENCORE" and $item = "") then
|
|
b:break-lines($done, (<tab col="1"/>, "
<?ENCORE?>", <tab col="1"/>, $todo))
|
|
else
|
|
b:break-lines
|
|
(
|
|
$done,
|
|
(
|
|
<tab col="{$b:t2}"/>,
|
|
let $data := replace(data($item), "^#line [0-9]+ ""[^""]*""\s+", "")
|
|
return
|
|
if (not(contains($data, "
"))) then
|
|
concat("<?", local-name($item), " "[$data], $data, "?>")
|
|
else
|
|
(
|
|
concat("<?", local-name($item)),
|
|
<tab col="{$b:t2 + 2}"/>,
|
|
b:re-indent($data, $b:t2 + 2),
|
|
<tab col="{$b:t2}"/>,
|
|
"?>"
|
|
),
|
|
<tab col="{$b:t1 + 1}"/>,
|
|
$todo
|
|
)
|
|
)
|
|
default return
|
|
error(xs:QName("b:break-lines"), concat("invalid input type: ", string($item)))
|
|
};
|
|
|
|
b:break-lines((), /root/node())
|