neuron-explorer
Qwen3.5‑4B 전용 조향형 뉴런 탐색기, 모든 MLP 뉴런을 밀어 결과를 보관·탐색
A modern XSLT 3.0-4.0 transformation engine with streaming and package support
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A modern XSLT 4.0 transformation engine for .NET with streaming and package support.
XSLT 4.0
• xsl:switch — conditional processing with select context
• xsl:for-each-member — array member iteration
• xsl:item-type — named type declarations
• xsl:record — record construction
• method="csv" — CSV serialization outputREADME 전문 보기 ↗A modern XSLT 4.0 transformation engine for .NET with streaming and package support.
Every figure below is measured, dated, and reproducible. Nothing here is an estimate.
Measured 2026-10-03 against w3c/xslt30-test @ fddf1cf and phoenixmldb-xquery main (source
mode), as the measured pass count from a confirming full run: the per-set passes in
conformance-results/summary.txt (#274). Two full runs agreed on every set before the baseline was
raised; the confirming run is the one committed. Eleven chunks, zero timeouts.
Since 2026-09-17: 492 failing → 155, on the same 10,839-case denominator, so the two figures are directly comparable. The rest of this section is the history behind that denominator.
The same harness runs QT3 through the XSLT engine's XQuery: 30,009/31,024 (96.7%), 1,015 failing. That's a different QT3 count from the XQuery repo's own figure, because each harness measures a different case set.
History (2026-09-17): the percentage fell and the engine improved. It read 97.1% against a denominator of 10,672 on 2026-09-15. The harness ran two sets the W3C catalog does not declare, skipped cases in one it does, and never wired
fn/system-property-genat all. With the set list matched to the catalog at 260 sets the denominator is 10,839, and passes went up by 19. The drop is 166 cases that were always failing and were never being counted — see 2.1.0 inRELEASES.md.Figures across that change are not comparable in either direction. The same engine reports 344 failures on the old set list and 492 on the new one.
Not the sum of
scripts/conformance-baseline.tsv. That file is a regression gate, not a pass count, and is deliberately allowed to sit below the measurement wherever a set is pinned to a floor. Today it sums to 10,326:insn/call-templatemeasures 39 and is recorded at its standing floor of 37. A gate below the truth costs at most a missed alarm on one set; a gate above it turnsmainred whenever that set drifts. The two figures agree only when nothing is pinned.
The denominator grew, and that is the point. It was 10,630 because the harness never opened
decl/expose — 42 cases of an entire feature area, scoring no information at all rather than
scoring zero. Wiring it in adds 42 cases of which 9 pass, so it lowers the percentage while
the engine has only improved. A number that went up because a failing area stayed invisible is
worse than a smaller number that counts it, so the smaller honest one is published here. See
BUGS.md #72.
Which XQuery this is measured against, because it changes the number. The conformance
project reaches XQuery through the src/PhoenixmlDb.XQuery symlink, so a local run measures
whatever that working tree is checked out at. The figure above is against phoenixmldb-xquery
main at 1.8.0, which is the intended basis.
A worked example of why the basis is stated and not assumed. That checkout was found sitting detached at the v1.7.0 release commit, 27 behind main, left over from an A/B three days earlier. Measured there, the suite reported 21 test-sets below baseline, 34 cases — which reads exactly like a regression, and QT3 alone accounted for 28 of it. Restoring the checkout to main cleared 18 of the 21 sets and returned QT3 to 29,534 on the nose. Same engine throughout; only the dependency source had moved. A per-set gate cannot distinguish "the code got worse" from "a sibling checkout moved", so the environment has to be part of the reading.
The baseline records the minimum across full runs, not the best one seen. Repeated sweeps
agree on every set but one: insn/call-template alternates between 38 and 37, and the baseline
holds 37. A baseline set to the maximum observed enshrines a lucky run and then reports a
regression every time the suite behaves normally.
| Group | Passing | Failing | |
|---|---|---|---|
attr — attributes | 1084/1117 | 97.0% | 33 |
decl — declarations | 1017/1122 | 90.6% | 105 |
type — types | 753/766 | 98.3% | 13 |
fn — functions | 1082/1131 | 95.7% | 49 |
strm — streaming | 2309/2373 | 97.3% | 64 |
expr — expressions | 637/648 | 98.3% | 11 |
misc | 1884/1921 | 98.1% | 37 |
insn — instructions | 1544/1594 | 96.9% | 50 |
| Total | 10,310/10,672 | 96.6% | 362 |
decl is the only group whose denominator changed: 1080 to 1122, the 42 newly-opened
decl/expose cases.
The sandp group runs but reports no per-case counts, so it is excluded from the total rather
than counted as passing.
A note on how these figures are measured, because one of them was wrong for months.
Conformance is measured in a Release build. It used to be measured in Debug, which lost 38 cases across 21 sets purely to the harness's 10s per-case timeout — 43 timeouts against Release's 5. Release is what ships, so a figure taken from a Debug build was not a claim about the product. Measured both ways on the same commit, same machine:
| cases | rate | timeouts | |
|---|---|---|---|
| Debug | 10,044/10,630 | 94.49% | 43 |
| Release | 10,082/10,630 | 94.84% | 5 |
21 sets better under Release, zero worse. misc/bug-3701 is the clean illustration: ~10.5s in
Debug against a 10s cap, ~5.7s in Release. scripts/conformance.sh now defaults to Release.
That correction is why the published figure rose from 94.4% to 94.8% on 2026-09-10 without the
engine changing. The rise since then — to 95.6% — is engine work. See BUGS.md #43.
This number went DOWN from the 96.2% published on 2026-09-02, and the engine did not get
worse — the measurement got honest. Tests that expect a specific error code were scored as
passes whenever the transform threw anything at all: the corpus writes the expected code as an
attribute (<error code="XTSE0010"/>) and both conformance runners read it from the element's
text content, so the comparison was always against an empty string, which matches everything.
fn:load-xquery-module scored 4/4 on the strength of throwing four times. Full write-up in
BUGS.md entry 28.
Checking the code properly cost 4.6 points; reading it from the right place gave most of that
back. Many errors carry their code in a structured ErrorCode property rather than in the
message text, so a message-only comparison under-credited correct behaviour just as badly as it
over-credited wrong behaviour. Both runners now check message text and property, down the whole
inner-exception chain.
222 of the 610 remaining failures are "the engine raised an error, but not the expected code." Those are real failures — the codes are normative — but they are a different and generally shallower defect than a wrong result or a missed error, so runs report the split:
Results: 27/50 passed (54.0%) — 23 of 23 failures raised an error with the wrong code
Every XSLT conformance figure this project published before 2026-09-04 was overstated. The net correction is 1.9 points, and 204 failures that were previously invisible.
Measured 2026-09-02 against the XSpec test corpus. This is the weakest of our conformance numbers and is published for the same reason as the strongest one.
Stale, and understating. It predates a week of engine work — three xsl:try defects, the
analyze-string dot-all flag, a copied element losing its text, seven wrong error-code sites,
package-version validation, and the XQuery-side fn:sum, atomization and map:put fixes. The
real figure is very likely higher. It is left as measured rather than estimated upward, and
re-measuring it is open work.
| Suites running to completion | 139 of 284 |
| — of the 162 the runner can drive | 139 (122 are XQuery or Schematron suites it does not) |
| Assertions passing | 1152 of 1364 |
| Assertions failing | 209 |
Roughly half the corpus completes, and about one assertion in seven still fails. Open causes are tracked in BUGS.md.
./scripts/fetch-conformance-suites.sh # clones xslt30-test + qt3tests into TestData/
./scripts/conformance.sh # W3C XSLT groups; writes conformance-results/summary.txt
./scripts/conformance.sh --all # adds the XQuery (QT3) suite, as CI runs it
phxspec --census $(find test -maxdepth 1 -name '*.xspec' | sort) # from an xspec checkout
A conformance run with TestData/ absent reports success without executing anything, so confirm
the corpora are present before believing a green result.
dotnet add package PhoenixmlDb.Xslt
A standalone xslt CLI tool is also available as a .NET global tool:
dotnet tool install -g xslt
# Transform XML with a stylesheet
xslt -s:stylesheet.xsl input.xml
# Transform with parameters
xslt -s:stylesheet.xsl -p:name=value input.xml
# Call a named template (no source document)
xslt -s:stylesheet.xsl -it:main
# Show timing breakdown
xslt --timing -s:stylesheet.xsl input.xml
Run xslt --help for the full list of options.
var transformer = new XsltTransformer();
await transformer.LoadStylesheetAsync(xsltString);
var result = await transformer.TransformAsync(xmlInput);
TransformAsync(string? inputXml) — pass source XML as string, or null for call-template/call-functionTransformAsync(TextReader inputXml) — read source from a TextReader (for large documents)TransformAsync(Stream inputXml) — read source from a StreamTransformAsync(string? inputXml, TextWriter output) — write result directly to a TextWriterTransformAsync(TextReader inputXml, TextWriter output) — full stream-to-stream pipelineTransformAsync(Stream inputXml, Stream output) — full stream-to-stream pipelineResultDocumentHandler — callback to provide TextWriters for xsl:result-document outputsSetSourceDocumentUri(Uri) — set base-uri/document-uri metadata on the source documentSetSourceSelect(string xpath) — select initial context node (default: document root)SetInitialModeSelect(string xpath) — apply templates to a computed node selectionSetParameter(string name, string value) — string parameter (xs:untypedAtomic)SetParameter(string name, object? value) — typed parameter (int, long, double, bool, decimal)SetInitialTemplateParameter(QName, object?) — xsl:with-param for named templatesSetInitialTunnelParameter(QName, object?) — tunnel parameterSetInitialMode(string)SetInitialTemplate(string), pass null to TransformAsyncSetInitialFunction(string) + AddInitialFunctionArgument(object?)SetCollection(string uri, List<string> paths) — register documents for fn:collection()A stylesheet that asks for streaming (xsl:mode streamable="yes", xsl:source-document streamable="yes") always gives the result the unstreamed stylesheet gives. How much of the input
is held in memory depends on the shape of the body that reads it.
A body runs on the live reader, holding one child subtree at a time, when one of these does all of its reading and the rest of the body reads nothing below the context node:
xsl:apply-templates, xsl:for-each, xsl:iterate
or xsl:for-each-group (with group-adjacent, group-starting-with or group-ending-with)
over the children, selected by a single child step (*, item, node()) with no predicate.xsl:source-document: one xsl:apply-templates with
no select or a path of child steps (root/item); one xsl:for-each, xsl:iterate or
xsl:for-each-group (with group-adjacent, group-starting-with or group-ending-with)
over such a path; one xsl:copy-of select="."; or aggregates only (count(//x),
sum(root/item/@n), string-join(…), and the like).xsl:iterate select="root/item" is the way to read a large file record by record: each item
is in memory for its own iteration, and xsl:break stops the reading of the input. To read
the input as it goes, give it as a stream and take the output as a writer
(TransformAsync(Stream, TextWriter)); the overloads that take a string hold the whole input.
The output of a rule for the document node goes to the writer when the rule ends; the output
of the rules for elements goes as each element ends. A stylesheet whose rule for the document
node is not of a streamed shape gets the whole input read first, on every overload, and the
same result.
The instruction may stand inside literal result elements, xsl:copy, xsl:element,
xsl:result-document, xsl:if, xsl:choose, xsl:try, xsl:where-populated or the content of
an xsl:variable. Anything in the body other than that one instruction may read the context
node itself, its attributes and its ancestors.
Any other body is run on a buffered copy: of the matched element for a template rule, of the
whole input for a body at the document node. That covers a second read of the children, a
predicate on the walked children, a path of several steps in an element rule, group-by,
xsl:fork, and sorting. The result is the same; the memory is that of the copy.
StrictStreamability is a separate question: it reports XTSE3430 for what the specification
says is not guaranteed-streamable, whether or not this processor buffers it.
TraceListener — callback for template-match, function-call, built-in-rule eventsApache 2.0 — see LICENSE